中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

有關Java反射的問題有哪些

發布時間:2021-10-20 10:07:59 來源:億速云 閱讀:173 作者:iii 欄目:編程語言

這篇文章主要講解了“有關Java反射的問題有哪些”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“有關Java反射的問題有哪些”吧!

反射可以修改final類型成員變量嗎?

final我們應該都知道,修飾變量的時候代表是一個常量,不可修改。那利用反射能不能達到修改的效果呢?

我們先試著修改一個用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();     }

打印出來的結果,還是Bob,也就是沒有修改到。

我們再修改下student變量試試:

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

可以看到,對于正常的對象變量即使被final修飾也是可以通過反射進行修改的。

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

先說結論,其實String值也被修改了,只是我們無法通過這個對象獲取到修改后的值。

這就涉及到JVM的內聯優化了:

內聯函數,編譯器將指定的函數體插入并取代每一處調用該函數的地方(上下文),從而節省了每次調用函數帶來的額外時間開支。

簡單的說,就是JVM在處理代碼的時候會幫我們優化代碼邏輯,比如上述的final變量,已知final修飾后不會被修改,所以獲取這個變量的時候就直接幫你在編譯階段就給賦值了。

所以上述的getName方法經過JVM編譯內聯優化后會變成:

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

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

有的朋友可能提出直接獲取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);

不好意思,還是打印出來Bob。這是因為System.out.println(user.name)這一句在經過編譯后,會被寫成:

System.out.println(user.name)  //經過內聯優化  System.out.println("Bob")

所以:

「反射是可以修改final變量的,但是如果是基本數據類型或者String類型的時候,無法通過對象獲取修改后的值,因為JVM對其進行了內聯優化。」

那有沒有辦法獲取修改后的值呢?

有,可以通過反射中的Field.get(Object obj)方法獲取:

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

反射獲取static靜態變量

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

我們知道,靜態變量是在類的實例化之前就進行了初始化(類的初始化階段),所以靜態變量是跟著類本身走的,跟具體的對象無關,所以我們獲取變量就不需要傳入對象,直接傳入null即可:

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

如上述代碼:

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

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

怎么提升反射效率

1、緩存重復用到的對象

利用緩存,其實我不說大家也都知道,在平時項目中用到多次的對象也會進行緩存,誰也不會多次去創建。

但是,這一點在反射中尤為重要,比如Class.forName方法,我們做個測試:

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

打印結果:

1、直接實例化 耗時:15  2、每次都通過反射獲取class,然后實例化 耗時:671  3、通過之前反射得到的class進行實例化 耗時:31

所以看出來,只要我們合理的運用這些反射方法,比如Class.forName,Constructor,Method,Field等,盡量在循環外就緩存好實例,就能提高反射的效率,減少耗時。

2、setAccessible(true)

之前我們說過當遇到私有變量和方法的時候,會用到setAccessible(true)方法關閉安全檢查。這個安全檢查其實也是耗時的。

所以我們在反射的過程中可以盡量調用setAccessible(true)來關閉安全檢查,無論是否是私有的,這樣也能提高反射的效率。

3、ReflectASM

ReflectASM 是一個非常小的 Java 類庫,通過代碼生成來提供高性能的反射處理,自動為 get/set  字段提供訪問類,訪問類使用字節碼操作而不是 Java 的反射技術,因此非常快。

ASM是一個通用的Java字節碼操作和分析框架。它可以用于修改現有類或直接以二進制形式動態生成類。

簡單的說,這是一個類似反射,但是不同于反射的高性能庫。他的原理是通過ASM庫,生成了一個新的類,然后相當于直接調用新的類方法,從而完成反射的功能。

感興趣的可以去看看源碼,實現原理比較簡單&mdash;&mdash;https://github.com/EsotericSoftware/reflectasm。

「小總結:」經過上述三種方法,我想反射也不會那么可怕到大大影響性能的程度了,如果真的發現反射影響了性能以及實際使用的情況,也許可以研究下,是否是因為沒用對反射和沒有處理好反射相關的緩存呢?

反射原理

如果我們試著查看這些反射方法的源碼,會發現最終都會走到native方法中,比如

getDeclaredField方法會走到

public native Field getDeclaredField(String name) throws NoSuchFieldException;

那么在底層,是怎么獲取到類的相關信息的呢?

首先回顧下JVM加載Java文件的過程:

  • 編譯階段,.java文件會被編譯成.class文件,.class文件是一種二進制文件,內容是JVM能夠識別的機器碼。

  • .class文件里面依次存儲著類文件的各種信息,比如:版本號、類的名字、字段的描述和描述符、方法名稱和描述、是不是public、類索引、字段表集合,方法集合等等數據。

  • 然后,JVM中的類加載器會讀取字節碼文件,取出二進制數據,加載到內存中,并且解析.class文件的信息。

  • 類加載器會獲取類的二進制字節流,在內存中生成代表這個類的java.lang.Class對象。

  • 最后會開始類的生命周期,比如連接、初始化等等。

而反射,就是去操作這個 java.lang.Class對象,這個對象中有整個類的結構,包括屬性方法等等。

總結來說就是,.class是一種有順序的結構文件,而Class對象就是對這種文件的一種表示,所以我們能從Class對象中獲取關于類的所有信息,這就是反射的原理。

感謝各位的閱讀,以上就是“有關Java反射的問題有哪些”的內容了,經過本文的學習后,相信大家對有關Java反射的問題有哪些這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

敦化市| 紫阳县| 西林县| 丽江市| 惠来县| 奉新县| 乐昌市| 洪泽县| 夏河县| 东阿县| 叶城县| 嘉鱼县| 吉木乃县| 黑山县| 郴州市| 和静县| 迁西县| 克山县| 辽阳县| 阜城县| 卓资县| 三江| 永清县| 台湾省| 江门市| 晋江市| 铜鼓县| 库尔勒市| 嘉峪关市| 修文县| 铜陵市| 武清区| 琼海市| 行唐县| 宜春市| 门源| 桑植县| 铁力市| 类乌齐县| 奉贤区| 怀远县|