成都Java學習中如何認識反射,業內專家告訴你

成都Java學習中如何認識反射,業內專家告訴你

成都Java學習中如何認識反射,業內專家告訴你

反射的概念

反射的概念是由 Smith 在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力。

換句話說,就是能夠得到代碼自身的特徵。

換句話說,就是把類本身也看成是對象,包括類中的變量名、方法名、內部類、超類、包、修飾符等等,都可以通過代碼來得到並被看成是對象。

java為此設計了一些類來方便我們使用反射。這些類並不多,它們分別是:Field、Constructor、Method、Class、Object,下面對這些類做一個簡單的說明。

摘抄於其它資料,僅供閱讀

Field 類:提供有關類或接口的屬性的信息,以及對它的動態訪問權限。反射的字段可能是一個類(靜態)屬性或實例屬性,簡單的理解可以把它看成一個封裝反射類的屬性的類。

Constructor 類:提供關於類的單個構造方法的信息以及對它的訪問權限。這個類和 Field 類不同,Field 類封裝了反射類的屬性,而 Constructor 類則封裝了反射類的構造方法。

Method 類:提供關於類或接口上某個單獨方法的信息。所反映的方法可能是類方法或實例方法(包括抽象方法)。 這個類不難理解,它是用來封裝反射類方法的一個類。

Class 類:類的實例表示正在運行的 Java 應用程序中的類和接口。枚舉是一種類,註釋是一種接口。每個數組屬於被映射為 Class 對象的一個類,所有具有相同元素類型和維數的數組都共享該 Class 對象。

Object 類:每個類都使用 Object 作為超類。所有對象(包括數組)都實現這個類的方法。

獲取Class類

有一個類,類名是Class,(首字母大寫,不同於關鍵字class)。任何一個java類都是這個Class類的對象,即“類本身也是對象”的感覺。

一旦我們獲取到了一個類的Class實例,那麼在此基礎上要獲取Field、Constructor、Method等等的話也就很容易了(因此java的所有代碼都在類中的嘛)。所以首要步驟是獲取Class實例。

獲取類自身有三種方式:

(1)利用 對象.getClass() 的方式獲取該對象的Class實例;

(2)利用 對象.class 的方式來獲取Class實例,對於基本數據類型的封裝類,還可以採用.TYPE來獲取相對應的基本數據類型的Class實例;

(3)使用 Class類的靜態方法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說明虛擬機為某個類只會產生一份字節碼,將來用這份字節碼可以產生多個實例對象。

2、也即是說,在運行期間,如果我們要產生某個類的對象,Java虛擬機(JVM)會檢查該類型的Class對象是否已被加載。如果沒有被加載,JVM會根據類的名稱找到.class文件並加載它。一旦某個類型的Class對象已被加載到內存,就可以用它來產生該類型的所有對象。

利用Class實例創建對象

以前我們創建對象都是用“new 類名()”的方式,現在我們先得到構造方法,並用構造方法來創建。現在我們要使用Consturctor(構造器)類:它代表某個類中的一個構造方法。

得到某個類所有的構造方法

Constructor [] constructors = Class.forName("java.lang.String").getConstructors();

得到某一個構造方法

Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

注:參數是一個Class實例,即去拿匹配這樣參數的構造方法。

創建實例對象,用Constructor的newInstance方法

傳統方式:String str=new String(new StringBuffer("abc"));

反射方式:String str=(String) constructor.newInstance(new StringBuffer("abc"));

注:newInstance()方法參數可變長,請嘗試放多個參數。不合適時,報異常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來創建實例與利用Class類來創建實例

class類也有創建實例的方法,下面的例子進行了展示。

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()調用私有構造函數:");

b.byClassNewInstance();

System.out.println("通過Constructor.newInstance()調用私有構造函數:");

b.byConstructorNewInstance();

}

/* 法一:通過Class.NewInstance()創建新的類示例 */

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()創建新的類示例 */

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 });//參數是對象數組

A a1 = (A) c1.newInstance(5, 6);//參數可連寫,因為newInstance()支持可變參數

//A a1 = (A) c1.newInstance(5, 6,7);//參數若不合適,則就報異常IllegalArgumentException

System.out.println("成功2");

} catch (Exception e) {

e.printStackTrace();

}

}

}

結論

class.newInstance和constructor.newInstance 區別

通過反射創建新的類示例,有兩種方式:

Class.newInstance()

Constructor.newInstance()

Class.newInstance() 只能夠調用無參的構造函數,即默認的構造函數;

Constructor.newInstance() 可以根據傳入的參數,調用任意構造構造函數。

Class.newInstance() 要求被調用的構造函數是可見的,也即必須是public類型的;

Constructor.newInstance() 在特定的情況下,可以調用私有的構造函數。

如果被調用的類的構造函數為默認的構造函數,採用Class.newInstance()則是比較好的選擇,一句代碼就OK;如果是調用帶參構造函數、私有構造函數,就需要採用Constractor.newInstance(),兩種情況視使用情況而定。不過Java Totorial中推薦採用Constractor.newInstance()。


分享到:


相關文章: