在文章JAVA設計模式-動態代理(Proxy)示例及說明和JAVA設計模式-動態代理(Proxy)源碼分析都提到了反射這個概念。html
// 經過反射機制,通知力宏作事情 method.invoke(object, args);
// 經過反射,將h做爲參數,實例化代理類,返回代理類實例。 return cons.newInstance(new Object[]{h});
並且在java
// 將接口類對象數組clone一份。 final Class<?>[] intfs = interfaces.clone();
也提到一個類對象數組的概念,若是你不知道反射,不知道類對象,那麼你在閱讀者兩篇文章的時候,極可能就會雨裏霧裏,不知所然。經過這篇文章你就能很輕鬆的掌握類對象和反射。面試
反射是java語言中一個很基礎,很簡單的知識,不只僅是在工做中會用到,並且也會常常出如今面試中。若是你認真閱讀本文,對你在技術層面來講又是一個提高。設計模式
一,前言數組
反射是什麼呢?實際上是java程序語言的一種機制,我理解爲是java的後門。在其餘文章中給出的定義和解釋都比較晦澀難懂,不如先看一下具體的代碼,再去理解,這樣就容易不少。ide
爲了更好的理解反射機制,不得不提到類對象的概念,爲了避免與類和對象的概念搞混,咱們就首先看一下類和對象的概念。相信你已經很是熟悉類和對象的概念了,那麼我就簡單的描述一下類與對象的概念:源碼分析
類:一個或一組事物的抽象描述,例如狗是對狗狗這一組事物的抽象描述。post
對象:具體的某一個事物,例如哈士奇等,也能夠說是類的一個實例。this
那麼類對象是什麼呢?spa
在java中一切皆對象。既然一切皆對象,固然類也是一種對象,那麼類的對象類型是什麼呢?是java.lang.Class,你也許在其餘的地方見到過。
那麼類對象是從哪裏來的,怎麼建立的呢?
咱們都知道,想要獲得一個類的對象,最基本的方法就是經過new關鍵字,去實例化一個對象。但類對象是一個特殊的對象,天然不能使用new關鍵字,翻看Class類的源碼就能夠證實:
/* * 私有化構造方法,只有java 虛擬機才能建立類對象 * Private constructor. Only the Java Virtual Machine creates Class objects. * This constructor is not used and prevents the default constructor being * generated. */ private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
Class類中只有這一個私有的構造方法。其實類對象是java虛擬機(JVM)在加載class文件的時候自動在虛擬機內存中給咱們建立的。
這裏就涉及到了另一個機制:類加載機制。
簡單描述一下類加載機制:就是虛擬機將class文件加載到虛擬機內存中,最終造成能夠被虛擬機直接使用的java類型。虛擬機會將class文件中的信息按照所需的存儲格式放在方法區中,同時會在內存中(HotSpot是在堆內存中)實例化一個java.lang.Class類對象。
類加載機制是一個很複雜的過程,不是本篇文章的重點,就不展開來講。至少到這裏咱們已經知道了,類對象是由虛擬機建立的並且HotSpot虛擬機將類對象存放在堆內存中。
那麼怎麼經過類對象來實現反射呢?爲了理解方便,來舉一個有趣的例子
二,一個有趣的例子
有一天一個同事養了一隻狗狗哈士奇,在做者面前大肆炫耀,說他的狗狗多麼萌,多麼威武,多麼聽話......,做者聽完心生嚮往,提出去看一看。可是這個同事高傲的擡起頭說了三個字:想的美。
居然不給我看!!!做者一氣之下,就走了java程序的後門-反射。你不讓我看,我恰恰要看。
哈士奇類的定義:
package com.zcz.reflecttest; public class HaShiQi implements Dog { public String color = "黑色"; private String name = "富貴"; //私有化構造 private HaShiQi() {}; @Override public void eat() { // TODO Auto-generated method stub System.out.println(name + " 去吃狗糧"); } @Override public void run() { // TODO Auto-generated method stub System.out.println(name + " 去跑着玩兒"); } private void dance() { System.out.println(name + " 來跳一支舞"); } @Override public String toString() { // TODO Auto-generated method stub return "名字:"+name+";顏色:"+color; } }
能夠看到同時的哈士奇實現了Dog接口:
package com.zcz.reflecttest; public interface Dog { public void eat(); public void run(); }
從代碼中能夠看到,個人同事不只僅沒告訴做者他的哈士奇居然會跳舞,甚至連哈士奇的名字都不想讓做者知道,並且連構造器都私有化了。
那麼接下來,走後門開始。在上提到類對象,正好我想經過反射看狗狗,也須要用到類對象。那麼接下來第一步就先獲取HaShiQi的類對象。
三,類對象的獲取
超級簡單:
package com.zcz.reflecttest; /** * 經過反射查看同事狗狗的信息 * @author zhangchengzi * */ public class LookLook { public static void main(String[] args) { // TODO Auto-generated method stub //獲取類對象,除了這份方法外還有另外兩種方法 Class clazz = HaShiQi.class; } }
從這裏開始咱們的反射的使用的開始了,先看看HaShiQi類中有哪些屬性吧?
四,獲取屬性
代碼:
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub //獲取類對象,除了這份方法外還有另外兩種方法 Class clazz = HaShiQi.class; System.out.println("——————— 獲取全部公有的屬性 —————————"); Field[] fields = clazz.getFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } }
打印結果:
——————— 獲取全部公有的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color
能夠看到只有一個color屬性,使用getFields方法是獲取不到私有屬性了,想要獲取私有屬性就要使用下方的方法:
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub //獲取類對象,除了這份方法外還有另外兩種方法 Class clazz = HaShiQi.class; System.out.println("——————— 獲取全部公有的屬性 —————————"); Field[] fields = clazz.getFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部的屬性 —————————"); fields = clazz.getDeclaredFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } }
打印結果:
——————— 獲取全部公有的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color ——————— 獲取全部的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color private java.lang.String com.zcz.reflecttest.HaShiQi.name
能夠看到,HaShiQi類中私有(private修飾的)的name屬性打印出來了。
五,獲取方法
代碼:
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub //獲取類對象,除了這份方法外還有另外兩種方法 Class clazz = HaShiQi.class; System.out.println("——————— 獲取全部公有的屬性 —————————"); Field[] fields = clazz.getFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部的屬性 —————————"); fields = clazz.getDeclaredFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部公有的方法 —————————"); Method[] methods = clazz.getMethods(); for(Method method : methods) { System.out.println(method); } }
打印結果:
——————— 獲取全部公有的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color ——————— 獲取全部的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color private java.lang.String com.zcz.reflecttest.HaShiQi.name ——————— 獲取全部公有的方法 ————————— public void com.zcz.reflecttest.HaShiQi.run() //重寫的toString 方法 public java.lang.String com.zcz.reflecttest.HaShiQi.toString() public void com.zcz.reflecttest.HaShiQi.eat() //Object類中的方法 public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
從結果中能夠發現,getMethod方法也是隻能獲取到公有的方法,並且連Object中的方法也獲取到了。跟獲取屬性也是同樣的,也有方法能夠獲取到HaShiQi類中全部的方法:
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub //獲取類對象,除了這份方法外還有另外兩種方法 Class clazz = HaShiQi.class; System.out.println("——————— 獲取全部公有的屬性 —————————"); Field[] fields = clazz.getFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部的屬性 —————————"); fields = clazz.getDeclaredFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部公有的方法 —————————"); Method[] methods = clazz.getMethods(); for(Method method : methods) { System.out.println(method); } System.out.println("——————— 獲取全部類中聲明的方法 —————————"); methods = clazz.getDeclaredMethods(); for(Method method : methods) { System.out.println(method); } }
打印結果:
——————— 獲取全部公有的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color ——————— 獲取全部的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color private java.lang.String com.zcz.reflecttest.HaShiQi.name ——————— 獲取全部公有的方法 ————————— public void com.zcz.reflecttest.HaShiQi.run() public java.lang.String com.zcz.reflecttest.HaShiQi.toString() public void com.zcz.reflecttest.HaShiQi.eat() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() ——————— 獲取全部類中聲明的方法 ————————— public void com.zcz.reflecttest.HaShiQi.run() public java.lang.String com.zcz.reflecttest.HaShiQi.toString() private void com.zcz.reflecttest.HaShiQi.dance() public void com.zcz.reflecttest.HaShiQi.eat()
這樣就能夠同時獲取到HaShiQi類中的私有方法(private修飾的)dance了。
六,獲取構造器
代碼:
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub //獲取類對象,除了這份方法外還有另外兩種方法 Class clazz = HaShiQi.class; System.out.println("——————— 獲取全部公有的屬性 —————————"); Field[] fields = clazz.getFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部的屬性 —————————"); fields = clazz.getDeclaredFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部公有的方法 —————————"); Method[] methods = clazz.getMethods(); for(Method method : methods) { System.out.println(method); } System.out.println("——————— 獲取全部類中聲明的方法 —————————"); methods = clazz.getDeclaredMethods(); for(Method method : methods) { System.out.println(method); } System.out.println("——————— 獲取全部公有的構造器 —————————"); Constructor[] constructors = clazz.getConstructors(); for(Constructor constructor : constructors) { System.out.println(constructor); } }
打印結果:
——————— 獲取全部公有的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color ——————— 獲取全部的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color private java.lang.String com.zcz.reflecttest.HaShiQi.name ——————— 獲取全部公有的方法 ————————— public void com.zcz.reflecttest.HaShiQi.run() public java.lang.String com.zcz.reflecttest.HaShiQi.toString() public void com.zcz.reflecttest.HaShiQi.eat() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() ——————— 獲取全部類中聲明的方法 ————————— public void com.zcz.reflecttest.HaShiQi.run() public java.lang.String com.zcz.reflecttest.HaShiQi.toString() private void com.zcz.reflecttest.HaShiQi.dance() public void com.zcz.reflecttest.HaShiQi.eat() ——————— 獲取全部公有的構造器 —————————
由於HaShiQi類中沒有公有的構造器,因此這裏什麼都沒有打印出來。天然也有獲取到私有構造器的方法:
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub //獲取類對象,除了這份方法外還有另外兩種方法 Class clazz = HaShiQi.class; System.out.println("——————— 獲取全部公有的屬性 —————————"); Field[] fields = clazz.getFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部的屬性 —————————"); fields = clazz.getDeclaredFields(); for(Field field : fields) { field.setAccessible(true); System.out.println(field); } System.out.println("——————— 獲取全部公有的方法 —————————"); Method[] methods = clazz.getMethods(); for(Method method : methods) { System.out.println(method); } System.out.println("——————— 獲取全部類中聲明的方法 —————————"); methods = clazz.getDeclaredMethods(); for(Method method : methods) { System.out.println(method); } System.out.println("——————— 獲取全部公有的構造器 —————————"); Constructor[] constructors = clazz.getConstructors(); for(Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("——————— 獲取全部的構造器 —————————"); constructors = clazz.getDeclaredConstructors(); for(Constructor constructor : constructors) { System.out.println(constructor); } }
打印結果:
——————— 獲取全部公有的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color ——————— 獲取全部的屬性 ————————— public java.lang.String com.zcz.reflecttest.HaShiQi.color private java.lang.String com.zcz.reflecttest.HaShiQi.name ——————— 獲取全部公有的方法 ————————— public void com.zcz.reflecttest.HaShiQi.run() public java.lang.String com.zcz.reflecttest.HaShiQi.toString() public void com.zcz.reflecttest.HaShiQi.eat() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() ——————— 獲取全部類中聲明的方法 ————————— public void com.zcz.reflecttest.HaShiQi.run() public java.lang.String com.zcz.reflecttest.HaShiQi.toString() private void com.zcz.reflecttest.HaShiQi.dance() public void com.zcz.reflecttest.HaShiQi.eat() ——————— 獲取全部公有的構造器 ————————— ——————— 獲取全部的構造器 ————————— private com.zcz.reflecttest.HaShiQi()
七,實例化對象
HaSHiQi類中的信息,包括屬性,方法,構造器,咱們都已經經過反射瀏覽了一遍,那麼接下來就要再使用反射實例化HaShiQi類的對象了,由於只有實例對象才能調用屬性和方法。
由於HaShiQi類中只顯示聲明瞭一個空參構造器,因此咱們只能使用這個構造器來實例化對象。
正常狀況下獲取指定參數的構造器,須要使用方法clazz.getConstructor(parameterTypes(參數類對象數組))。可是HaShiQi的構造方法是私有的,因此使用這個方法去獲取構造器會報錯:
Constructor cons = clazz.getConstructor();
Exception in thread "main" java.lang.NoSuchMethodException: com.zcz.reflecttest.HaShiQi.<init>() at java.lang.Class.getConstructor0(Class.java:3082) at java.lang.Class.getConstructor(Class.java:1825) at com.zcz.reflecttest.LookLook.main(LookLook.java:61)
因此咱們使用另一個方法解決這個問題:
// 實例化對象 // 獲取構造器 Constructor con = clazz.getDeclaredConstructor(); // 強制設置爲能夠訪問 con.setAccessible(true); HaShiQi haShiQi = (HaShiQi)con.newInstance(); System.out.println("沒有作任何修改前:"+haShiQi.toString());
打印結果:
沒有作任何修改前:名字:富貴;顏色:黑色
哈哈,機智如我,怎麼能被這種問題打到,從代碼中能夠看到咱們使用了構造器的newInstance方法實例化了一個HaShiQi對象。
代碼:con.newInstance();
是否是很熟悉?,在文章JAVA設計模式-動態代理(Proxy)源碼分析中,實例化代理類對象的時候,也使用到了:
// 經過反射,將h做爲參數,實例化代理類,返回代理類實例。 return cons.newInstance(new Object[]{h});
接下來就是訪問對象的屬性和方法了若是咱們把全部的屬性和方法都訪問到,不就把同事的狗狗看了一遍了嗎?。
八,修改屬性
// 修改狗狗的顏色 // 獲取狗狗的color屬性 Field filed = clazz.getField("color"); filed.set(haShiQi, "紅色"); System.out.println("修改狗狗的顏色:"+haShiQi.toString());
打印結果:
沒有作任何修改前:名字:富貴;顏色:黑色
修改狗狗的顏色:名字:富貴;顏色:紅色
修改爲功,接着修改狗狗的名字:
// 修改狗狗的顏色 // 獲取狗狗的color屬性 Field filed = clazz.getField("color"); filed.set(haShiQi, "紅色"); System.out.println("修改狗狗的顏色:"+haShiQi.toString()); // 修改狗狗的名字 filed = clazz.getDeclaredField("name"); // 強制設置爲能夠訪問 filed.setAccessible(true); filed.set(haShiQi, "驚喜"); System.out.println("修改狗狗的名字:"+haShiQi.toString());
打印結果:
沒有作任何修改前:名字:富貴;顏色:黑色
修改狗狗的顏色:名字:富貴;顏色:紅色
修改狗狗的名字:名字:驚喜;顏色:紅色
修改爲功,可是千萬要注意是的是的getDeclaredField方法,而不是getField方法,使用getField方法會拋出異常:Exception in thread "main" java.lang.NoSuchFieldException: name;
同時filed.setAccessible(true);也是必不可少了,不然將拋出異常:Exception in thread "main" java.lang.IllegalAccessException: Class com.zcz.reflecttest.LookLook can not access a member of class com.zcz.reflecttest.HaShiQi with modifiers "private"
屬性修改完,咱們就開始調用方法吧。
九,調用方法
//調用run方法 Method method = clazz.getMethod("run"); method.invoke(haShiQi, null);
打印結果:
驚喜 去跑着玩兒
調用成功;
代碼:method.invoke(haShiQi, null);
是否是以爲這句代碼也是很熟悉的?在文章JAVA設計模式-動態代理(Proxy)示例及說明中也有相同的用法:
// 經過反射機制,通知力宏作事情 method.invoke(object, args);
咱們繼續,eat方法和run方法同樣,都是公有的方法,這裏就再也不演示了,接下來演示一下私有的dance方法:
//調用run方法 Method method = clazz.getMethod("run"); method.invoke(haShiQi, null); //調用dance方法 method = clazz.getDeclaredMethod("dance"); method.setAccessible(true); method.invoke(haShiQi, null);
打印結果:
驚喜 去跑着玩兒
驚喜 來跳一支舞
也一樣成功了,可是也要注意getDeclaredMethod方法和method.setAccessible(true);
到這裏,同事不讓我看的狗狗,我經過java的反射機制從裏到外的所有都看了一遍,不只僅是看了一遍,我還給他的狗狗改了顏色和名字。調用了全部的方法。是否是很神奇,頗有成就感?
十,總結
反射的基本用法已經都在上面的實例中演示到了,可是反射仍是有其餘的方法在這裏並無提到,有機會的話繼續補充。
在上面的代碼過程當中,你也許也發現了,不只僅是有類對象,並且還有屬性,方法和構造器都是對象,屬性是java.lang.reflect.Field類的對象,方法是java.lang.reflect.Method類的對象,構造器是java.lang.reflect.Constructor類的對象。看來在java中確實是一切皆對象。
好了,來看看其餘文章中對反射的解釋吧:
Java 反射機制是在運行狀態中,對於任意一個類,都可以得到這個類的全部屬性和方法,對於任意一個對象都可以調用它的任意一個屬性和方法。這種在運行時動態的獲取信息以及動態調用對象的方法的功能稱爲 Java 的反射機制。
在上面的例子中,咱們是否是在運行中,獲取了HaShiQi的屬性和方法(包括私有的),是否是調用了每個屬性和方法(包括私有的)。
這樣一來,反射機制是否是好理解一點兒了呢,但願能幫到你。
原創不易,轉載請註明出處:http://www.javashuo.com/article/p-vcxhllpw-a.html