摘要:Java反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
本文分享自華爲雲社區《java知識點問題精選之反射》,原文做者:breakDraw 。html
Java反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。java
反射就是把java類中的各類成分映射成一個個的Java對象。segmentfault
例如:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術能夠對一個類進行解剖,把個個組成部分映射成一個個對象。數組
(其實:一個類中這些成員方法、構造方法、在加入類中都有一個類來描述)緩存
反射
Q: 調用類對象.class 和 forName(類名)的區別?安全
Class<A> classA = A.class; Class<A> classA = Class.forName("A");
A: 僅使用.class不能進行第一次靜態初始化, forname函數則能夠多線程
例如B是A的基類,下面這段代碼如何?
假設有父子2個類,以下:jvm
static class Parent { } static class Son extends Parent{}
Q: 用instanceof 能夠和父類比較嗎,且會返回true嗎?函數
Son son = new Son(); if (son instanceof Parent) { System.out.println("a instanof B"); }
A: 能夠比較,且返回true。性能
Q: 用getClass並用== 能夠和父類比較嗎,且會返回true嗎,下面這樣:
注意A是B的子類。
Son son = new Son(); if (son.getClass() == Parent.class){ System.out.println("son class == Parent.class"); }
A: 不能夠,編譯就會報錯了。和Class<泛型>的 ==號比較有關。
由於getClass返回的是<? extends Son>, .class返回的是Class<Parent>
Q: 用getClass並用.equals能夠和父類比較嗎,且會返回true嗎,下面這樣:
Son son = new Son(); if (son.getClass().equals(Parent.class)){ System.out.println("son class.equals(Parent.class)"); }
A: 能夠比較,正常編譯, 可是會返回false,即不相等!
Q: getDeclaredXXX 有哪幾種?
A: 5種:
- 註解Annotation
- 內部類Classed
- 構造方法Construcotor
- 字段Field
- 方法Method
Q:getMethods()返回哪些方法, getDeclaredMethods()會返回哪些方法?
A:
getMethods()返回 本類、父類、父接口 的public方法
getDeclaredMethods()只 返回本類的 全部 方法
其餘getXXX和getDeclaredXXX的區別同理。
拿到Filed、Method、Constructor以後咋用
- Method能夠invoke(object, args)
- Constructor能夠newInstance(Object…)來作構造調用。
- Filed能夠用get(object)、set(object)來設置屬性值。
Q: 反射拿到Method對象後, 該對象.getModifiers() 是幹嗎的?
A: 返回該方法的修飾符,而且是1個整數。
Q:
下面這段代碼會發生什麼?
package com.huawei.test public class A { public A(int i ) { System.out.printf("i=" +i); } public static void main(String[] args) { try { A a = (A)Class.forName("com.huawei.test.A").newInstance(); } catch (ClassNotFoundException e) { System.out.printf("ClassNotFoundException"); } catch (InstantiationException e) { System.out.printf("InstantiationException"); } catch (IllegalAccessException e) { System.out.printf("IllegalAccessException"); } } }
A:
打印InstantiationException初始化錯誤。由於A沒有默認構造器了,因此不能夠用newInstance來構造。應該改爲這樣,經過獲取正確的構造器來進行構造。
A a = (A)Class.forName("A").getConstructor(int.class).newInstance(123);
Q:如何提升反射的效率?
A:
- 使用高性能反射包,例如ReflectASM
- 緩存反射的對象,避免每次都要重複去字節碼中獲取。(緩存!緩存!)
- method反射可設置method.setAccessible(true)來關閉安全檢查。
- 儘可能不要getMethods()後再遍歷篩選,而直接用getMethod(methodName)來根據方法名獲取方法
- 利用hotspot虛擬機中的反射優化技術(jit技術)
參考資料: https://segmentfault.com/q/1010000003004720
https://www.cnblogs.com/coding-night/p/10772631.html
Q:
用反射獲取到的method對象, 是返回一個method引用,仍是返回1個拷貝的method對象?
A:
反射拿method對象時, 會作一次拷貝,而不是直接返回引用,所以最好對頻繁使用的同一個method作緩存,而不是每次都去查找。
Q:
getMethods()後本身作遍歷獲取方法,和getMethod(methodName) 直接獲取方法, 爲何性能會有差別?
A:
getMethods() 返回method數組時,每一個method都作了一次拷貝。 getMethod(methodName)只會返回那個方法的拷貝, 性能的差別就體如今拷貝上。
Q:
獲取方法時,jvm內部其實有緩存,可是返回給外部時依然會作拷貝。那麼該method的緩存是持久存在的嗎?
A:
不是持久存在的,內存不足時會被回收。源碼以下:
private Class.ReflectionData<T> reflectionData() { SoftReference<Class.ReflectionData<T>> reflectionData = this.reflectionData; int classRedefinedCount = this.classRedefinedCount; Class.ReflectionData rd; return reflectionData != null && (rd = (Class.ReflectionData)reflectionData.get()) != null && rd.redefinedCount == classRedefinedCount ? rd : this.newReflectionData(reflectionData, classRedefinedCount); }
能夠看到這是一個軟引用。
軟引用的定義:內存緊張時可能會被回收,不過也能夠經過-XX:SoftRefLRUPolicyMSPerMB參數控制回收的時機,只要發生GC就會將其回收。
若是reflectionData被回收以後,又執行了反射方法,那隻能經過newReflectionData方法從新建立一個這樣的對象了。
Q: 反射是線程安全的嗎?
A:
是線程安全的。 獲取反射的數據時,經過cas去獲取。 cas概念能夠見多線程一節。
Q:
a普通方法調用
b反射方法調用
c關閉安全檢查的反射方法調用,性能差別以下:
b反射方法調用和c關閉安全檢查的反射方法調用的性能差別在哪?普通方法調用和關閉安全檢查的反射方法調用的性能差別在哪?
A:
- 安全檢查的性能消耗在於
,SecurityManager.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); 這項檢測須要運行時申請RuntimePermission(「accessDeclaredMembers」)。 因此若是不考慮安全檢查, 對反射方法調用invoke時, 應當設置 Method#setAccessible(true) - 普通方法和反射方法的性能差別在於
- Method#invoke 方法會對參數作封裝和解封操做
- 須要檢查方法可見性
- 須要校驗參數
- 反射方法難之內聯
- JIT 沒法優化