?
在Java運行時環境中,對于任意一個類,能否知道這個類的哪些屬性和方法?對于任意一個對象,能否調用它的任意一個方法?答案是肯定的。這種動態獲取類的信息以及動態調用對象的方法的功能來自于Java語言的反射(Reflection)機制。
反射給java提供了,運行時獲取一個類實例的可能,這一點非常靈活,你僅僅傳一個類的全限定名,就能通過反射,來獲取對應的類實例,我們一般會用Class類,來調用這個被反射的Objcet類下的:構造方法,屬性,或方法等。
反射在一些開源框架里用的非常之多,Spring,Struts,Hibnerate,MyBatics都有它的影子,反射雖然很靈活,能夠使得寫的代碼,變的大幅精簡,所以在用的時候,一定要注意具體的應用場景。
反射的優缺點如下:
優點:
A:能夠運行時動態獲取類的實例,大大提高系統的靈活性和擴展性。
B:與Java動態編譯相結合,可以實現無比強大的功能
缺點:
A:使用反射的性能較低
B:使用反射相對來說不安全
C:破壞了類的封裝性,可以通過反射獲取這個類的私有方法和屬性
任何事物,都有兩面性,反射的優點,也同是就是它的缺點,所以,沒有好與壞,只有最合適的場景,一陰一陽,才是天道平衡的條件。
在反射API中我們重點關注一下幾個類:
Class? -- 代表類
Field? -- 代表屬性(成員變量)
Method? -- 代表方法
Constructor? -- 代表構造方法
一、Class
Java中不論一個類產生了多少個對象,這些對象的Class對象都始終是一個。Class對象中含有該類的任何信息(屬性,方法,類名,父類,包等),在Java中獲取Class對象的方法有三種:
// 第一種方法:類名.class
Class ? cla = Student.class;
Class ? as =int.class;?? // 基本數據類型唯一能點出的就是class
// 第二種方法:通過對象調用.getClass()
Student ? stu =newStudent();
Class ? c = stu.getClass();
// 第三種方法:通過類的全限定名獲取
try{
Class ? c1 = Class.forName("entity.Student");
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(cla.getName()); // 全限定名
System.out.println(Modifier.toString(cla.getModifiers()));
System.out.println(cla.getSimpleName());
System.out.println(cla.getPackage().getName());
可以通過Class對象產生該類的對象,如下:
// 獲取Class對象
Class ? cla = Student.class;
/* 1. 直接創建對象(調用默認無參構造方法),類里必須要有默認構造方法 */
Object ? obj1 = cla.newInstance();
System.out.println(obj1);
二、Constructor
如果想通過有參構造方法來創建對象,那么這時候就得先獲取有參構造方法,再通過有參構造方法來創建對象:
// 獲取Class對象
Class cla = Student.class;
/* 1. 直接創建對象(調用默認無參構造方法),類里必須要有默認構造方法 */
Object obj1 = cla.newInstance();
System.out.println(obj1);
/* 2. 通過無參構造方法創建對象,和第一種方法效果一樣 */
Constructor no = cla.getConstructor(); // 先獲取無參構造方法
Object obj2 = no.newInstance();
System.out.println(obj2);
/* 3. 通過有參構造方法創建對象 */
Constructor has = cla.getDeclaredConstructor(String.class,int.class); // 形參
Object obj3 = has.newInstance("老李", 23); // 傳入的是實參
System.out.println(obj3);
三、Field
Field代表是類中的屬性,我們可以獲取屬性,并修改其值(注:先得有對象才能修改值,另:修改沒權限的屬性時,需要先打開該屬性的權限)。
// 獲取Class對象
Class cla =newStudent().getClass();
// 獲取構造方法
Constructor con = cla.getConstructor(String.class,int.class);
// 創建對象
Object obj = con.newInstance("如來", 222);
// 獲取要操作的屬性
Field name = cla.getDeclaredField("name");
// 反射操作private屬性的時候,需要打開權限
name.setAccessible(true);
// 獲取obj的name屬性值
System.out.println(name.get(obj));
// 把obj的name屬性值改為:菩提
name.set(obj, "菩提");
System.out.println(name.get(obj));
// 把id設置為10086
Field id = cla.getDeclaredField("id");
id.set(obj, 10086);
System.out.println(id.get(obj))
四、Method
Method代表類中的方法,和Field操作類型:
// 獲取Class對象
Class cla = Student.class;
// 創建對象
Object obj = cla.getDeclaredConstructor(String.class,int.class).newInstance("達摩", 666);
// 獲取要操作的方法
Method showNo = cla.getDeclaredMethod("show");
Method showHas = cla.getDeclaredMethod("show", String.class);
Method calc = cla.getDeclaredMethod("calc",int.class,double.class);
calc.setAccessible(true);
// 調用方法
showNo.invoke(obj);
showHas.invoke(obj, "老衲");
Object value = calc.invoke(obj, 10086, Math.PI);
System.out.println(value);
我們可以用反射來改進簡單工廠模式:
packagedemo08;
importjava.io.FileInputStream;
importjava.util.Properties;
publicclassPetFactory {
publicstaticvoidmain(String[] args) {
System.out.println(getInstance("dog")); ?// demo08.Dog@67a9b034
}
// 工廠方法
publicstaticPet ? getInstance(String tag) {
Properties ? p =newProperties();
try{
p.load(newFileInputStream("conf/pet.properties"));
}catch(Exception e) {
System.out.println("加載配置文件錯誤!");
}
String ? className = p.getProperty(tag);
try{
// 利用反射創建對象
Class ? cla = Class.forName(className);
return(Pet)cla.newInstance();
}catch(ClassNotFoundException e) {
System.out.println("無法識別您的標識!");
}catch(InstantiationException e) {
e.printStackTrace();
}catch(IllegalAccessException e) {
e.printStackTrace();
}
returnnull;
}
}
classPet {}
classDogextendsPet {}
classCatextendsPet {}
classPenguinextendsPet {}
conf/pet.properties文件內容如下:
dog=demo08.Dog
cat=demo08.Cat
penguin=demo08.Penguin
這里的配置文件為.properties,稱作屬性文件。通過反射讀取里邊的內容。這樣代碼是固定的,但是配置文件的內容我們可以改,這樣使我們的代碼靈活了很多!
綜上JAVA反射的再次學習,靈活的運用它,能夠使我們的代碼更加靈活,但是它也有它的缺點,就是運用它會使我們的軟件的性能降低,復雜度增加,所以還要我們慎重的使用它。
更多內容關注微信公眾號mjw-java或訪問www.moliying.com