反射(Class類、Constructor類、Field類、Method類)
阿新 • • 發佈:2018-11-01
反射:通過類的Class物件來操作。可以得到屬性物件,方法物件,構造器物件。可以通過Class物件和構造器物件來例項化物件。但是,通過Class物件來例項化時不能傳遞引數,通過構造器時可以傳遞引數。
1.Class類例項化的幾種方式:
- 可以通過類名.class;
- 可以通過物件名.getClass;
- 可以通過Class的靜態方法forName方法(傳遞類的路徑名)來獲取Class物件。
- 對於包裝器型別來說,可以通過類名.TYPE來獲取。
public class Maintest { public static void main(String[] args)throws Exception{ Integer i=new Integer(12); Class i1=Integer.class; Class i2=i.getClass(); Class i3=Class.forName("java.lang.Integer"); Class i4=Integer.TYPE;//基本資料型別的Class類 Class i5=int.class; System.out.println(i1==i2&&i2==i3); System.out.println(i4==i5); } }
執行結果都為true
2.Class類的方法:可以通過Class方法獲得各類所實現的介面,該類的全名,該類的直接父類。獲得所有的構造器,獲得所有的方法及成員變數。getDeclaredxxx表示獲得所有的,getxxx表示獲得public修飾的。
public class Maintest { public static void main(String[] args)throws Exception{ Class c=String.class; System.out.println(c.getName());//輸出類的全名 System.out.println(Arrays.toString(c.getInterfaces()));//輸出類所實現的介面 System.out.println(c.getSuperclass());//輸出類的直接父類 } }
執行結果:
java.lang.String
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
class java.lang.Object
自定義一個Person類,只有兩個私有的name和age屬性,get和set方法,有參和無參構造。
獲得構造器的方法:
public class Maintest { public static void main(String[] args)throws Exception{ Class per=Person.class; Constructor[] constructors=per.getDeclaredConstructors();//獲取所有構造 Constructor[] constructors1=per.getConstructors();//獲取public修飾的構造 for(Constructor con:constructors)//所有 { System.out.println(con); } System.out.println("-------------------------"); for(Constructor con:constructors1)//public修飾的 { System.out.println(con); } } }
執行結果:
private test.Person()
public test.Person(java.lang.String,int)
-------------------------
public test.Person(java.lang.String,int)
獲得成員變數物件,並通過每個成員變數物件來獲得變數名,變數型別,變數修飾符。也可以通過getDeclaredField方法傳遞變數名來獲得指定的變數。
public class Maintest {
public static void main(String[] args)throws Exception{
Class per=Person.class;
Field[] allF=per.getDeclaredFields();
System.out.println("屬性名\t\t"+"屬性型別\t\t"+"修飾符");
for(Field field:allF)
{
System.out.println(field.getName()+"\t\t"+field.getType()+"\t\t"+field.getModifiers());
}
}
}
結果:
屬性名 屬性型別 修飾符
name class java.lang.String 2
age int 2
獲得方法物件與之類似,可以通過方法名、形參來獲得指定的方法。通過invoke方法傳遞物件和實參來呼叫傳過去物件的該方法。方法物件可以通過getParameterTypes方法獲得方法的引數列表。
3.建立物件並例項化的幾種方法:
- new的時候呼叫有參構造;
- 通過Class類得到有參構造器的物件,通過這個構造器物件例項化並傳參。
- 建立好空物件,通過Class物件獲得get和set方法,呼叫方法傳遞引數
- 建立好空物件,通過Class物件獲得成員變數Field物件,如果為私有,將其設定為可訪問的。然後直接set值
public class Maintest {
public static void main(String[] args)throws Exception{
Class p=Person.class;
//例項化Person物件
//第一種
Person person1=new Person("zhangsan",24);
System.out.println(person1);
//第二種 通過反射得到特定構造方法。通過構造器例項化物件(傳參)
Constructor constructor=p.getDeclaredConstructor(String.class,int.class);
Person person2=(Person) constructor.newInstance(new Object[]{"lisi",23});
System.out.println(person2);
//第三種 空物件已經建立好 通過反射獲得get和set方法並呼叫。
Person person3=(Person) p.newInstance();//空物件,name為null,age為0
Method setname=p.getDeclaredMethod("setName", String.class);//獲得到方法物件,輸入型別避免過載
Method setage=p.getDeclaredMethod("setAge", int.class);
setname.invoke(person3,"luck");
setage.invoke(person3,22);
System.out.println(person3);
//第四種 和第三種類似,只不過是直接操作私有成員
Person person4=new Person();//空物件
Field name=p.getDeclaredField("name");
Field age=p.getDeclaredField("age");
name.setAccessible(true);//將私有變數設定為可直接操作的
age.setAccessible(true);
name.set(person4,"Dimond");
age.set(person4,23);
System.out.println(person4);
}
}
執行結果:
Person{name='zhangsan', age=24}
Person{name='lisi', age=23}
Person{name='luck', age=22}
Person{name='Dimond', age=23}
setAccessible不僅可以對私有屬性用,還可以對私有構造器用,同樣也可以對私有方法用。可以用getDeclaredxxx來獲取物件。
對陣列的操作,可以通過Array類,這個類就相當於陣列類的構造器物件。
public class Maintest {
public static void main(String[] args)throws Exception{
int[] i= (int[])Array.newInstance(int.class,10);
System.out.println(Arrays.toString(i));//陣列中為0
Array.setInt(i,8,888);//將第八個數設定為888
System.out.println(Arrays.toString(i));
}
}
執行結果:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 888, 0]
代理模式:代理物件來代理A物件,要用和A相同的方法。客戶端訪問A的方法時,不直接訪問,而是通過代理間接訪問。因此代理中要有A的引用(關聯關係)。又因為代理類中的方法和A中的方法一樣,因此他們實現同一個介面即可。可以通過反射來實現動態代理。