反射的概念
反射的概念是由 Smith 在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。
換句話說,就是能夠得到代碼自身的特征。
換句話說,就是把類本身也看成是對象,包括類中的變量名、方法名、內部類、超類、包、修飾符等等,都可以通過代碼來得到并被看成是對象。
java為此設計了一些類來方便我們使用反射。這些類并不多,它們分別是:Field、Constructor、Method、Class、Object,下面對這些類做一個簡單的說明。
摘抄于其它資料,僅供閱讀
Field 類:提供有關類或接口的屬性的信息,以及對它的動態(tài)訪問權限。反射的字段可能是一個類(靜態(tài))屬性或實例屬性,簡單的理解可以把它看成一個封裝反射類的屬性的類。
Constructor 類:提供關于類的單個構造方法的信息以及對它的訪問權限。這個類和 Field 類不同,F(xiàn)ield 類封裝了反射類的屬性,而 Constructor 類則封裝了反射類的構造方法。
Method 類:提供關于類或接口上某個單獨方法的信息。所反映的方法可能是類方法或實例方法(包括抽象方法)。 這個類不難理解,它是用來封裝反射類方法的一個類。
Class 類:類的實例表示正在運行的 Java 應用程序中的類和接口。枚舉是一種類,注釋是一種接口。每個數(shù)組屬于被映射為 Class 對象的一個類,所有具有相同元素類型和維數(shù)的數(shù)組都共享該 Class 對象。
Object 類:每個類都使用 Object 作為超類。所有對象(包括數(shù)組)都實現(xiàn)這個類的方法。
獲取Class類
有一個類,類名是Class,(首字母大寫,不同于關鍵字class)。任何一個java類都是這個Class類的對象,即“類本身也是對象”的感覺。
一旦我們獲取到了一個類的Class實例,那么在此基礎上要獲取Field、Constructor、Method等等的話也就很容易了(因此java的所有代碼都在類中的嘛)。所以首要步驟是獲取Class實例。
獲取類自身有三種方式:
(1)利用 對象.getClass() 的方式獲取該對象的Class實例;
(2)利用 對象.class 的方式來獲取Class實例,對于基本數(shù)據(jù)類型的封裝類,還可以采用.TYPE來獲取相對應的基本數(shù)據(jù)類型的Class實例;
(3)使用 Class類的靜態(tài)方法forName(“全路徑名”),用類的名字獲取一個Class實例。
示例
class ClassTest {
public static void main(String[] args) throws Exception {
String str1 = "abc";
Class cls1 = str1.getClass();//法一
Class cls2 = String.class;//法二
Class cls3 = Class.forName("java.lang.String");//法三
System.out.println(cls1 == cls2);
System.out.println(cls1 == cls3);
}
}
運行結果為
true
true
解釋
1、運行結果為true說明虛擬機為某個類只會產(chǎn)生一份字節(jié)碼,將來用這份字節(jié)碼可以產(chǎn)生多個實例對象。
2、也即是說,在運行期間,如果我們要產(chǎn)生某個類的對象,Java虛擬機(JVM)會檢查該類型的Class對象是否已被加載。如果沒有被加載,JVM會根據(jù)類的名稱找到.class文件并加載它。一旦某個類型的Class對象已被加載到內存,就可以用它來產(chǎn)生該類型的所有對象。
利用Class實例創(chuàng)建對象
以前我們創(chuàng)建對象都是用“new 類名()”的方式,現(xiàn)在我們先得到構造方法,并用構造方法來創(chuàng)建,F(xiàn)在我們要使用Consturctor(構造器)類:它代表某個類中的一個構造方法。
得到某個類所有的構造方法
Constructor [] constructors = Class.forName("java.lang.String").getConstructors();
得到某一個構造方法
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
注:參數(shù)是一個Class實例,即去拿匹配這樣參數(shù)的構造方法。
創(chuàng)建實例對象,用Constructor的newInstance方法
傳統(tǒng)方式:String str=new String(new StringBuffer("abc"));
反射方式:String str=(String) constructor.newInstance(new StringBuffer("abc"));
注:newInstance()方法參數(shù)可變長,請嘗試放多個參數(shù)。不合適時,報異常IllegalArgumentException。
上述原理可以下面示例來演練
class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("java.lang.String");
Constructor constructor = c.getConstructor(StringBuffer.class);
String str = (String) constructor.newInstance(new StringBuffer("abc"));
System.out.println(str);
}
}
利用Constructor來創(chuàng)建實例與利用Class類來創(chuàng)建實例
class類也有創(chuàng)建實例的方法,下面的例子進行了展示。
此例來源于//seahb.iteye.com/blog/855107。
import java.lang.reflect.Constructor;
class A {
private A() { // 將private改為public試試
System.out.println("A's constructor A() is called.");
}
private A(int a, int b) {
System.out.println("A's constructor A(a,b) is called.");
System.out.println("a:" + a + " b:" + b);
}
}
class B {
public static void main(String[] args) {
B b = new B();
System.out.println("通過Class.NewInstance()調用私有構造函數(shù):");
b.byClassNewInstance();
System.out.println("通過Constructor.newInstance()調用私有構造函數(shù):");
b.byConstructorNewInstance();
}
/* 法一:通過Class.NewInstance()創(chuàng)建新的類示例 */
private void byClassNewInstance() {
try {
Class c = Class.forName("A");
A a = (A) c.newInstance();//調用無參構造方法。如果方法是私有的,則運行時會異常IllegalAccessException
} catch (Exception e) {
e.printStackTrace();
System.out.println("通過Class.NewInstance()調用構造方法【失敗】");
}
}
/*法二:通過Constructor.newInstance()創(chuàng)建新的類示例 */
private void byConstructorNewInstance() {
try {
Class c = Class.forName("A");
Constructor c0 = c.getDeclaredConstructor();/* 調用無參構造方法 */
c0.setAccessible(true); //必須設置一下可見性后就可調用了
A a0 = (A) c0.newInstance();//調用構造方法
System.out.println("成功1");
Constructor c1 = c.getDeclaredConstructor(new Class[] { int.class, int.class });/* 調用帶參構造方法 */
c1.setAccessible(true);
//A a1 = (A) c1.newInstance(new Object[] { 5, 6 });//參數(shù)是對象數(shù)組
A a1 = (A) c1.newInstance(5, 6);//參數(shù)可連寫,因為newInstance()支持可變參數(shù)
//A a1 = (A) c1.newInstance(5, 6,7);//參數(shù)若不合適,則就報異常IllegalArgumentException
System.out.println("成功2");
} catch (Exception e) {
e.printStackTrace();
}
}
}
結論
class.newInstance和constructor.newInstance 區(qū)別
通過反射創(chuàng)建新的類示例,有兩種方式:
Class.newInstance()
Constructor.newInstance()
Class.newInstance() 只能夠調用無參的構造函數(shù),即默認的構造函數(shù);
Constructor.newInstance() 可以根據(jù)傳入的參數(shù),調用任意構造構造函數(shù)。
Class.newInstance() 要求被調用的構造函數(shù)是可見的,也即必須是public類型的;
Constructor.newInstance() 在特定的情況下,可以調用私有的構造函數(shù)。
如果被調用的類的構造函數(shù)為默認的構造函數(shù),采用Class.newInstance()則是比較好的選擇,一句代碼就OK;如果是調用帶參構造函數(shù)、私有構造函數(shù),就需要采用Constractor.newInstance(),兩種情況視使用情況而定。不過Java Totorial中推薦采用Constractor.newInstance()。