Java反射机制大神必学系列之 ,高级与低级的差别在哪里?
Java反射機(jī)制大神必學(xué)系列之 ,高級(jí)與低級(jí)的差別在哪里? java學(xué)習(xí)愛好者 2019-05-20 19:08 前言 今天介紹下Java的反射機(jī)制,以前我們獲取一個(gè)類的實(shí)例都是使用new一個(gè)實(shí)例出來。那樣太low了,今天跟我一起來學(xué)習(xí)學(xué)習(xí)一種更加高大上的方式來實(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語言的反射機(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ì)象,即通過。 動(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è)軟件肯定是沒有多少人用的。采用靜態(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)建和表示:
Code code1 = new Code(); 上面說了,所有的類都是Class的對(duì)象,那么如何表示呢,可不可以通過如下方式呢:
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ì)象,但是卻可以通過已有的類得到一個(gè)Class對(duì)象,共有三種方式,如下:
Class c1 = Code.class; 這說明任何一個(gè)類都有一個(gè)隱含的靜態(tài)成員變量class,這種方式是通過獲取類的靜態(tài)成員變量class得到的 Class c2 = code1.getClass(); code1是Code的一個(gè)對(duì)象,這種方式是通過一個(gè)類的對(duì)象的getClass()方法獲得的 Class c3 = Class.forName("com.trigl.reflect.Code"); 這種方法是Class類調(diào)用forName方法,通過一個(gè)類的全量限定名獲得 這里,c1、c2、c3都是Class的對(duì)象,他們是完全一樣的,而且有個(gè)學(xué)名,叫做Code的類類型(class type)。
這里就讓人奇怪了,前面不是說Code是Class的對(duì)象嗎,而c1、c2、c3也是Class的對(duì)象,那么Code和c1、c2、c3不就一樣了嗎?為什么還叫Code什么類類型?這里不要糾結(jié)于它們是否相同,只要理解類類型是干什么的就好了,顧名思義,類類型就是類的類型,也就是描述一個(gè)類是什么,都有哪些東西,所以我們可以通過類類型知道一個(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.ReflectDemo Java反射相關(guān)操作
前面我們知道了怎么獲取Class,那么我們可以通過這個(gè)Class干什么呢?
總結(jié)如下:
獲取成員方法Method 獲取成員變量Field 獲取構(gòu)造函數(shù)Constructor 下面來具體介紹
獲取成員方法信息
單獨(dú)獲取某一個(gè)方法是通過Class類的以下方法獲得的:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到該類所有的方法,不包括父類的 public Method getMethod(String name, Class<?>... parameterTypes) // 得到該類所有的public方法,包括父類的 兩個(gè)參數(shù)分別是方法名和方法參數(shù)類的類類型列表。
例如類A有如下一個(gè)方法:
public void fun(String name,int age) { System.out.println("我叫"+name+",今年"+age+"歲"); } 現(xiàn)在知道A有一個(gè)對(duì)象a,那么就可以通過:
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); //通過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í)候我們想獲取類中所有成員方法的信息,要怎么辦。可以通過以下幾步來實(shí)現(xiàn):
1.獲取所有方法的數(shù)組:
Class c = Class.forName("com.tengj.reflect.Person"); Method[] methods = c.getDeclaredMethods(); // 得到該類所有的方法,不包括父類的 或者: Method[] methods = c.getMethods();// 得到該類所有的public方法,包括父類的 2.然后循環(huán)這個(gè)數(shù)組就得到每個(gè)方法了:
for (Method method : methods) 完整代碼如下:
person類跟上面一樣,這里以及后面就不貼出來了,只貼關(guān)鍵代碼
public class ReflectDemo { public static void main(String[] args){ try { Class c = Class.forName("com.tengj.reflect.Person"); Method[] methods = c.getDeclaredMethods(); for(Method m:methods){ String methodName= m.getName(); System.out.println(methodName); } } catch (Exception e) { e.printStackTrace(); } } } 執(zhí)行結(jié)果:
getName setName setAge fun fun getAge 這里如果把c.getDeclaredMethods();改成c.getMethods();執(zhí)行結(jié)果如下,多了很多方法,以為把Object里面的方法也打印出來了,因?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ì)象,所以我們通過java.lang.reflect.Field里面封裝的方法來獲取這些信息。
單獨(dú)獲取某個(gè)成員變量,通過Class類的以下方法實(shí)現(xiàn):
public Field getDeclaredField(String name) // 獲得該類自身聲明的所有變量,不包括其父類的變量 public Field getField(String name) // 獲得該類自所有的public成員變量,包括其父類變量 參數(shù)是成員變量的名字。
例如一個(gè)類A有如下成員變量:
private int n; 如果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è)置是否允許訪問,因?yàn)樵撟兞渴莗rivate的,所以要手動(dòng)設(shè)置允許訪問,如果msg是public的就不需要這行了。 Object msg = field.get(o); System.out.println(msg); } catch (Exception e) { e.printStackTrace(); } } } 執(zhí)行結(jié)果:
hello wrold 同樣,如果想要獲取所有成員變量的信息,可以通過以下幾步
1.獲取所有成員變量的數(shù)組:
Field[] fields = c.getDeclaredFields(); 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ì)象,所以我們通過java.lang.reflect.Constructor里面封裝的方法來獲取這些信息。
單獨(dú)獲取某個(gè)構(gòu)造函數(shù),通過Class類的以下方法實(shí)現(xiàn):
public Constructor getDeclaredConstructor(Class<?>... parameterTypes) // 獲得該類所有的構(gòu)造器,不包括其父類的構(gòu)造器 public Constructor getConstructor(Class<?>... parameterTypes) // 獲得該類所以public構(gòu)造器,包括父類 這個(gè)參數(shù)為構(gòu)造函數(shù)參數(shù)類的類類型列表。
例如類A有如下一個(gè)構(gòu)造函數(shù):
public A(String a, int b) { // code body } 那么就可以通過:
Constructor constructor = a.getDeclaredConstructor(String.class, int.class); 來獲取這個(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è)置是否允許訪問,因?yàn)樵摌?gòu)造器是private的,所以要手動(dòng)設(shè)置允許訪問,如果構(gòu)造器是public的就不需要這行了。 constructor.newInstance("tengj"); } catch (Exception e) { e.printStackTrace(); } } } 執(zhí)行結(jié)果:
tengj 注意:Class的newInstance方法,只能創(chuàng)建只包含無參數(shù)的構(gòu)造函數(shù)的類,如果某類只有帶參數(shù)的構(gòu)造函數(shù),那么就要使用另外一種方式:fromClass.getDeclaredConstructor(String.class).newInstance("tengj");
獲取所有的構(gòu)造函數(shù),可以通過以下步驟實(shí)現(xiàn):
1.獲取該類的所有構(gòu)造函數(shù),放在一個(gè)數(shù)組中:
Constructor[] constructors = c.getDeclaredConstructors(); 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) 通過反射了解集合泛型的本質(zhì)
首先下結(jié)論:
Java中集合的泛型,是防止錯(cuò)誤輸入的,只在編譯階段有效,繞過編譯到了運(yùn)行期就無效了。
下面通過一個(gè)實(shí)例來驗(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(); // 沒有泛型 List list2 = new ArrayList(); // 有泛型 /
- 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.然后通過反射添加元素方式,在運(yùn)行期動(dòng)態(tài)加載類,首先得到list1和list2
- 的類類型相同,然后再通過方法反射繞過編譯器來調(diào)用add方法,看能否插入int
- 型的元素 / Class c1 = list1.getClass(); Class c2 = list2.getClass(); System.out.println(c1 == c2); // 結(jié)果:true,說明類類型完全相同 // 驗(yàn)證:我們可以通過方法的反射來給list2添加元素,這樣可以繞過編譯檢查 try { Method m = c2.getMethod("add", Object.class); // 通過方法反射得到add方法 m.invoke(list2, 20); // 給list2添加一個(gè)int型的,上面顯示在編譯器是會(huì)報(bào)錯(cuò)的 System.out.println("list2的長(zhǎng)度是:" + list2.size()); // 結(jié)果:2,說明list2長(zhǎng)度增加了,并沒有泛型檢查 } 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í)候,底層原理是通過Java反射來實(shí)現(xiàn)的,希望這篇筆記也對(duì)你有用。
參考 Java反射機(jī)制深入詳解
Java反射入門
Java反射機(jī)制
java反射詳解
Java 反射機(jī)制淺析
反射機(jī)制的理解及其用途
整理的思維導(dǎo)圖 個(gè)人整理的Java反射機(jī)制的思維導(dǎo)圖,導(dǎo)出的圖片無法查看備注的一些信息,所以需要源文件的童鞋可以關(guān)注我個(gè)人主頁上的公眾號(hào),回復(fù)反射機(jī)制即可獲取源文件。
一直覺得自己寫的不是技術(shù),而是情懷,一篇篇文章是自己這一路走來的痕跡。靠專業(yè)技能的成功是最具可復(fù)制性的,希望我的這條路能讓你少走彎路,希望我能幫你抹去知識(shí)的蒙塵,希望我能幫你理清知識(shí)的脈絡(luò),希望未來技術(shù)之巔上有你也有我。
QQ群號(hào)碼:786947691
群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個(gè)知識(shí)點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時(shí)間來學(xué)習(xí)提升自己,不要再用"沒有時(shí)間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個(gè)交代!
總結(jié)
以上是生活随笔為你收集整理的Java反射机制大神必学系列之 ,高级与低级的差别在哪里?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5.16 学习记录
- 下一篇: 每天三分钟玩转Git(已完结)