Java基础与提高干货系列——Java反射机制
前言
今天介紹下Java的反射機(jī)制,以前我們獲取一個(gè)類的實(shí)例都是使用new一個(gè)實(shí)例出來(lái)。那樣太low了,今天跟我一起來(lái)學(xué)習(xí)學(xué)習(xí)一種更加高大上的方式來(lái)實(shí)現(xiàn)。
正文
Java反射機(jī)制定義
Java反射機(jī)制是指在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語(yǔ)言的反射機(jī)制。
用一句話總結(jié)就是反射可以實(shí)現(xiàn)在運(yùn)行時(shí)可以知道任意一個(gè)類的屬性和方法。
反射機(jī)制的優(yōu)點(diǎn)與缺點(diǎn)
為什么要用反射機(jī)制?直接創(chuàng)建對(duì)象不就可以了嗎,這就涉及到了動(dòng)態(tài)與靜態(tài)的概念
- 靜態(tài)編譯:在編譯時(shí)確定類型,綁定對(duì)象,即通過(guò)。
-
動(dòng)態(tài)編譯:運(yùn)行時(shí)確定類型,綁定對(duì)象。動(dòng)態(tài)編譯最大限度發(fā)揮了java的靈活性,體現(xiàn)了多態(tài)的應(yīng)用,有以降低類之間的藕合性。
-
優(yōu)點(diǎn)
可以實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建對(duì)象和編譯,體現(xiàn)出很大的靈活性,特別是在J2EE的開發(fā)中它的靈活性就表現(xiàn)的十分明顯。比如,一個(gè)大型的軟件,不可能一次就把把它設(shè)計(jì)的很完美,當(dāng)這個(gè)程序編譯后,發(fā)布了,當(dāng)發(fā)現(xiàn)需要更新某些功能時(shí),我們不可能要用戶把以前的卸載,再重新安裝新的版本,假如這樣的話,這個(gè)軟件肯定是沒(méi)有多少人用的。采用靜態(tài)的話,需要把整個(gè)程序重新編譯一次才可以實(shí)現(xiàn)功能的更新,而采用反射機(jī)制的話,它就可以不用卸載,只需要在運(yùn)行時(shí)才動(dòng)態(tài)的創(chuàng)建和編譯,就可以實(shí)現(xiàn)該功能。 - 缺點(diǎn)
對(duì)性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類操作總是慢于只直接執(zhí)行相同的操作。
理解Class類和類類型
想要了解反射首先理解一下Class類,它是反射實(shí)現(xiàn)的基礎(chǔ)。
類是java.lang.Class類的實(shí)例對(duì)象,而Class是所有類的類(There is a class named Class)
對(duì)于普通的對(duì)象,我們一般都會(huì)這樣創(chuàng)建和表示:
上面說(shuō)了,所有的類都是Class的對(duì)象,那么如何表示呢,可不可以通過(guò)如下方式呢:
Class c = new Class();但是我們查看Class的源碼時(shí),是這樣寫的:
private Class(ClassLoader loader) { classLoader = loader; }可以看到構(gòu)造器是私有的,只有JVM可以創(chuàng)建Class的對(duì)象,因此不可以像普通類一樣new一個(gè)Class對(duì)象,雖然我們不能new一個(gè)Class對(duì)象,但是卻可以通過(guò)已有的類得到一個(gè)Class對(duì)象,共有三種方式,如下:
Class c1 = Code.class; 這說(shuō)明任何一個(gè)類都有一個(gè)隱含的靜態(tài)成員變量class,這種方式是通過(guò)獲取類的靜態(tài)成員變量class得到的 Class c2 = code1.getClass(); code1是Code的一個(gè)對(duì)象,這種方式是通過(guò)一個(gè)類的對(duì)象的getClass()方法獲得的 Class c3 = Class.forName("com.trigl.reflect.Code"); 這種方法是Class類調(diào)用forName方法,通過(guò)一個(gè)類的全量限定名獲得這里,c1、c2、c3都是Class的對(duì)象,他們是完全一樣的,而且有個(gè)學(xué)名,叫做Code的類類型(class type)。
這里就讓人奇怪了,前面不是說(shuō)Code是Class的對(duì)象嗎,而c1、c2、c3也是Class的對(duì)象,那么Code和c1、c2、c3不就一樣了嗎?為什么還叫Code什么類類型?這里不要糾結(jié)于它們是否相同,只要理解類類型是干什么的就好了,顧名思義,類類型就是類的類型,也就是描述一個(gè)類是什么,都有哪些東西,所以我們可以通過(guò)類類型知道一個(gè)類的屬性和方法,并且可以調(diào)用一個(gè)類的屬性和方法,這就是反射的基礎(chǔ)。
舉個(gè)簡(jiǎn)單例子代碼:
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException {//第一種:Class c1 = Code.class;Class class1=ReflectDemo.class;System.out.println(class1.getName());//第二種:Class c2 = code1.getClass();ReflectDemo demo2= new ReflectDemo();Class c2 = demo2.getClass();System.out.println(c2.getName());//第三種:Class c3 = Class.forName("com.trigl.reflect.Code");Class class3 = Class.forName("com.tengj.reflect.ReflectDemo");System.out.println(class3.getName());} }執(zhí)行結(jié)果:
com.tengj.reflect.ReflectDemo com.tengj.reflect.ReflectDemo com.tengj.reflect.ReflectDemoJava反射相關(guān)操作
前面我們知道了怎么獲取Class,那么我們可以通過(guò)這個(gè)Class干什么呢?
總結(jié)如下:
- 獲取成員方法Method
- 獲取成員變量Field
- 獲取構(gòu)造函數(shù)Constructor
下面來(lái)具體介紹
獲取成員方法信息
單獨(dú)獲取某一個(gè)方法是通過(guò)Class類的以下方法獲得的:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到該類所有的方法,不包括父類的 public Method getMethod(String name, Class<?>... parameterTypes) // 得到該類所有的public方法,包括父類的兩個(gè)參數(shù)分別是方法名和方法參數(shù)類的類類型列表。
例如類A有如下一個(gè)方法:
現(xiàn)在知道A有一個(gè)對(duì)象a,那么就可以通過(guò):
Class c = Class.forName("com.tengj.reflect.Person"); //先生成class Object o = c.newInstance(); //newInstance可以初始化一個(gè)實(shí)例 Method method = c.getMethod("fun", String.class, int.class);//獲取方法 method.invoke(o, "tengj", 10); //通過(guò)invoke調(diào)用該方法,參數(shù)第一個(gè)為實(shí)例對(duì)象,后面為具體參數(shù)值完整代碼如下:
public class Person {private String name;private int age;private String msg="hello wrold";public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Person() {}private Person(String name) {this.name = name;System.out.println(name);}public void fun() {System.out.println("fun");}public void fun(String name,int age) {System.out.println("我叫"+name+",今年"+age+"歲");} }public class ReflectDemo {public static void main(String[] args){try {Class c = Class.forName("com.tengj.reflect.Person");Object o = c.newInstance();Method method = c.getMethod("fun", String.class, int.class);method.invoke(o, "tengj", 10);} catch (Exception e) {e.printStackTrace();}} }執(zhí)行結(jié)果:
我叫tengj,今年10歲怎樣,是不是感覺很厲害,我們只要知道這個(gè)類的路徑全稱就能玩弄它于鼓掌之間。
有時(shí)候我們想獲取類中所有成員方法的信息,要怎么辦。可以通過(guò)以下幾步來(lái)實(shí)現(xiàn):
1.獲取所有方法的數(shù)組:
2.然后循環(huán)這個(gè)數(shù)組就得到每個(gè)方法了:
for (Method method : methods)完整代碼如下:
person類跟上面一樣,這里以及后面就不貼出來(lái)了,只貼關(guān)鍵代碼
執(zhí)行結(jié)果:
getName setName setAge fun fun getAge這里如果把c.getDeclaredMethods();改成c.getMethods();執(zhí)行結(jié)果如下,多了很多方法,以為把Object里面的方法也打印出來(lái)了,因?yàn)镺bject是所有類的父類:
getName setName getAge setAge fun fun wait wait wait equals toString hashCode getClass notify notifyAll獲取成員變量信息
想一想成員變量中都包括什么:成員變量類型+成員變量名
類的成員變量也是一個(gè)對(duì)象,它是java.lang.reflect.Field的一個(gè)對(duì)象,所以我們通過(guò)java.lang.reflect.Field里面封裝的方法來(lái)獲取這些信息。
單獨(dú)獲取某個(gè)成員變量,通過(guò)Class類的以下方法實(shí)現(xiàn):
public Field getDeclaredField(String name) // 獲得該類自身聲明的所有變量,不包括其父類的變量 public Field getField(String name) // 獲得該類自所有的public成員變量,包括其父類變量參數(shù)是成員變量的名字。
例如一個(gè)類A有如下成員變量:
如果A有一個(gè)對(duì)象a,那么就可以這樣得到其成員變量:
Class c = a.getClass(); Field field = c.getDeclaredField("n");完整代碼如下:
public class ReflectDemo {public static void main(String[] args){try {Class c = Class.forName("com.tengj.reflect.Person");//獲取成員變量Field field = c.getDeclaredField("msg"); //因?yàn)閙sg變量是private的,所以不能用getField方法Object o = c.newInstance();field.setAccessible(true);//設(shè)置是否允許訪問(wèn),因?yàn)樵撟兞渴莗rivate的,所以要手動(dòng)設(shè)置允許訪問(wèn),如果msg是public的就不需要這行了。Object msg = field.get(o);System.out.println(msg);} catch (Exception e) {e.printStackTrace();}} }執(zhí)行結(jié)果:
hello wrold同樣,如果想要獲取所有成員變量的信息,可以通過(guò)以下幾步
1.獲取所有成員變量的數(shù)組:
2.遍歷變量數(shù)組,獲得某個(gè)成員變量field
for (Field field : fields)完整代碼:
public class ReflectDemo {public static void main(String[] args){try {Class c = Class.forName("com.tengj.reflect.Person");Field[] fields = c.getDeclaredFields();for(Field field :fields){System.out.println(field.getName());}} catch (Exception e) {e.printStackTrace();}} }執(zhí)行結(jié)果:
name age msg獲取構(gòu)造函數(shù)
最后再想一想構(gòu)造函數(shù)中都包括什么:構(gòu)造函數(shù)參數(shù)
同上,類的成構(gòu)造函數(shù)也是一個(gè)對(duì)象,它是java.lang.reflect.Constructor的一個(gè)對(duì)象,所以我們通過(guò)java.lang.reflect.Constructor里面封裝的方法來(lái)獲取這些信息。
單獨(dú)獲取某個(gè)構(gòu)造函數(shù),通過(guò)Class類的以下方法實(shí)現(xiàn):
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) // 獲得該類所有的構(gòu)造器,不包括其父類的構(gòu)造器 public Constructor<T> getConstructor(Class<?>... parameterTypes) // 獲得該類所以public構(gòu)造器,包括父類這個(gè)參數(shù)為構(gòu)造函數(shù)參數(shù)類的類類型列表。
例如類A有如下一個(gè)構(gòu)造函數(shù):
那么就可以通過(guò):
Constructor constructor = a.getDeclaredConstructor(String.class, int.class);來(lái)獲取這個(gè)構(gòu)造函數(shù)。
完整代碼:
public class ReflectDemo {public static void main(String[] args){try {Class c = Class.forName("com.tengj.reflect.Person");//獲取構(gòu)造函數(shù)Constructor constructor = c.getDeclaredConstructor(String.class);constructor.setAccessible(true);//設(shè)置是否允許訪問(wèn),因?yàn)樵摌?gòu)造器是private的,所以要手動(dòng)設(shè)置允許訪問(wèn),如果構(gòu)造器是public的就不需要這行了。constructor.newInstance("tengj");} catch (Exception e) {e.printStackTrace();}} }執(zhí)行結(jié)果:
tengj注意:Class的newInstance方法,只能創(chuàng)建只包含無(wú)參數(shù)的構(gòu)造函數(shù)的類,如果某類只有帶參數(shù)的構(gòu)造函數(shù),那么就要使用另外一種方式:fromClass.getDeclaredConstructor(String.class).newInstance("tengj");
獲取所有的構(gòu)造函數(shù),可以通過(guò)以下步驟實(shí)現(xiàn):
1.獲取該類的所有構(gòu)造函數(shù),放在一個(gè)數(shù)組中:
2.遍歷構(gòu)造函數(shù)數(shù)組,獲得某個(gè)構(gòu)造函數(shù)constructor:
for (Constructor constructor : constructors)完整代碼:
public class ReflectDemo {public static void main(String[] args){Constructor[] constructors = c.getDeclaredConstructors();for(Constructor constructor:constructors){System.out.println(constructor);}} catch (Exception e) {e.printStackTrace();}} }執(zhí)行結(jié)果:
public com.tengj.reflect.Person() public com.tengj.reflect.Person(java.lang.String)通過(guò)反射了解集合泛型的本質(zhì)
首先下結(jié)論:
Java中集合的泛型,是防止錯(cuò)誤輸入的,只在編譯階段有效,繞過(guò)編譯到了運(yùn)行期就無(wú)效了。
下面通過(guò)一個(gè)實(shí)例來(lái)驗(yàn)證:
/*** 集合泛型的本質(zhì)* @description* @author Trigl* @date 2016年4月2日上午2:54:11*/ public class GenericEssence {public static void main(String[] args) {List list1 = new ArrayList(); // 沒(méi)有泛型 List<String> list2 = new ArrayList<String>(); // 有泛型/** 1.首先觀察正常添加元素方式,在編譯器檢查泛型,* 這個(gè)時(shí)候如果list2添加int類型會(huì)報(bào)錯(cuò)*/list2.add("hello"); // list2.add(20); // 報(bào)錯(cuò)!list2有泛型限制,只能添加String,添加int報(bào)錯(cuò)System.out.println("list2的長(zhǎng)度是:" + list2.size()); // 此時(shí)list2長(zhǎng)度為1/** 2.然后通過(guò)反射添加元素方式,在運(yùn)行期動(dòng)態(tài)加載類,首先得到list1和list2* 的類類型相同,然后再通過(guò)方法反射繞過(guò)編譯器來(lái)調(diào)用add方法,看能否插入int* 型的元素*/Class c1 = list1.getClass();Class c2 = list2.getClass();System.out.println(c1 == c2); // 結(jié)果:true,說(shuō)明類類型完全相同// 驗(yàn)證:我們可以通過(guò)方法的反射來(lái)給list2添加元素,這樣可以繞過(guò)編譯檢查try {Method m = c2.getMethod("add", Object.class); // 通過(guò)方法反射得到add方法m.invoke(list2, 20); // 給list2添加一個(gè)int型的,上面顯示在編譯器是會(huì)報(bào)錯(cuò)的System.out.println("list2的長(zhǎng)度是:" + list2.size()); // 結(jié)果:2,說(shuō)明list2長(zhǎng)度增加了,并沒(méi)有泛型檢查} catch (Exception e) {e.printStackTrace();}/** 綜上可以看出,在編譯器的時(shí)候,泛型會(huì)限制集合內(nèi)元素類型保持一致,但是編譯器結(jié)束進(jìn)入* 運(yùn)行期以后,泛型就不再起作用了,即使是不同類型的元素也可以插入集合。*/} }執(zhí)行結(jié)果:
list2的長(zhǎng)度是:1 true list2的長(zhǎng)度是:2總結(jié)
到此,Java反射機(jī)制入門的差不多了,我是復(fù)習(xí)SpringMVC里面IOC/DI的時(shí)候,底層原理是通過(guò)Java反射來(lái)實(shí)現(xiàn)的,希望這篇筆記也對(duì)你有用。
參考
Java反射機(jī)制深入詳解
Java反射入門
Java反射機(jī)制
java反射詳解
Java 反射機(jī)制淺析
反射機(jī)制的理解及其用途
整理的思維導(dǎo)圖
個(gè)人整理的Java反射機(jī)制的思維導(dǎo)圖,導(dǎo)出的圖片無(wú)法查看備注的一些信息,所以需要源文件的童鞋可以關(guān)注我個(gè)人主頁(yè)上的公眾號(hào),回復(fù)反射機(jī)制即可獲取源文件。
一直覺得自己寫的不是技術(shù),而是情懷,一篇篇文章是自己這一路走來(lái)的痕跡。靠專業(yè)技能的成功是最具可復(fù)制性的,希望我的這條路能讓你少走彎路,希望我能幫你抹去知識(shí)的蒙塵,希望我能幫你理清知識(shí)的脈絡(luò),希望未來(lái)技術(shù)之巔上有你也有我。
文/嘟嘟MD(簡(jiǎn)書作者)
原文鏈接:http://www.jianshu.com/p/1a60d55a94cd
總結(jié)
以上是生活随笔為你收集整理的Java基础与提高干货系列——Java反射机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 公共技术点之 Java 反射 Refle
- 下一篇: Java 动态代理机制分析及扩展,第 1