有關(guān)Java反射的問(wèn)題有哪些

這篇文章主要講解了“有關(guān)Java反射的問(wèn)題有哪些”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“有關(guān)Java反射的問(wèn)題有哪些”吧!

目前創(chuàng)新互聯(lián)建站已為成百上千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)站空間網(wǎng)站運(yùn)營(yíng)、企業(yè)網(wǎng)站設(shè)計(jì)、西城網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

反射可以修改final類(lèi)型成員變量嗎?

final我們應(yīng)該都知道,修飾變量的時(shí)候代表是一個(gè)常量,不可修改。那利用反射能不能達(dá)到修改的效果呢?

我們先試著修改一個(gè)用final修飾的String變量。

public class User {     private final String name = "Bob";     private final Student student = new Student();          public String getName() {         return name;     }      public Student getStudent() {         return student;     } }       User user = new User();     Class clz = User.class;     Field field1 = null;     try{         field1=clz.getDeclaredField("name");         field1.setAccessible(true);         field1.set(user,"xixi");         System.out.println(user.getName());     }catch(NoSuchFieldException e){         e.printStackTrace();     }catch(IllegalAccessException e){         e.printStackTrace();     }

打印出來(lái)的結(jié)果,還是Bob,也就是沒(méi)有修改到。

我們?cè)傩薷南聅tudent變量試試:

field1 = clz.getDeclaredField("student"); field1.setAccessible(true); field1.set(user, new Student());  打印: 修改前com.example.studynote.reflection.Student@77459877 修改后com.example.studynote.reflection.Student@72ea2f77

可以看到,對(duì)于正常的對(duì)象變量即使被final修飾也是可以通過(guò)反射進(jìn)行修改的。

這是為什么呢?為什么String不能被修改,而普通的對(duì)象變量可以被修改呢?

先說(shuō)結(jié)論,其實(shí)String值也被修改了,只是我們無(wú)法通過(guò)這個(gè)對(duì)象獲取到修改后的值。

這就涉及到JVM的內(nèi)聯(lián)優(yōu)化了:

內(nèi)聯(lián)函數(shù),編譯器將指定的函數(shù)體插入并取代每一處調(diào)用該函數(shù)的地方(上下文),從而節(jié)省了每次調(diào)用函數(shù)帶來(lái)的額外時(shí)間開(kāi)支。

簡(jiǎn)單的說(shuō),就是JVM在處理代碼的時(shí)候會(huì)幫我們優(yōu)化代碼邏輯,比如上述的final變量,已知final修飾后不會(huì)被修改,所以獲取這個(gè)變量的時(shí)候就直接幫你在編譯階段就給賦值了。

所以上述的getName方法經(jīng)過(guò)JVM編譯內(nèi)聯(lián)優(yōu)化后會(huì)變成:

public String getName() {        return "Bob";    }

所以無(wú)論怎么修改,都獲取不到修改后的值。

有的朋友可能提出直接獲取name呢?比如這樣:

//修改為public public final String name = "Bob";  //反射修改后,打印user.name field1=clz.getDeclaredField("name"); field1.setAccessible(true); field1.set(user,"xixi"); System.out.println(user.name);

不好意思,還是打印出來(lái)Bob。這是因?yàn)镾ystem.out.println(user.name)這一句在經(jīng)過(guò)編譯后,會(huì)被寫(xiě)成:

System.out.println(user.name)  //經(jīng)過(guò)內(nèi)聯(lián)優(yōu)化  System.out.println("Bob")

所以:

「反射是可以修改final變量的,但是如果是基本數(shù)據(jù)類(lèi)型或者String類(lèi)型的時(shí)候,無(wú)法通過(guò)對(duì)象獲取修改后的值,因?yàn)镴VM對(duì)其進(jìn)行了內(nèi)聯(lián)優(yōu)化?!?/p>

那有沒(méi)有辦法獲取修改后的值呢?

有,可以通過(guò)反射中的Field.get(Object obj)方法獲?。?/p>

//獲取field對(duì)應(yīng)的變量在user對(duì)象中的值 System.out.println("修改后"+field.get(user));

反射獲取static靜態(tài)變量

說(shuō)完了final,再說(shuō)說(shuō)static,怎么修改static修飾的變量呢?

我們知道,靜態(tài)變量是在類(lèi)的實(shí)例化之前就進(jìn)行了初始化(類(lèi)的初始化階段),所以靜態(tài)變量是跟著類(lèi)本身走的,跟具體的對(duì)象無(wú)關(guān),所以我們獲取變量就不需要傳入對(duì)象,直接傳入null即可:

public class User {  public static String name; }   field2 = clz.getDeclaredField("name"); field2.setAccessible(true); //獲取靜態(tài)變量 Object getname=field2.get(null); System.out.println("修改前"+getname);  //修改靜態(tài)變量 field2.set(null, "xixi"); System.out.println("修改后"+User.name);

如上述代碼:

  • Field.get(null) 可以獲取靜態(tài)變量。

  • Field.set(null,object) 可以修改靜態(tài)變量。

怎么提升反射效率

1、緩存重復(fù)用到的對(duì)象

利用緩存,其實(shí)我不說(shuō)大家也都知道,在平時(shí)項(xiàng)目中用到多次的對(duì)象也會(huì)進(jìn)行緩存,誰(shuí)也不會(huì)多次去創(chuàng)建。

但是,這一點(diǎn)在反射中尤為重要,比如Class.forName方法,我們做個(gè)測(cè)試:

long startTime = System.currentTimeMillis();     Class clz = Class.forName("com.example.studynote.reflection.User");     User user;     int i = 0;     while (i < 1000000) {         i++;         //方法1,直接實(shí)例化         user = new User();         //方法2,每次都通過(guò)反射獲取class,然后實(shí)例化         user = (User) Class.forName("com.example.studynote.reflection.User").newInstance();         //方法3,通過(guò)之前反射得到的class進(jìn)行實(shí)例化         user = (User) clz.newInstance();     }      System.out.println("耗時(shí):" + (System.currentTimeMillis() - startTime));

打印結(jié)果:

1、直接實(shí)例化 耗時(shí):15  2、每次都通過(guò)反射獲取class,然后實(shí)例化 耗時(shí):671  3、通過(guò)之前反射得到的class進(jìn)行實(shí)例化 耗時(shí):31

所以看出來(lái),只要我們合理的運(yùn)用這些反射方法,比如Class.forName,Constructor,Method,F(xiàn)ield等,盡量在循環(huán)外就緩存好實(shí)例,就能提高反射的效率,減少耗時(shí)。

2、setAccessible(true)

之前我們說(shuō)過(guò)當(dāng)遇到私有變量和方法的時(shí)候,會(huì)用到setAccessible(true)方法關(guān)閉安全檢查。這個(gè)安全檢查其實(shí)也是耗時(shí)的。

所以我們?cè)诜瓷涞倪^(guò)程中可以盡量調(diào)用setAccessible(true)來(lái)關(guān)閉安全檢查,無(wú)論是否是私有的,這樣也能提高反射的效率。

3、ReflectASM

ReflectASM 是一個(gè)非常小的 Java 類(lèi)庫(kù),通過(guò)代碼生成來(lái)提供高性能的反射處理,自動(dòng)為 get/set  字段提供訪問(wèn)類(lèi),訪問(wèn)類(lèi)使用字節(jié)碼操作而不是 Java 的反射技術(shù),因此非常快。

ASM是一個(gè)通用的Java字節(jié)碼操作和分析框架。它可以用于修改現(xiàn)有類(lèi)或直接以二進(jìn)制形式動(dòng)態(tài)生成類(lèi)。

簡(jiǎn)單的說(shuō),這是一個(gè)類(lèi)似反射,但是不同于反射的高性能庫(kù)。他的原理是通過(guò)ASM庫(kù),生成了一個(gè)新的類(lèi),然后相當(dāng)于直接調(diào)用新的類(lèi)方法,從而完成反射的功能。

感興趣的可以去看看源碼,實(shí)現(xiàn)原理比較簡(jiǎn)單&mdash;&mdash;https://github.com/EsotericSoftware/reflectasm。

「小總結(jié):」經(jīng)過(guò)上述三種方法,我想反射也不會(huì)那么可怕到大大影響性能的程度了,如果真的發(fā)現(xiàn)反射影響了性能以及實(shí)際使用的情況,也許可以研究下,是否是因?yàn)闆](méi)用對(duì)反射和沒(méi)有處理好反射相關(guān)的緩存呢?

反射原理

如果我們?cè)囍榭催@些反射方法的源碼,會(huì)發(fā)現(xiàn)最終都會(huì)走到native方法中,比如

getDeclaredField方法會(huì)走到

public native Field getDeclaredField(String name) throws NoSuchFieldException;

那么在底層,是怎么獲取到類(lèi)的相關(guān)信息的呢?

首先回顧下JVM加載Java文件的過(guò)程:

  • 編譯階段,.java文件會(huì)被編譯成.class文件,.class文件是一種二進(jìn)制文件,內(nèi)容是JVM能夠識(shí)別的機(jī)器碼。

  • .class文件里面依次存儲(chǔ)著類(lèi)文件的各種信息,比如:版本號(hào)、類(lèi)的名字、字段的描述和描述符、方法名稱和描述、是不是public、類(lèi)索引、字段表集合,方法集合等等數(shù)據(jù)。

  • 然后,JVM中的類(lèi)加載器會(huì)讀取字節(jié)碼文件,取出二進(jìn)制數(shù)據(jù),加載到內(nèi)存中,并且解析.class文件的信息。

  • 類(lèi)加載器會(huì)獲取類(lèi)的二進(jìn)制字節(jié)流,在內(nèi)存中生成代表這個(gè)類(lèi)的java.lang.Class對(duì)象。

  • 最后會(huì)開(kāi)始類(lèi)的生命周期,比如連接、初始化等等。

而反射,就是去操作這個(gè) java.lang.Class對(duì)象,這個(gè)對(duì)象中有整個(gè)類(lèi)的結(jié)構(gòu),包括屬性方法等等。

總結(jié)來(lái)說(shuō)就是,.class是一種有順序的結(jié)構(gòu)文件,而Class對(duì)象就是對(duì)這種文件的一種表示,所以我們能從Class對(duì)象中獲取關(guān)于類(lèi)的所有信息,這就是反射的原理。

感謝各位的閱讀,以上就是“有關(guān)Java反射的問(wèn)題有哪些”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)有關(guān)Java反射的問(wèn)題有哪些這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

當(dāng)前文章:有關(guān)Java反射的問(wèn)題有哪些
網(wǎng)站URL:http://bm7419.com/article2/jdciic.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機(jī)企業(yè)網(wǎng)站制作、、搜索引擎優(yōu)化、動(dòng)態(tài)網(wǎng)站、靜態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司