在正常情況下,需要先有一個類的完整路徑引入之後才可以按照固定的格式產生實例話對象,但是在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)obj;
}
而getClass()用於判斷對象的准確類型。
在後面用到的類:
package toto.learn1;
publicclass User {
private String name;
private String password;
private String gender;
public User(String name, String password, String gender) {
super();
this.name = name;
this.password = password;
this.gender = gender;
}
public User() {
super();
// TODO Auto-generatedconstructor stub
}
public User(String name, String password) {
super();
this.name = name;
this.password = password;
}
public String getName() {
returnname;
}
publicvoid setName(String name) {
this.name = name;
}
public String getPassword() {
returnpassword;
}
publicvoid setPassword(String password) {
this.password = password;
}
public String getGender() {
returngender;
}
publicvoid setGender(String gender) {
this.gender = gender;
}
publicvoid run(String str, int num){
System.out.println("run");
}
publicvoid run(String name){
System.out.println("hello:"+name);
}
}
以下是關於反射的部分。
package toto.learn1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
publicclass Demo2 {
/**
* 反射
* @throwsIllegalAccessException
* @throwsInstantiationException
* @throwsNoSuchMethodException
* @throwsSecurityException
*/
publicstaticvoid main(String[] args) throws Exception {
Class clazz = User.class;
//構造方法
//獲得構造方法
Constructor[] cons = clazz.getConstructors();
for(Constructor con : cons){
//參數列表,獲得的約定是完整的參數類型,包括這種類型坐在的包。
Class[] types = con.getParameterTypes();
System.out.println("參數類型為:");
for(Class type:types){
System.out.println(type.getName()+"...");
}
System.out.println();
}
//獲得指定的構造方法 創建對象
try {
Object obj = clazz.newInstance();//默認調用無參的構造方法
System.out.println(obj.getClass());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
/**參數類型為:
java.lang.String...
java.lang.String...
參數類型為:
java.lang.String...
java.lang.String...
java.lang.String...
class toto.learn1.User*/
System.out.println("-------------------------");
//獲得指定的構造方法2 創建對象
Object obj = clazz.newInstance();//默認調用無參的構造方法
Constructor constructor = clazz.getConstructor(String.class,String.class);//此處調用有兩個參數的構造方法
//String.class是因為構造方法中有帶有年個參數的構造方法的形參都是String類的。
User usr = (User)constructor.newInstance("toto","查看");//傳遞兩個參數。
System.out.println(usr.getName()+" "+usr.getPassword());//結果:toto 查看
//方法
//獲得類所有的方法
Method [] methods = clazz.getMethods();
for(Method method:methods){
System.out.println(method.getName());
}
/**通過getMethods()方法獲得的類中包含有父類的方法,並且拿到的是共有的方法
* run
run
getName
setName
getPassword
setPassword
getGender
setGender
wait
wait
wait
hashCode
getClass
equals
toString
notify
notifyAll
* */
//如果想獲得聲明的所有方法,包括非public的,不包括繼承的,可以使用一下途徑實現
System.out.println("-----------------------");
Method [] methods2 = clazz.getDeclaredMethods();
for(Method method:methods2){
System.out.println(method.getName());
}
/**結果是:
* run
run
getName
setName
getPassword
setPassword
getGender
setGender*/
//獲得指定的方法,調用方法
Method runMethod = clazz.getMethod("run", String.class); /**第一個表示要調用那個方法
第二個參數是可變參數,表示調用的潤方法的參數類型。*/
//獲得了方法後,要執行時,得給出一個對象,在這裡要執行的對象是User,故作如下實例
User usr1 = new User();
runMethod.invoke(usr1, "toto");//表示執行usr1對象中的runMethod方法,並且向方法中傳遞了參數。
/**
* 運行結果:hello:toto 此為User類中run(String name);方法輸出的結果,其中name即為toto*/
//屬性
//獲得對象的屬性,先創建一個對象
Object object = new User("toto","123","male");//調用User中的構造方法:
//由於獲得對象的私有屬性,得獲得get方法,故得先獲得對象的字節碼文件,通過這個文件獲得get方法
Class objectClazz = object.getClass();
//獲得get方法
Method runMethod2 = clazz.getMethod("getName"); //由於該方法沒有參數,故不用在向裡面傳遞參數
//執行此方法,輸出參數
Object name = runMethod2.invoke(object);
System.out.println(name);//由於上面傳遞了name值,故此處返回的結果值為:toto
//獲得所有屬性
System.out.println("-------------------------------");
//Field[] fields =clazz.getFields();//此方法拿到的是public的屬性,由於User類中沒有共有的屬性
Field[] fields = clazz.getDeclaredFields();//拿到聲明的屬性
for(Field fd : fields){
System.out.println(fd.getName()+"屬性類型: "+fd.getType());
}
/** 結果為:
* name屬性類型: class java.lang.String
password屬性類型: class java.lang.String
gender屬性類型: class java.lang.String*/
//為對象的password屬性賦值:
User user2 = new User();
String fieldName = "password";//作為變量來處理的,可以傳遞任意的屬性名稱
//getpw是通過反射拿到的屬性,其實就是password
Field getpw = user2.getClass().getDeclaredField(fieldName);
//判斷屬性是否為私有:
int num = getpw.getModifiers();//返回的是整型的值。
if(num == Modifier.PRIVATE){//獲得的是
System.out.println("屬性是私有的");
//讓java虛擬機不檢查訪問權限
//getpw.setAccessible(true);
}
//System.out.println(getpw);//結果private java.lang.Stringtoto.learn1.User.password
getpw.set(user2, 234567);//前一個參數表示的是為那個對象賦密碼。
System.out.println(user2.getPassword());
}
}
l 通過Field類訪問對象屬性時,需要注意訪問權限的問題,若對象
的屬性被聲明為私有,則無法訪問,此時需要打開java語言檢查
filed.setAccessible(true);
l Method類代表某個類中的一個成員方法
l 通過invoke方法可以調用Method表示的方法,此時同樣需要傳遞
對象給Method,若被調用的方法為靜態,則傳入一個null值
l 注意:jdk1.4和jdk1.5的invoke方法的區別:
l Jdk1.5:public Object invoke(Objectobj,Object... args)
l Jdk1.4:public Object invoke(Objectobj,Object[] args)
l 當傳入一個數組時,為了向後兼容,虛擬機會優先考慮考慮
調用JDK1.4的方法