當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > java學(xué)習(xí)中如何認(rèn)識(shí)反射,業(yè)內(nèi)專(zhuān)家告訴你
java學(xué)習(xí)中如何認(rèn)識(shí)反射,業(yè)內(nèi)專(zhuān)家告訴你
時(shí)間:2018-06-22 來(lái)源:未知
反射的概念
反射的概念是由 Smith 在1982年首次提出的,主要是指程序可以訪(fǎng)問(wèn)、檢測(cè)和修改它本身狀態(tài)或行為的一種能力。
換句話(huà)說(shuō),就是能夠得到代碼自身的特征。
換句話(huà)說(shuō),就是把類(lèi)本身也看成是對(duì)象,包括類(lèi)中的變量名、方法名、內(nèi)部類(lèi)、超類(lèi)、包、修飾符等等,都可以通過(guò)代碼來(lái)得到并被看成是對(duì)象。
java為此設(shè)計(jì)了一些類(lèi)來(lái)方便我們使用反射。這些類(lèi)并不多,它們分別是:Field、Constructor、Method、Class、Object,下面對(duì)這些類(lèi)做一個(gè)簡(jiǎn)單的說(shuō)明。
摘抄于其它資料,僅供閱讀
Field 類(lèi):提供有關(guān)類(lèi)或接口的屬性的信息,以及對(duì)它的動(dòng)態(tài)訪(fǎng)問(wèn)權(quán)限。反射的字段可能是一個(gè)類(lèi)(靜態(tài))屬性或?qū)嵗龑傩�,�?jiǎn)單的理解可以把它看成一個(gè)封裝反射類(lèi)的屬性的類(lèi)。
Constructor 類(lèi):提供關(guān)于類(lèi)的單個(gè)構(gòu)造方法的信息以及對(duì)它的訪(fǎng)問(wèn)權(quán)限。這個(gè)類(lèi)和 Field 類(lèi)不同,F(xiàn)ield 類(lèi)封裝了反射類(lèi)的屬性,而 Constructor 類(lèi)則封裝了反射類(lèi)的構(gòu)造方法。
Method 類(lèi):提供關(guān)于類(lèi)或接口上某個(gè)單獨(dú)方法的信息。所反映的方法可能是類(lèi)方法或?qū)嵗椒?包括抽象方法)。 這個(gè)類(lèi)不難理解,它是用來(lái)封裝反射類(lèi)方法的一個(gè)類(lèi)。
Class 類(lèi):類(lèi)的實(shí)例表示正在運(yùn)行的 Java 應(yīng)用程序中的類(lèi)和接口。枚舉是一種類(lèi),注釋是一種接口。每個(gè)數(shù)組屬于被映射為 Class 對(duì)象的一個(gè)類(lèi),所有具有相同元素類(lèi)型和維數(shù)的數(shù)組都共享該 Class 對(duì)象。
Object 類(lèi):每個(gè)類(lèi)都使用 Object 作為超類(lèi)。所有對(duì)象(包括數(shù)組)都實(shí)現(xiàn)這個(gè)類(lèi)的方法。
獲取Class類(lèi)
有一個(gè)類(lèi),類(lèi)名是Class,(首字母大寫(xiě),不同于關(guān)鍵字class)。任何一個(gè)java類(lèi)都是這個(gè)Class類(lèi)的對(duì)象,即“類(lèi)本身也是對(duì)象”的感覺(jué)。
一旦我們獲取到了一個(gè)類(lèi)的Class實(shí)例,那么在此基礎(chǔ)上要獲取Field、Constructor、Method等等的話(huà)也就很容易了(因此java的所有代碼都在類(lèi)中的嘛)。所以首要步驟是獲取Class實(shí)例。
獲取類(lèi)自身有三種方式:
(1)利用 對(duì)象.getClass() 的方式獲取該對(duì)象的Class實(shí)例;
(2)利用 對(duì)象.class 的方式來(lái)獲取Class實(shí)例,對(duì)于基本數(shù)據(jù)類(lèi)型的封裝類(lèi),還可以采用.TYPE來(lái)獲取相對(duì)應(yīng)的基本數(shù)據(jù)類(lèi)型的Class實(shí)例;
(3)使用 Class類(lèi)的靜態(tài)方法forName(“全路徑名”),用類(lèi)的名字獲取一個(gè)Class實(shí)例。
示例
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);
}
}
運(yùn)行結(jié)果為
true
true
解釋
1、運(yùn)行結(jié)果為true說(shuō)明虛擬機(jī)為某個(gè)類(lèi)只會(huì)產(chǎn)生一份字節(jié)碼,將來(lái)用這份字節(jié)碼可以產(chǎn)生多個(gè)實(shí)例對(duì)象。
2、也即是說(shuō),在運(yùn)行期間,如果我們要產(chǎn)生某個(gè)類(lèi)的對(duì)象,Java虛擬機(jī)(JVM)會(huì)檢查該類(lèi)型的Class對(duì)象是否已被加載。如果沒(méi)有被加載,JVM會(huì)根據(jù)類(lèi)的名稱(chēng)找到.class文件并加載它。一旦某個(gè)類(lèi)型的Class對(duì)象已被加載到內(nèi)存,就可以用它來(lái)產(chǎn)生該類(lèi)型的所有對(duì)象。
利用Class實(shí)例創(chuàng)建對(duì)象
以前我們創(chuàng)建對(duì)象都是用“new 類(lèi)名()”的方式,現(xiàn)在我們先得到構(gòu)造方法,并用構(gòu)造方法來(lái)創(chuàng)建。現(xiàn)在我們要使用Consturctor(構(gòu)造器)類(lèi):它代表某個(gè)類(lèi)中的一個(gè)構(gòu)造方法。
得到某個(gè)類(lèi)所有的構(gòu)造方法
Constructor [] constructors = Class.forName("java.lang.String").getConstructors();
得到某一個(gè)構(gòu)造方法
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
注:參數(shù)是一個(gè)Class實(shí)例,即去拿匹配這樣參數(shù)的構(gòu)造方法。
創(chuàng)建實(shí)例對(duì)象,用Constructor的newInstance方法
傳統(tǒng)方式:String str=new String(new StringBuffer("abc"));
反射方式:String str=(String) constructor.newInstance(new StringBuffer("abc"));
注:newInstance()方法參數(shù)可變長(zhǎng),請(qǐng)嘗試放多個(gè)參數(shù)。不合適時(shí),報(bào)異常IllegalArgumentException。
上述原理可以下面示例來(lái)演練
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來(lái)創(chuàng)建實(shí)例與利用Class類(lèi)來(lái)創(chuàng)建實(shí)例
class類(lèi)也有創(chuàng)建實(shí)例的方法,下面的例子進(jìn)行了展示。
此例來(lái)源于//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("通過(guò)Class.NewInstance()調(diào)用私有構(gòu)造函數(shù):");
b.byClassNewInstance();
System.out.println("通過(guò)Constructor.newInstance()調(diào)用私有構(gòu)造函數(shù):");
b.byConstructorNewInstance();
}
/* 法一:通過(guò)Class.NewInstance()創(chuàng)建新的類(lèi)示例 */
private void byClassNewInstance() {
try {
Class c = Class.forName("A");
A a = (A) c.newInstance();//調(diào)用無(wú)參構(gòu)造方法。如果方法是私有的,則運(yùn)行時(shí)會(huì)異常IllegalAccessException
} catch (Exception e) {
e.printStackTrace();
System.out.println("通過(guò)Class.NewInstance()調(diào)用構(gòu)造方法【失敗】");
}
}
/*法二:通過(guò)Constructor.newInstance()創(chuàng)建新的類(lèi)示例 */
private void byConstructorNewInstance() {
try {
Class c = Class.forName("A");
Constructor c0 = c.getDeclaredConstructor();/* 調(diào)用無(wú)參構(gòu)造方法 */
c0.setAccessible(true); //必須設(shè)置一下可見(jiàn)性后就可調(diào)用了
A a0 = (A) c0.newInstance();//調(diào)用構(gòu)造方法
System.out.println("成功1");
Constructor c1 = c.getDeclaredConstructor(new Class[] { int.class, int.class });/* 調(diào)用帶參構(gòu)造方法 */
c1.setAccessible(true);
//A a1 = (A) c1.newInstance(new Object[] { 5, 6 });//參數(shù)是對(duì)象數(shù)組
A a1 = (A) c1.newInstance(5, 6);//參數(shù)可連寫(xiě),因?yàn)閚ewInstance()支持可變參數(shù)
//A a1 = (A) c1.newInstance(5, 6,7);//參數(shù)若不合適,則就報(bào)異常IllegalArgumentException
System.out.println("成功2");
} catch (Exception e) {
e.printStackTrace();
}
}
}
結(jié)論
class.newInstance和constructor.newInstance 區(qū)別
通過(guò)反射創(chuàng)建新的類(lèi)示例,有兩種方式:
Class.newInstance()
Constructor.newInstance()
Class.newInstance() 只能夠調(diào)用無(wú)參的構(gòu)造函數(shù),即默認(rèn)的構(gòu)造函數(shù);
Constructor.newInstance() 可以根據(jù)傳入的參數(shù),調(diào)用任意構(gòu)造構(gòu)造函數(shù)。
Class.newInstance() 要求被調(diào)用的構(gòu)造函數(shù)是可見(jiàn)的,也即必須是public類(lèi)型的;
Constructor.newInstance() 在特定的情況下,可以調(diào)用私有的構(gòu)造函數(shù)。
如果被調(diào)用的類(lèi)的構(gòu)造函數(shù)為默認(rèn)的構(gòu)造函數(shù),采用Class.newInstance()則是比較好的選擇,一句代碼就OK;如果是調(diào)用帶參構(gòu)造函數(shù)、私有構(gòu)造函數(shù),就需要采用Constractor.newInstance(),兩種情況視使用情況而定。不過(guò)Java Totorial中推薦采用Constractor.newInstance()。
華清遠(yuǎn)見(jiàn)90+項(xiàng)目獲批!教育部2021最新協(xié)同育人項(xiàng)目名
華清遠(yuǎn)見(jiàn)榮獲2021騰訊教育“年度口碑影響力職業(yè)教育品
華清遠(yuǎn)見(jiàn)受邀參加2021年武漢民辦高校信息學(xué)科合作聯(lián)盟
溫暖同行共創(chuàng)佳績(jī) 2019華清遠(yuǎn)見(jiàn)北京總部年會(huì)大曝光
助力高校AI人工智能學(xué)科建設(shè) 華清遠(yuǎn)見(jiàn)人工智能師資班
華清遠(yuǎn)見(jiàn)受邀參加四川省物聯(lián)網(wǎng)年會(huì),榮獲優(yōu)秀企業(yè)專(zhuān)家