一.Class類的使用
1.在面向?qū)ο蟮氖澜缰杏蚁牵晔氯f(wàn)物都是對(duì)象
2.java語(yǔ)言中靜態(tài)成員和普通數(shù)據(jù)類型不是對(duì)象
3.類也是對(duì)象,是java.lang.Class的實(shí)例對(duì)象
class Foo{
public void print()
{
System.out.println("it is Foo");
}
}
public class ClassDemo1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//Foo類的實(shí)例對(duì)象
Foo foo1 = new Foo(); //foo1表示Foo類的實(shí)例對(duì)象
/*
* Foo這個(gè)類本身也是一個(gè)實(shí)例對(duì)象禁谦,就是Class(java.lang.Class)類的實(shí)例對(duì)象
* /任何一個(gè)類都是Class類的實(shí)例對(duì)象, 該實(shí)例對(duì)象有三種表示方法
*/
//第一種 --> 任何一個(gè)類都有一個(gè)隱含的靜態(tài)成員變量class
Class c1 = Foo.class; //c1表示Class類的實(shí)例對(duì)象
//第二種孕豹,已知該類的對(duì)象, 通過(guò)該類對(duì)象的getClass方法表示
Class c2 = foo1.getClass(); //c2表示Class類的實(shí)例對(duì)象
/*
* 以上c1,c2都表示Class類的實(shí)例對(duì)象,但是這個(gè)實(shí)例對(duì)象又是說(shuō)Foo這個(gè)類
* c1,c2表示Foo類的類類型 ( class type ), 因?yàn)镕oo類可以理解為Class類的實(shí)例對(duì)象
* 也就是世界萬(wàn)物皆對(duì)象辛辨,類也是對(duì)象捕捂,是Class類的實(shí)例對(duì)象
* 這個(gè)對(duì)象我們稱為該類的類類型
*/
//不管c1 or c2都代表了Foo類的類類型瑟枫,一個(gè)類只可能是Class類的一個(gè)實(shí)例對(duì)象, 所以c1=c2
System.out.println(c1==c2);//輸出: true
//第三種
Class c3 = null;
try {
c3 = Class.forName("com.lxf.reflect.Foo");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(c3==c2); //輸出:true
//我們可以通過(guò)類的類類型創(chuàng)建該類的實(shí)例--->通過(guò)c1 or c2 or c3創(chuàng)建Foo的實(shí)例
try {
Foo foo2 = (Foo)c2.newInstance(); //需要有無(wú)參數(shù)的構(gòu)造方法
foo2.print();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
二.靜態(tài)加載
- 在編譯java源文件的時(shí)候的加載類叫做靜態(tài)加載,比如:Test.java文件如下
class Test{
public static void main(String[] args) {
People p = new People();
p.eat();
}
}
編譯Test.java
javac Test.java
這時(shí)候People.class字節(jié)碼文件并沒(méi)有,所以在編譯的時(shí)候會(huì)報(bào)錯(cuò)
創(chuàng)建People.java
class People
{
public void eat()
{
System.out.println("我喜歡美食");
}
}
此時(shí)在編譯
先編譯People.java
javac People.java //編譯后會(huì)產(chǎn)生People.class字節(jié)碼文件
javac Test.java //編譯后會(huì)產(chǎn)生Test.class字節(jié)碼文件
執(zhí)行Test
java Test //會(huì)輸出:我喜歡美食
三.動(dòng)態(tài)加載
Java的一個(gè)強(qiáng)大的特性是能夠動(dòng)態(tài)加載一個(gè)給定名稱的類指攒,而事先不需要指導(dǎo)這個(gè)類的名字慷妙。這個(gè)特性使得Java的開(kāi)發(fā)人員能夠構(gòu)造一個(gè)不需要重新編譯即可擴(kuò)展和修改的靈活動(dòng)態(tài)的系統(tǒng),在Java中允悦,動(dòng)態(tài)加載通常是調(diào)用類 java.lang.Class
的 forName
方法來(lái)實(shí)現(xiàn)膝擂;
問(wèn)題描述:
例如,下面的代碼在Main方法中調(diào)用ClassLoader來(lái)加載一個(gè)命令行傳入的class.
LoaderTest.java文件
package aa
public class LoaderTest
{
public static void main(String[] args)
{
LoadClass(args[0]);
}
public static void LoadClass(String clsName)
{
try
{
beLoaded bl =
(beLoaded)Class.forName(clsName).newInstance();
bl.PrintInfo();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
beLoaded.java文件
package aa;
public class beLoaded
{
public void PrintInfo()
{
System.out.println("I am be loaded!");
}
}
上面的代碼在正常情況下非常好使隙弛,并且使得整個(gè)系統(tǒng)可以同具體的java類分離開(kāi)來(lái)架馋,只需要傳入一個(gè)class的類名即可完成功能調(diào)用。而且從擴(kuò)展的角度來(lái)說(shuō)全闷,定義一些從beLoaded類上繼承下來(lái)的類叉寂,并將類名通過(guò)參數(shù)傳入系統(tǒng),即可實(shí)現(xiàn)各種不同的功能類的調(diào)用室埋,非常方便办绝。
在命令行上鍵入如下命令:
java aa.LoaderTest aa.beLoaded
屏幕會(huì)輸出下面的內(nèi)容:
I am be loaded!
下面我們創(chuàng)建一個(gè)beLoaded的子類,類名叫做beLoadedChild姚淆,代碼如下:
package aa;
public class beLoadedChild extends beLoaded
{
public void PrintInfo()
{
System.out.println("I am be loaded and I am Child");
}
}
在命令行上鍵入如下命令:
java aa.LoaderTest aa.beLoadedChild
屏幕會(huì)輸出下面的內(nèi)容:
I am be loaded and I am Child
通過(guò)上面的例子我們可以看出孕蝉,只要設(shè)計(jì)好LoaderTest這個(gè)類和beLoaded類,就可以實(shí)現(xiàn)系統(tǒng)的擴(kuò)展性腌逢,對(duì)不同的功能目標(biāo)調(diào)用不同的beLoaded的子類降淮,LoaderTest類beLoaded類是不需要重新編譯的,系統(tǒng)十分的靈活和方便搏讶。
四.基本數(shù)據(jù)類型佳鳖,void關(guān)鍵字都存在類類型
//int的類類型
Class c1 = int.class;
System.out.println(c1.getName());
//String的類類型, String的字節(jié)碼
Class c2 = String.class;
System.out.println(c2.getName());
Class c3 = double.class;
Class c4 = Double.class;
Class c5 = void.class;
Class c6 = Package.class;
五.Class類的基本API
- 獲取類的所有方法
/**
* 打印類的信息,成員方法
* @param obj
*/
public static void printMethodMessage(Object obj)
{
//獲取參數(shù)對(duì)象類的類類型, 參數(shù)傳遞的是哪個(gè)子類的對(duì)象媒惕,c就代表該子類的類類型
Class c = obj.getClass();
System.out.println("類名為:" + c.getName());
/*
* 獲取類的成員方法:
* 成員方法是java.lang.reflect.Method的對(duì)象
* 一個(gè)成員方法就是一個(gè)Method對(duì)象
* getMethods() 方法獲取的是所有Public類型的函數(shù)系吩,包括從父類繼承來(lái)的
* getDeclaredMethods() 獲取的是所有該類自己聲明的方法,不問(wèn)訪問(wèn)權(quán)限
*/
//獲取參數(shù)對(duì)象類的所有方法
Method[] methods = c.getMethods();
for (Method method : methods) {
//獲取方法的返回值類類型
Class returnType = method.getReturnType();
//打印方法返回值類型
System.out.print(returnType.getName());
System.out.print("方法名為:" + method.getName() + " ( ");
//獲取返回值類型---->得到的是參數(shù)列表的類類型( int.class, String.class等 )
Class[] paramTypes = method.getParameterTypes();
for (Class c1 : paramTypes) {
System.out.print(c1.getName() + ",");
}
System.out.println(" ) ");
}
}
- 獲取類所有的屬性
/*
* 獲取類的成員屬性
*
* 成員屬性是java.lang.reflect.Field的對(duì)象
* Field類封裝了關(guān)于成員屬性的操作
* getFields() 方法獲取的是所有public的成員變量的信息
* getDeclaredField() 獲取的是該類自己聲明的成員屬性的信息(包括私有)
*/
private static void printFieldMessage(Object obj) {
Class c = obj.getClass();
Field[] fs = c.getDeclaredFields();
for (Field field : fs) {
//得到類成員屬性類型的類類型( int.class, String.class等等)
Class fieldType = field.getType();
//獲取成員屬性類型名
String typeName = fieldType.getName();
//獲取成員屬性名
String fieldName = field.getName();
System.out.println(typeName + " " + fieldName);
}
}
- 獲取類所有構(gòu)造方法的信息
/*
* 打印對(duì)象構(gòu)造方法的信息
*/
public static void printConMessage(Object obj)
{
Class c = obj.getClass();
/*
* 構(gòu)造函數(shù)也是對(duì)象
* java.lang.Constructor中封裝了構(gòu)造函數(shù)的信息
* getConstructors 獲取所有Pubic的構(gòu)造函數(shù)
* getDeclaredConstructors 獲取所有構(gòu)造方法
*/
//Constructor[] cs = c.getConstructors();
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
System.out.println(constructor.getName());
//獲取構(gòu)造函數(shù)的參數(shù)列表《饰怠--- 得到的是參數(shù)列表的類類型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName() + ", ");
}
System.out.println(" ) ");
}
}
- 獲取類方法穿挨,屬性,構(gòu)造方法的測(cè)試
public static void main(String[] args) {
String test = "hello";
System.out.println("得到類的成員方法信息:");
ClassUtil.printMethodMessage(test);
System.out.println("得到類的成員屬性信息:");
ClassUtil.printFieldMessage(test);
System.out.println("得到類的構(gòu)造方法信息:");
ClassUtil.printConMessage("hello world");
}
五.方法的反射
class A{
public void print(int a, int b)
{
System.out.println(a+b);
}
}
//獲取print(int, int)方法, 要獲取方法肴盏,就是獲取類的信息科盛,首先獲取類的類型s
public static void main(String[] args){
A a1 = new A();
Class c= a1.getClass();
/**
* 獲取方法名和參數(shù)列表
* getMethod() 獲取public方法
* getDelcaredMethod 獲取自己聲明的方法
*/
Method m = c.getMethod("print", new Class[]{int.class,int.class});
//Method m2 = c.getMethod("print", int.class, int.class); 和上面一行等效
//方法的反射,用m對(duì)象進(jìn)行方法的調(diào)用,和a1.print(10,20)效果相同
//方法如果沒(méi)有返回值則返回null, 否則返回具體的返回值
Object o = m.invoke(a1, new Object[]{10,20}); //或m.invoke(a1, 10,20); //輸出30
}
五.通過(guò)Class,Method了解泛型的本質(zhì)
- 我們先看一段代碼
ArrayList list1 = new ArrayList();
ArrayList<String> list2 = new ArrayList<String>();
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1==c2);//輸出結(jié)果為true
//list2.add(100); //會(huì)報(bào)錯(cuò)菜皂,因?yàn)閘ist2定義的是String類型
可以看到以上list1是未定義泛型的贞绵,而list2定義為String類型的泛型,
以上 c1==c2
打印輸出 true
, 說(shuō)明編譯后集合的泛型是 去泛型化的
恍飘,也就證明了java中集合的泛型榨崩,是防止錯(cuò)誤輸入
的谴垫,只在編譯階段有效
,繞過(guò)編譯就無(wú)效了.
- 我們現(xiàn)在通過(guò)方法的反射蜡饵,調(diào)用list2.add()方法添加非String類的數(shù)據(jù)
try {
Method m = c1.getMethod("add", Object.class);
m.invoke(list2, 10); //繞過(guò)編譯操作弹渔,等同于繞過(guò)了泛型
System.out.println(list2.size());//輸出1
} catch (Exception e) {
// TODO: handle exception
}
以上代碼可以正常運(yùn)行胳施,我們想list2中添加了100整型溯祸,卻添加成功了,說(shuō)明我們的 反射機(jī)制
是 繞過(guò)編譯階段
的舞肆,直接體現(xiàn)在 運(yùn)行階段
焦辅;