java學習中如(ru)何認識反(fan)射,業內專家告訴你
時間:2018-06-22 來(lai)源:未知
反射的概念
反射的概(gai)念(nian)是由(you) Smith 在(zai)1982年首(shou)次提出的,主要是指程序可(ke)以訪問(wen)、檢(jian)測和修(xiu)改(gai)它(ta)本身狀態或行為的一種能力。
換句話說,就是能夠得到(dao)代(dai)碼(ma)自(zi)身的特征。
換句(ju)話說,就(jiu)是(shi)把類(lei)(lei)本身也(ye)看成是(shi)對象,包(bao)括類(lei)(lei)中的(de)變量名、方(fang)法名、內部類(lei)(lei)、超類(lei)(lei)、包(bao)、修飾符(fu)等(deng)等(deng),都可以(yi)通過代碼(ma)來(lai)得到并被(bei)看成是(shi)對象。
java為此設計了(le)一些類來方(fang)便我們使用反射。這些類并不多,它們分別是:Field、Constructor、Method、Class、Object,下面對(dui)這些類做一個(ge)簡(jian)單的說(shuo)明。
摘抄于其它(ta)資料,僅供閱讀
Field 類:提供有關類或(huo)接口的(de)屬性的(de)信息(xi),以及對它的(de)動(dong)態訪問(wen)權限。反射的(de)字段可能是一個類(靜態)屬性或(huo)實(shi)例屬性,簡單(dan)的(de)理解可以把它看成(cheng)一個封裝反射類的(de)屬性的(de)類。
Constructor 類:提供關于(yu)類的(de)單個(ge)構造(zao)方法(fa)的(de)信息以及(ji)對它(ta)的(de)訪(fang)問(wen)權限。這個(ge)類和 Field 類不(bu)同,Field 類封(feng)裝了反射類的(de)屬性,而 Constructor 類則封(feng)裝了反射類的(de)構造(zao)方法(fa)。
Method 類:提供(gong)關(guan)于類或(huo)接(jie)口上某個單獨方法(fa)(fa)(fa)的(de)信息。所反映的(de)方法(fa)(fa)(fa)可能是類方法(fa)(fa)(fa)或(huo)實例方法(fa)(fa)(fa)(包括抽象方法(fa)(fa)(fa))。 這(zhe)個類不難理解(jie),它是用(yong)來封裝反射類方法(fa)(fa)(fa)的(de)一個類。
Class 類:類的(de)(de)實例表示(shi)正在(zai)運行的(de)(de) Java 應(ying)用程序中的(de)(de)類和接口。枚舉是一種類,注釋(shi)是一種接口。每個數(shu)組(zu)屬于被映射為 Class 對(dui)(dui)象的(de)(de)一個類,所有(you)具有(you)相同元素類型和維數(shu)的(de)(de)數(shu)組(zu)都共(gong)享該(gai) Class 對(dui)(dui)象。
Object 類:每個類都(dou)使用 Object 作為超類。所有對象(包括數組)都(dou)實現這個類的方法。
獲取Class類(lei)
有(you)一個(ge)(ge)類(lei),類(lei)名是(shi)Class,(首字母大寫,不同于關鍵字class)。任何一個(ge)(ge)java類(lei)都是(shi)這個(ge)(ge)Class類(lei)的(de)對(dui)象(xiang),即“類(lei)本身也是(shi)對(dui)象(xiang)”的(de)感覺(jue)。
一(yi)旦我們(men)獲(huo)取到了一(yi)個類(lei)(lei)的(de)Class實例,那么在此(ci)基礎(chu)上要獲(huo)取Field、Constructor、Method等(deng)等(deng)的(de)話也就很容易了(因(yin)此(ci)java的(de)所有代碼都在類(lei)(lei)中的(de)嘛)。所以(yi)首要步驟是獲(huo)取Class實例。
獲(huo)取(qu)類自身(shen)有三種方式(shi):
(1)利(li)用 對(dui)象.getClass() 的方式(shi)獲取該對(dui)象的Class實例;
(2)利用 對象.class 的方式(shi)來獲(huo)取(qu)Class實例,對于基本數據(ju)類(lei)型的封裝類(lei),還可以采用.TYPE來獲(huo)取(qu)相對應的基本數據(ju)類(lei)型的Class實例;
(3)使用(yong) Class類的靜(jing)態(tai)方法forName(“全路徑名”),用(yong)類的名字獲取一個(ge)Class實例(li)。
示例
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");//法(fa)三
System.out.println(cls1 == cls2);
System.out.println(cls1 == cls3);
}
}
運行結果為
true
true
解釋
1、運行(xing)結果(guo)為true說明虛擬機為某個(ge)(ge)類只會產生一份字(zi)節(jie)(jie)碼,將來用這(zhe)份字(zi)節(jie)(jie)碼可(ke)以產生多(duo)個(ge)(ge)實(shi)例對象。
2、也即是(shi)(shi)說,在運行期間(jian),如果我(wo)們(men)要產生某個類(lei)的對象(xiang),Java虛擬機(ji)(JVM)會檢(jian)查該類(lei)型(xing)的Class對象(xiang)是(shi)(shi)否已被(bei)加(jia)載。如果沒有(you)(you)被(bei)加(jia)載,JVM會根(gen)據類(lei)的名稱找到.class文件并(bing)加(jia)載它。一旦某個類(lei)型(xing)的Class對象(xiang)已被(bei)加(jia)載到內存,就可以(yi)用它來(lai)產生該類(lei)型(xing)的所有(you)(you)對象(xiang)。
利用Class實例創建(jian)對象
以前我(wo)(wo)們創建(jian)(jian)對象(xiang)都是用“new 類名(ming)()”的方式,現在(zai)我(wo)(wo)們先得到構(gou)造(zao)方法,并用構(gou)造(zao)方法來創建(jian)(jian)。現在(zai)我(wo)(wo)們要使用Consturctor(構(gou)造(zao)器)類:它代表(biao)某個類中的一個構(gou)造(zao)方法。
得到某個類所有的(de)構造(zao)方法
Constructor [] constructors = Class.forName("java.lang.String").getConstructors();
得(de)到某一(yi)個構造方法
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
注:參數是一個Class實例,即去拿匹配這樣參數的構造方法。
創建(jian)實例對(dui)象,用Constructor的newInstance方(fang)法
傳統方式:String str=new String(new StringBuffer("abc"));
反射方式:String str=(String) constructor.newInstance(new StringBuffer("abc"));
注(zhu):newInstance()方(fang)法參數可變長,請嘗(chang)試放多個參數。不(bu)合(he)適時(shi),報(bao)異(yi)常IllegalArgumentException。
上述原理可(ke)以(yi)下面示例來演練
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);
}
}
利用(yong)Constructor來(lai)創建(jian)實例與(yu)利用(yong)Class類來(lai)創建(jian)實例
class類(lei)也有創建(jian)實例的方法(fa),下面的例子進(jin)行(xing)了(le)展示。
此(ci)例來(lai)源(yuan)于//seahb.iteye.com/blog/855107。
import java.lang.reflect.Constructor;
class A {
private A() { // 將(jiang)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()調用私有構造(zao)函數(shu):");
b.byClassNewInstance();
System.out.println("通過Constructor.newInstance()調用私有(you)構造(zao)函(han)數:");
b.byConstructorNewInstance();
}
/* 法一:通過Class.NewInstance()創(chuang)建新的類示例 */
private void byClassNewInstance() {
try {
Class c = Class.forName("A");
A a = (A) c.newInstance();//調用無(wu)參(can)構造方法。如果方法是(shi)私有的,則運行時會異常IllegalAccessException
} catch (Exception e) {
e.printStackTrace();
System.out.println("通過Class.NewInstance()調用構造(zao)方(fang)法【失(shi)敗】");
}
}
/*法二:通過Constructor.newInstance()創建新的類示例 */
private void byConstructorNewInstance() {
try {
Class c = Class.forName("A");
Constructor c0 = c.getDeclaredConstructor();/* 調用無參構造方法 */
c0.setAccessible(true); //必須(xu)設置(zhi)一下可見性后就可調(diao)用(yong)了
A a0 = (A) c0.newInstance();//調(diao)用構造方法(fa)
System.out.println("成功(gong)1");
Constructor c1 = c.getDeclaredConstructor(new Class[] { int.class, int.class });/* 調用帶(dai)參構造方法 */
c1.setAccessible(true);
//A a1 = (A) c1.newInstance(new Object[] { 5, 6 });//參(can)數(shu)是對象數(shu)組(zu)
A a1 = (A) c1.newInstance(5, 6);//參數可連(lian)寫,因為newInstance()支(zhi)持可變參數
//A a1 = (A) c1.newInstance(5, 6,7);//參數(shu)若不合適(shi),則就報異常(chang)IllegalArgumentException
System.out.println("成功(gong)2");
} catch (Exception e) {
e.printStackTrace();
}
}
}
結論
class.newInstance和constructor.newInstance 區(qu)別
通過(guo)反(fan)射創建新的類示例,有兩(liang)種方式:
Class.newInstance()
Constructor.newInstance()
Class.newInstance() 只(zhi)能夠調用(yong)無參的(de)構造函(han)數(shu),即默認(ren)的(de)構造函(han)數(shu);
Constructor.newInstance() 可以根據傳入的參(can)數,調用任意(yi)構造(zao)(zao)構造(zao)(zao)函(han)數。
Class.newInstance() 要求被調用(yong)的(de)(de)構(gou)造函數(shu)是(shi)可見(jian)的(de)(de),也即必須是(shi)public類(lei)型的(de)(de);
Constructor.newInstance() 在特定的情(qing)況下,可以調用私有的構造函數。
如(ru)果(guo)被調(diao)用(yong)的(de)類的(de)構造(zao)(zao)函(han)數(shu)為默認的(de)構造(zao)(zao)函(han)數(shu),采(cai)用(yong)Class.newInstance()則是比較好的(de)選擇(ze),一句代碼就(jiu)OK;如(ru)果(guo)是調(diao)用(yong)帶參(can)構造(zao)(zao)函(han)數(shu)、私有構造(zao)(zao)函(han)數(shu),就(jiu)需(xu)要(yao)采(cai)用(yong)Constractor.newInstance(),兩種情況視使用(yong)情況而定(ding)。不過Java Totorial中推(tui)薦采(cai)用(yong)Constractor.newInstance()。

