在正常情況下,需要先有一個類的完整路徑引入之後才可以按照固定的格式產生實例話對象,但是在Java中也允許通過一個實例話對象找到一個類的完整信息。那麼這就是Class類的功能。
實際上類是Java反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即可以通過對象的反射求出類的名稱。
實例化Class類,獲得字節碼文件的方法有三種:
第一種:通過forName()方法;第二種:類.class第三種:對象.getClass()
package toto.learn;
class X1{}
publicclass GetClassDemo02 {
publicstaticvoid main(String[] args) {
Class c1=null;//指定泛型
Class c2=null;//指定泛型
Class c3=null;//指定泛型
try{
c1=Class.forName("toto.learn.X");//最常用的形式,這種方式將字節碼文件加載到內存中。
}catch(ClassNotFoundException e){
e.printStackTrace();
}
c2 = new X1().getClass();//通過Object類中的方法實例
c3 = X1.class;//通過類class實例化
System.out.println("類名稱:"+c1.getName());//得到類的名稱
System.out.println("類名稱:"+c2.getName());//得到類的名稱
System.out.println("類名稱:"+c3.getName());//得到類的名稱
}
}
通過以上方法獲得類名稱的方式得到的是包名+類名
如果要想通過Class類本身實例化其他類的對象,則可以使用newInstance()方法,但是必須要保證被實例化的類中必須存在一個無參夠造方法。
被實例化對象的類中必須存在無參構造方法,如果不存在的話,則肯定是無法實例化的。
1、 通過Class類中的getConstructors()取得本類中的全部構造方法
2、 向構造方法中傳遞一個對象數組進去,裡面包含了構造方法中所需的各個參數
3、 之後通過Constructor實例化對象。
package org.lxh.demo15.instancedemo;
import java.lang.reflect.Constructor;
publicclass InstanceDemo03 {
publicstaticvoid main(String[] args) {
Class c = null;
try {
c = Class.forName("org.lxh.demo15.instancedemo.Person"); // 聲明Class對象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Person per = null; // 聲明Person對象
Constructor cons[] = null; // 聲明一個表示構造方法的數組
cons = c.getConstructors(); // 通過反射,取得全部構造
try {// 向構造方法中傳遞參數,此方法使用可變參數接收,並實例化對象
per = (Person) cons[0].newInstance("李興華", 30);
} catch (Exception e) { // 因為只有一個構造,所以數組下標為0
e.printStackTrace();
}
System.out.println(per); // 輸出對象
}
}
per = (Person)cons[0].newInstance("李興華", 30); //此處是調用並使用構造方法的部分。
在聲明對象數組的時候,必須考慮到構造方法中參數的類型順序,所以第一個參數的類型為Stirng,第二個參數的類型WieInteger(在使用是可以自動拆箱)
Constructorcons[]=null;//實例化構造方法的數組
Cons =c.getConstructors();//取得全部構造
//向構造方法中傳遞參數,此方使用可變參數接收,並實例化對象
Per = (Person)cons[0].newInstance(“李興華”,30);
設置構造方法的參數內容
publicPerson(String name,int age){//通過構造設置屬性內容
}
反射的應用
可以使用反射取得實現的全部接口
可以使用反射取得一個類所繼承的父類
可以使用反射取得一個類中的全部構造方法
可以使用反射取得一個類中的全部方法
可以使用反射取得一個類中的全部屬性
在實際開發中發,以上的程序就是反射應用最多的地方,當然反射機制所提供的功能遠不如此,還可以通過反射得到一個類中的完整構造,那麼這就要使用到java.lang.reflect包中的一下幾個類。
Constructor:表示類中的構造方法
Field:表示類中的屬性
Method:表示類中的方法
這三個類都是AccessibleObject類中的子類。
要想取得一個類中所實現的全部接口,則必須使用Class類中的getInterfaces()方法。此方法定義如下:
publicClass[] getInterfaces();
此方法返回一個Class類的對象數組,之後就可以直接利用Class類中的getName()方法輸出即可。
通過反射取得實現的全部接口
package org.lxh.demo15;
publicclass GetInterfaceDemo {
publicstaticvoid main(String[] args) {
Class c1 =null;//聲明Class對象
try{
c1 = Class.forName("org.lxh.demo15.Person");//實例化Class對象
}catch(ClassNotFoundException e){
e.printStackTrace();
}
Class c[] = c1.getInterfaces();//取得實現的全部接口
for(int i=0;i
System.out.println("實現的接口名稱:"+c[i].getName());//輸出接口名稱
}
}
}
一個類中可以實現多個接口,但是只能繼承一個父類,所以如果要想取得一個類的父類,可以直接使用Class類中的getSuperclass()方法。此方法定義如下:
PublicClass getSuperclass()
此方法返回的是Class實例,和之前的得到接口一樣,可以通過getName()方法取得名稱。
取得構造方法的例子:
package org.lxh.demo15;
import java.lang.reflect.Constructor;//導入反射操作包
publicclass GetConstructorDemo01 {
publicstaticvoid main(String[] args) {
Class c1 = null;//聲明Class對象
try{
c1 = Class.forName("org.lxh.demo15.Person");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
Constructor con[]=c1.getConstructors();//得到全部構造方法
for(int i=0;i
System.out.println("構造方法:"+con[i]);//直接打印輸出
}
}
}
還原修飾符
在整個Java中對於方法的修飾符使用一定的數字表示出來的,而如果要想把這個數字還原成用戶可以看懂的關鍵字,則必須依靠Modifier類完成,此類定義在java.lang.reflect包中。直接使用Modifer類的一下方法可修飾符:
publicstatic String toString(int mod)
int mo = con[i].getModifiers();
System.out.print(Modifier.toString(mo)+””); //還原權限
getDeclaredMethods()方法,此方法返回一個Method類的對象數組,而如果要想進一步取得方法具體信息,例如:方法的參數,拋出的異常聲明等等,則就是必須依靠Method類
再反射操作中同樣可以取得一個類中的全部屬性,但是在取得屬性的時候有以下兩種不同的操作:
得到實現的接口或父類中的公共屬性:public Field[] getFields() throwsSecurityException
得到本類中自己定義的的全部屬性:public Field[] getDeclaredFields() throws SecurityException
如果要使用反射調用類中的方法可以通過Method類完成,操作步驟如下:
1、 通過Class類的getMethod(Stringname,Class…parameterTypes)方法取得一個Method的對象,並設置此方法操作時所需的參數類型。
2、 之後才可以使用invoke進行調用,並向方法中傳遞要設置的參數。
在Proxy類中的newProxyInstance()方法中,需要一個ClassLoader類的實例,ClassLoader實際上對應的是類加載器,在Java中主要有以下三種類加載器:
BootstrapClassLoader:此加載器采用C++ 編寫,一般開發中是看不到的;
ExtensionClassLoader:用來進行擴展類的加載,一般對應的是jrelibext目錄中的類;
AppClassLoader:加載classpath指定的類,是最常用使用的一種加載器。
通過forName()加載類兩次時,此時的類只加載了一次
如果有以下代碼:
Object obj= new VipUser();//這裡VipUser是User的子類,它繼承了User
if(obj instanceof User){
System.out.println("instanceof判斷是一個user");
User user = (User)obj;
}
當使用以上代碼中的instanceof來判斷時,此時obj就是一個User了,但實際上它不是。它只是User的一個子類。
總結:使用instanceof時主要用來判斷是否實現了後面的接口。
if(obj.getClass()==User.class){
System.out.println("getClass判斷,是一個user");
User user = (User)ob