深入理解Java反射

2021-02-19 銅板街技術

題外話

最近公司創建了技術部的公眾號用來鼓勵大家進行分享,很多同學比較糾結,覺得找不到比較適合聊的 topic。總的來說大概兩個原因:一個是覺得太基礎講出來比較 low 沒有人會關注,另一個是講一些很牛的新技術又怕出錯;然而每一項技術在自己的應用中都會有你自己獨特的視角,也許這一點正是別人關心的。我個人認為分享一些我們在編碼中經常會碰到,而大多數人可能知其然而不知其所以然的話題是很有意義的,今天我打算分享下我們 Java 中一個經常用到的工具,反射 和 動態代理

當我們在 IDE 中編寫代碼的時候,打一個點號,IDE 會自動彈出對應的屬性和方法名。當我們在 debug 的時候,IDE 會將方法運行時方法內局部變量和外部實例上屬性的值都展示出來,spring 中的 IOC 和 AOP,以及一個 RPC 框架中,我們反序列化,consumer 的代理,以及 provider 的調用都會用到 Java 的反射功能,有人說使用反射會慢,那麼到底慢在哪裡呢?


反射

反射使 Java 語言有了動態編譯的功能,也就是在我們編碼的時候不需要知道對象的具體類型,但是在運行期可以通過 Class.forName() 獲取一個類的 class 對象,在通過newInstance 獲取實例。


先看下 java.lang.reflect 包下的幾個主要類的關係圖,當然動態代理的工具類也在該包下。

圖 1

作為頂級接口,這個接口提供了獲取註解相關的功能,我們在方法,類,屬性,構造方法上都可以加註解,所以下面的 Field,Method,Constructor 都有實現這個接口,以下是我們經常用的兩個方法,jdk8 以後接口裡面可以通過 default 修飾方法實現了。

1Annotation[] getAnnotations(); 獲取目標對象(方法和屬性)上的所有註解
2default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
3     Objects.requireNonNull(annotationClass);
4     
5     for (Annotation annotation : getDeclaredAnnotations()) {
6         if (annotationClass.equals(annotation.annotationType())) {
7             
8             
9             return annotationClass.cast(annotation);
10         }
11     }
12     return null;
13 }

提供了獲取泛型相關的功能,只有方法和構造方法上支持泛型,所以只有 Method,Constructor 實現了該接口

- Member

作為一個對象內部方法和屬性的聲明的抽象,包含了名稱,修飾符,所在的類,其中修飾符包含了 static final public private volatile 等,通過一個整數表示,每一個類型在二進位中佔一個位。

1public Class<?> getDeclaringClass();
2public String getName();
3public int getModifiers();
4
5以下為Modifier類部分代碼
6
7public static final int PUBLIC           = 0x00000001;
8public static final int PRIVATE          = 0x00000002;
9public static final int PROTECTED        = 0x00000004;
10public static final int STATIC           = 0x00000008;
11public static final int FINAL            = 0x00000010;
12public static final int SYNCHRONIZED     = 0x00000020;
13public static final int VOLATILE         = 0x00000040;
14public static final int TRANSIENT        = 0x00000080;
15public static final int NATIVE           = 0x00000100;
16public static final int INTERFACE        = 0x00000200;
17public static final int ABSTRACT         = 0x00000400;
18public static final int STRICT           = 0x00000800;
19public static boolean isPublic(int mod) {
20    return (mod & PUBLIC) != 0;
21}

這是一個類,提供了權限管理的功能,例如是否允許在反射中在外部調用一個 private 方法,獲取一個 private 屬性的值,所以 method,constructor, field 都繼承該類,下面這段代碼展示了如何在反射中訪問一個私有的成員變量,class 對象的構造方法不允許對外。

1private static void setAccessible0(AccessibleObject obj, boolean flag)
2    throws SecurityException
3{
4    if (obj instanceof Constructor && flag == true) {
5        Constructor<?> c = (Constructor<?>)obj;
6        if (c.getDeclaringClass() == Class.class) {
7            throw new SecurityException("Cannot make a java.lang.Class" +
8                                        " constructor accessible");
9        }
10    }
11    obj.override = flag;
12}
13
14boolean override;
15
16public boolean isAccessible() {
17    return override;
18}

以下為 Field 裡面通過 field.get (原始對象) 獲取屬性值得實現,先通過 override 做校驗,如果沒有重載該權限,則需要校驗訪問權限。

1public Object get(Object obj)
2    throws IllegalArgumentException, IllegalAccessException
3{
4    if (!override) {
5        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
6            Class<?> caller = Reflection.getCallerClass();
7            checkAccess(caller, clazz, obj, modifiers);
8        }
9    }
10    return getFieldAccessor(obj).get(obj);
11}

下面我們看看如何通過反射修改 Field 裡面屬性的值。

通過上面的代碼,我們可以看出 jdk 將 Field 屬性的讀取和寫入委託給 FieldAccessor,那麼如何獲取 FieldAccessor 呢 ?

1class UnsafeFieldAccessorFactory {
2    UnsafeFieldAccessorFactory() {
3    }
4
5    static FieldAccessor newFieldAccessor(Field var0, boolean var1) {
6        Class var2 = var0.getType();
7        boolean var3 = Modifier.isStatic(var0.getModifiers());
8        boolean var4 = Modifier.isFinal(var0.getModifiers());
9        boolean var5 = Modifier.isVolatile(var0.getModifiers());
10        boolean var6 = var4 || var5;
11        boolean var7 = var4 && (var3 || !var1);
12
13        if (var3) {
14            UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(var0.getDeclaringClass());
15
16            return (FieldAccessor) ((!var6)
17            ? ((var2 == Boolean.TYPE)
18            ? new UnsafeStaticBooleanFieldAccessorImpl(var0)
19            : ((var2 == Byte.TYPE)
20            ? new UnsafeStaticByteFieldAccessorImpl(var0)
21            : ((var2 == Short.TYPE)
22            ? new UnsafeStaticShortFieldAccessorImpl(var0)
23            : ((var2 == Character.TYPE)
24            ? new UnsafeStaticCharacterFieldAccessorImpl(var0)
25            : ((var2 == Integer.TYPE)
26            ? new UnsafeStaticIntegerFieldAccessorImpl(var0)
27            : ((var2 == Long.TYPE)
28            ? new UnsafeStaticLongFieldAccessorImpl(var0)
29            : ((var2 == Float.TYPE)
30            ? new UnsafeStaticFloatFieldAccessorImpl(var0)
31            : ((var2 == Double.TYPE)
32            ? new UnsafeStaticDoubleFieldAccessorImpl(var0)
33            : new UnsafeStaticObjectFieldAccessorImpl(var0)))))))))
34            : ((var2 == Boolean.TYPE)
35            ? new UnsafeQualifiedStaticBooleanFieldAccessorImpl(var0, var7)
36            : ((var2 == Byte.TYPE)
37            ? new UnsafeQualifiedStaticByteFieldAccessorImpl(var0, var7)
38            : ((var2 == Short.TYPE)
39            ? new UnsafeQualifiedStaticShortFieldAccessorImpl(var0, var7)
40            : ((var2 == Character.TYPE)
41            ? new UnsafeQualifiedStaticCharacterFieldAccessorImpl(var0, var7)
42            : ((var2 == Integer.TYPE)
43            ? new UnsafeQualifiedStaticIntegerFieldAccessorImpl(var0, var7)
44            : ((var2 == Long.TYPE)
45            ? new UnsafeQualifiedStaticLongFieldAccessorImpl(var0, var7)
46            : ((var2 == Float.TYPE)
47            ? new UnsafeQualifiedStaticFloatFieldAccessorImpl(var0, var7)
48            : ((var2 == Double.TYPE)
49            ? new UnsafeQualifiedStaticDoubleFieldAccessorImpl(var0, var7)
50            : new UnsafeQualifiedStaticObjectFieldAccessorImpl(var0, var7))))))))));
51        } else {
52            return (FieldAccessor) ((!var6)
53            ? ((var2 == Boolean.TYPE)
54            ? new UnsafeBooleanFieldAccessorImpl(var0)
55            : ((var2 == Byte.TYPE) ? new UnsafeByteFieldAccessorImpl(var0)
56                                   : ((var2 == Short.TYPE)
57            ? new UnsafeShortFieldAccessorImpl(var0)
58            : ((var2 == Character.TYPE)
59            ? new UnsafeCharacterFieldAccessorImpl(var0)
60            : ((var2 == Integer.TYPE)
61            ? new UnsafeIntegerFieldAccessorImpl(var0)
62            : ((var2 == Long.TYPE) ? new UnsafeLongFieldAccessorImpl(var0)
63                                   : ((var2 == Float.TYPE)
64            ? new UnsafeFloatFieldAccessorImpl(var0)
65            : ((var2 == Double.TYPE) ? new UnsafeDoubleFieldAccessorImpl(var0)
66                                     : new UnsafeObjectFieldAccessorImpl(var0)))))))))
67            : ((var2 == Boolean.TYPE)
68            ? new UnsafeQualifiedBooleanFieldAccessorImpl(var0, var7)
69            : ((var2 == Byte.TYPE)
70            ? new UnsafeQualifiedByteFieldAccessorImpl(var0, var7)
71            : ((var2 == Short.TYPE)
72            ? new UnsafeQualifiedShortFieldAccessorImpl(var0, var7)
73            : ((var2 == Character.TYPE)
74            ? new UnsafeQualifiedCharacterFieldAccessorImpl(var0, var7)
75            : ((var2 == Integer.TYPE)
76            ? new UnsafeQualifiedIntegerFieldAccessorImpl(var0, var7)
77            : ((var2 == Long.TYPE)
78            ? new UnsafeQualifiedLongFieldAccessorImpl(var0, var7)
79            : ((var2 == Float.TYPE)
80            ? new UnsafeQualifiedFloatFieldAccessorImpl(var0, var7)
81            : ((var2 == Double.TYPE)
82            ? new UnsafeQualifiedDoubleFieldAccessorImpl(var0, var7)
83            : new UnsafeQualifiedObjectFieldAccessorImpl(var0, var7))))))))));
84        }
85    }
86}

以上代碼可以發現,通過工廠模式根據 field 屬性類型以及是否靜態來獲取,為什麼會有這樣的劃分呢?

首先,jdk 是通過 UNSAFE 類對堆內存中對象的屬性進行直接的讀取和寫入,要讀取和寫入首先需要確定屬性所在的位置,也就是相對對象起始位置的偏移量,而靜態屬性是針對類的不是每個對象實例一份,所以靜態屬性的偏移量需要單獨獲取。

其實通過該偏移量我們可以大致推斷出一個實例內每個屬性在堆內存的相對位置,以及分別佔用多大的空間,有了位置信息,我們還需要這個欄位的類型,以方便執行器知道讀幾個字節的數據,並且如何進行解析,目前提供了 8 大基礎類型(char vs Charector)和數組和普通引用類型。

Java 虛擬機為了保證每個對象所佔的空間都是 8 個字節倍數,有時候為了避免兩個volatile 欄位存放在同一個緩存行,所以有時候會再某些欄位上做空位填充。

以下為 UnSafe 類的部分代碼

1public final class Unsafe {
2    private static final Unsafe theUnsafe;
3    private Unsafe() {
4    }
5
6    @CallerSensitive
7    public static Unsafe getUnsafe() {
8        Class var0 = Reflection.getCallerClass();
9        if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
10            throw new SecurityException("Unsafe");
11        } else {
12            return theUnsafe;
13        }
14    }
15
16    public native int getInt(Object var1, long var2);
17    public native void putInt(Object var1, long var2, int var4);
18    public native Object getObject(Object var1, long var2);
19    public native void putObject(Object var1, long var2, Object var4);
20    public native boolean getBoolean(Object var1, long var2);
21    public native void putBoolean(Object var1, long var2, boolean var4);
22    public native byte getByte(Object var1, long var2);
23
24    public native long objectFieldOffset(Field var1);
25@Deprecated
26public int fieldOffset(Field var1) {
27    return Modifier.isStatic(var1.getModifiers())?(int)this.staticFieldOffset(var1):(int)this.objectFieldOffset(var1);
28}

然後我們在來看看通過反射來調用方法

同樣 jdk 通過 MethodAccessor 來進行method的調用,Java 虛擬機提供了兩種模式來支持 method 的調用 一個是 NativeMethodAccessorImpl 一個是通過 ASM 字節碼直接動態生成一個類在 invoke 方法內部調用目標方法,由於是動態生成所以 jdk 中沒有其源碼,但 jdk 提供了 DelegatingMethodAccessorImpl 委派模式以方便在運行過程中可以動態切換字節碼模式和 native 模式,我們可以看下生成 MethodAccessor 的代碼。

1class NativeMethodAccessorImpl extends MethodAccessorImpl {
2    private final Method method;
3    private DelegatingMethodAccessorImpl parent;
4    private int numInvocations;
5
6    NativeMethodAccessorImpl(Method var1) {
7        this.method = var1;
8    }
9
10    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
11        if(++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
12            MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
13            this.parent.setDelegate(var3);
14        }
15
16        return invoke0(this.method, var1, var2);
17    }
18
19    void setParent(DelegatingMethodAccessorImpl var1) {
20        this.parent = var1;
21    }
22
23    private static native Object invoke0(Method var0, Object var1, Object[] var2);
24}


可以看到 JDK 內部通過 numInvocations 判斷如果該反射調用次數超過 ReflectionFactory.inflationThreshold() 則用字節碼實現。如果小於該值則採用 native 實現,native 的調用比字節碼方式慢很多, 動態實現和本地實現相比執行效率要快 20 倍。因為動態實現無需經過 Java,C++ 再到 Java 的轉換,之前在 jdk6 以前有個工具 ReflectAsm 就是採用這種方式提升執行效率,不過在 jdk8 以後,也提供了字節碼方式,由於許多反射只需要執行一次,然而動態方式生成字節碼十分耗時,所以 jdk 提供了一個閾值默認15,當某個反射的調用次數小於15的話就走本地實現,大於15則走動態模式,而這個閾值可以在 jdk 啟動參數裡面做配置。

反射為什麼慢

經過以上優化,其實反射的效率並不慢,在某些情況下可能達到和直接調用基本相同的效率,但是在首次執行或者沒有緩存的情況下還是會有性能上的開銷,主要在以下方面:

1.Class.forName(); 會調用本地方法,我們用到的 method 和 field 都會在此時加載進來,雖然會進行緩存,但是本地方法免不了有 Java 到 C++ 在到Java 得轉換開銷。

2.class.getMethod(),會遍歷該 class 所有的公用方法,如果沒匹配到還會遍歷父類的所有方法,並且 getMethods() 方法會返回結果的一份拷貝,所以該操作不僅消耗 CPU 還消耗堆內存,在熱點代碼中應該儘量避免,或者進行緩存。

3.invoke 參數是一個 object 數組,而 object 數組不支持 Java 基礎類型,而自動裝箱也是很耗時的。

反射的運用

spring 加載 bean 的流程基本都用到了反射機制

1. 獲取類的實例 通過構造方法getInstance(靜態變量初始化,屬性賦值,構造方法);

2.如果實現了BeanNameAware接口,則用反射注入bean賦值給屬性;

3.如果實現了BeanFactoryAware接口,則設置 beanFactory;

4.如果實現了ApplicationContextAware,則設置 ApplicationContext;

5.調用BeanPostProcesser的預先初始化方法;

6.如果實現了InitializingBean,調用AfterPropertySet方法;

7.調用定製的 init-method()方法  對應的直接 @PostConstruct;

8.調用BeanPostProcesser的後置初始化完畢的方法。

fastjson 可以參考 ObjectDeserializer 的幾個實現 JavaBeanDeserializer 和ASMJavaBeanDeserializer。


動態代理

jdk 提供了一個工具類來動態生成一個代理,允許在執行某一個方法時進行額外的處理。

1Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
2
3class HWInvocationHandler implements InvocationHandler{
4    目標對象
5    private Object target;
6    public HWInvocationHandler(Object target){
7        this.target = target;
8    }
9    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
10        System.out.println("-插入前置通知代碼---");
11        執行相應的目標方法
12        Object rs = method.invoke(target,args);
13        System.out.println("-插入後置處理代碼---");
14        return rs;
15    }
16}

我們分析下這個方法的實現,首先生成的代理對象,需要實現參數裡面聲明的所有接口。接口的實現應給委託給 InvocationHandler 進行處理,invocationHandler 裡面可以根據 method 聲明判斷是否需要做增強,所以所生成的代理類裡面必須能夠獲取到 InvocationHandler,在我們無法知道代理類的具體類型的時候,我們可以通過反射從構造方法裡將 InvocationHandler 傳給代理類的實例。

所以 總的來說生成代理對象需要兩步

1. 獲取代理類的 class 對象

2. 通過 class 對象獲取構造方法,通過反射生成代理類的實例,並將 InvocationHandler 傳人。

1@CallerSensitive
2public static Object newProxyInstance(ClassLoader loader,
3                                      Class<?>[] interfaces,
4                                      InvocationHandler h)
5    throws IllegalArgumentException
6{
7    Objects.requireNonNull(h);
8
9    final Class<?>[] intfs = interfaces.clone();
10
11    生成代理類
15    Class<?> cl = getProxyClass0(loader, intfs);
16
17    
20    try {
21
22        獲取代理類的構造方法
23        final Constructor<?> cons = cl.getConstructor(constructorParams);
24        final InvocationHandler ih = h;
25        if (!Modifier.isPublic(cl.getModifiers())) {
26            AccessController.doPrivileged(new PrivilegedAction<Void>() {
27                public Void run() {
28                    cons.setAccessible(true);
29                    return null;
30                }
31            });
32        }
33        獲取代理類的實例,並且將invocationhandler傳人
34        return cons.newInstance(new Object[]{h});
35    } catch (IllegalAccessException|InstantiationException e) {
36       ...
37    }
38}

下面我們在看下 getProxyClass0 如何獲取代理類的 class 對象,這裡 jdk 通過 WeakCache 來緩存已經生成的 class 對象,因為生成該 class 通過字節碼生成還是很耗時,同時為了解決之前由於動態代理生成太多 class 對象導致內存不足,所以這裡通過弱引用 WeakReference 來緩存所生成的代理對象 class,當發生 GC 的時候如果該 class 對象沒有其他的強引用將會被直接回收。生成代理類的 class 在 ProxyGenerator 的 generateProxyClass 方法內實現,該方法返回一個 byte[] 數組,最後通過一個本地方法加載到虛擬機,所以可以看出生成該對象還是非常耗時的。

1生成字節碼數組
2byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
3    proxyName, interfaces, accessFlags);
4try {
5加載進虛擬機
6    return defineClass0(loader, proxyName,
7                        proxyClassFile, 0, proxyClassFile.length);
8} catch (ClassFormatError e) {
9    
16    throw new IllegalArgumentException(e.toString());
17}
18
19private byte[] generateClassFile() {
20    this.addProxyMethod(hashCodeMethod, Object.class);
21    this.addProxyMethod(equalsMethod, Object.class);
22    this.addProxyMethod(toStringMethod, Object.class);
23    Class[] var1 = this.interfaces;
24    int var2 = var1.length;
25
26    int var3;
27    Class var4;
28    for(var3 = 0; var3 < var2; ++var3) {
29        var4 = var1[var3];
30        Method[] var5 = var4.getMethods();
31        int var6 = var5.length;
32
33        for(int var7 = 0; var7 < var6; ++var7) {
34            Method var8 = var5[var7];
35            this.addProxyMethod(var8, var4);
36        }
37    }
38
39    this.methods.add(this.generateConstructor());
40...
41 }
42 //生成一個帶invocationhandler參數的構造方法
43private ProxyGenerator.MethodInfo generateConstructor() throws IOException {
44    ProxyGenerator.MethodInfo var1 = new ProxyGenerator.MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);
45    DataOutputStream var2 = new DataOutputStream(var1.code);
46    this.code_aload(0, var2);
47    this.code_aload(1, var2);
48    var2.writeByte(183);
49    var2.writeShort(this.cp.getMethodRef("java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
50    var2.writeByte(177);
51    var1.maxStack = 10;
52    var1.maxLocals = 2;
53    var1.declaredExceptions = new short[0];
54    return var1;
55}

上面的流程可以簡單歸納為

1. 增加 hashcode,equals,toString 方法;

2. 增加所有接口中聲明的未實現方法;

3. 增加一個方法參數為 java/lang/reflect/InvocationHandler 的構造方法;

4. 其他靜態初始化數據。

動態代理的應用

1. spring-aop

spring aop 默認基於 jdk 動態代理來實現,我們來看下下面這個經典的面試問題

一個類裡面,兩個方法A和方法B,方法B上有加註解做事物增強,那麼A調用 this.B 為什麼沒有事物效果?

因為 spring-aop 默認基於 jdk 的動態代理實現,最終執行是通過生成的代理對象的,而代理對象執行A方法和B方法其實是調用的 InvocationHandler 裡面的增強後的方法,其中B方法是經過 InvocationHandler 做增強在方法前後增加了事物開啟和提交的代碼,而真正執行代碼是通過 methodB.invoke(原始對象) 而A方法的實現內部雖然包含了this.B 方法。 但其實是調用了 methodA.invoke(原始對象),而這一句代碼相當於調用的是原始對象的 methodA 方法,而這裡面的 this.B() 方法其實是調用的原始對象的B方法,沒有進行過事物增強,而如果是通過 cglib 做字節碼增強,生成這個類的子類,這種調用 this.B 方法是有事物效果的。

圖 2

2. rpc consumer

有過 RMI 開發經驗的人可能會很熟悉,為什麼在對外 export rmi 服務的時候會分別在client 和 server 生成兩個 stub 文件,其中 client 的文件其實就是用動態代理生成了一個代理類。這個代理類實現了所要對外提供服務的所有接口,每個方法的實現其實就是將接口信息,方法聲明、參數、返回值信息通過網絡發給服務端,而服務端收到請求後通過找到對應的實現然後用反射 method.invoke 進行調用,然後將結果返回給客戶端。

其實其他的 RPC 框架的實現方式大致和這個類似,只是客戶端的代理類可能不僅要將方法聲明通過網絡傳輸給服務提供方,也可以做一下服務路由、負載均衡以及傳輸一些額外的 attachment 數據給 provider。

圖 3

下次我們聊一聊互金公司怎麼扣份額。

作者簡介

小強,銅板街資金端後臺開發工程師,2015年6月加入銅板街。目前負責銅板街資金端清結算相關的開發。


 END

長按關注我們

相關焦點

  • Java反射機制深入詳解
    一.概念反射就是把Java的各種成分映射成相應的Java類。Class類的構造方法是private,由JVM創建。反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法並且顯示出來。
  • Java基礎教程:java反射機制教程
    一、反射概念 在正式講解反射之前,為了很好的去理解它我們先從一個案例說起。請看下面的代碼: 這時候java語言在設計的時候為我們提供了一個機制,就是反射機制,他能夠很方便的去解決我們的問題。
  • 理解Java反射的正確姿勢
    反射簡介反射是Java的高級特性之一,但是在實際的開發中,使用Java反射的案例卻非常的少,但是反射確實在底層框架中被頻繁的使用。比如:JDBC中的加載資料庫驅動程序,Spring框架中加載bean對象,以及態代理,這些都使用到反射,因為我們要想理解一些框架的底層原理,反射是我們必須要掌握的。理解反射我們先從他的概念入手,那麼什麼是反射呢?
  • Java反射,泛型在Json中的運用
    最近項目中遇到了Json數據自動獲取的功能,不然令人想起java的反射,已經很長時間沒複習java了正好一塊連java的這一塊內容一起過一遍。java中的反射無疑就相當於java開發者的春天,在眾多的框架中也能看到它的身影,可以在運行時檢查類,接口、變量和方法等信息,可以實例化調用方法以及設置變量值等。本文主要以代碼的形式直接將反射,泛型的運用展現出來。java中的反射首先新建一個基礎類Author。
  • Java 基礎與提高幹貨系列—Java 反射機制
    正文Java反射機制定義Java反射機制是指在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。用一句話總結就是反射可以實現在運行時可以知道任意一個類的屬性和方法。
  • 深入理解Java的動態編譯
    前提筆者很久之前就有個想法:參考現有的主流ORM框架的設計,造一個ORM輪子,在基本不改變使用體驗的前提下把框架依賴的大量的反射設計去掉,這些反射API構築的組件使用動態編譯加載的實例去替代,從而可以得到接近於直接使用原生JDBC的性能。於是帶著這樣的想法,深入學習Java的動態編譯。
  • Java基礎之反射篇
    Java基礎之反射篇「Hello,大家好!我是老醜。今天給大家帶來的知識點是反射」1. 反射是什麼?反射(Reflection),它允許在運行中的Java程序獲取自身的信息,並且可以操作類或者對象的內部屬性。2. 反射可以做什麼?3.
  • Java代碼審計基礎之反射
    中的map,可以理解為「可自定義鍵值的數組」我們可以看到主要調用了 Runtime.getRuntime().exec那麼我們要如何通過反射的方式進行調用呢?反射調用 Runtime.getRuntime().exec第一種方式,通過強行反射私有構造方法,用 Runtime 實例化進行反射這裡有一個小坑,Runtime的構造函數是私有的:
  • Java 反射,這篇寫的很透徹!
    Java技術棧www.javastack.cn關注閱讀更多優質文章一、反射機制是什麼?二、反射的具體使用三、反射的優缺點很多時候我們會遇到別人問一個問題:你給我講一下反射,到底是什麼東西?怎麼實現的?我們能用反射來做什麼?它有什麼優缺點?
  • 8千字java反射乾貨|java反射精講
    java反射機制精講目錄1. 反射機制的概念2. 反射的基礎Class類3. 反射的用法4.反射的應用示例反射機制的概念:在運行狀態中,對於任意一個類,都能夠獲取到這個類的所有屬性和方法,對於任意一個對象,都能夠調用它的任意一個方法和屬性(包括私有的方法和屬性),這種動態獲取的信息以及動態調用對象的方法的功能就稱為java語言的反射機制。反射被視為動態語言的關鍵。簡單來說反射就是java的各種成分映射成對應的java類。
  • 學Java反射,看這篇就夠了 | 原力計劃
    我們來一句話定義反射:反射就是把 Java 類中的各種成分映射成一個個的 Java 對象。不理解這句話什麼意思?沒關係,在我百度了幾分鐘後,找到三種解釋:解釋一:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術可以對一個類進行解剖,把各個組成部分映射成一個個對象。
  • 深入理解Java:註解(Annotation)基本概念
    Annotion(註解)是一個接口,程序可以通過反射來獲取指定程序元素的Annotion對象,然後通過Annotion對象來獲取註解裡面的元數據。Annotation(註解)是JDK5.0及以後版本引入的。它可以用於創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。
  • 阿里P8教你Java註解與反射
    使用反射基本上是一種解釋操作,我們可以告訴JVM,我們想要做什麼然後它滿足我們的要求,這類操作總是慢於直接執行相同的操作。5.3 Java反射相關的主要APIjava.lang.Class:代表一個類java.lang.reflect.Method:代表類的方法java.lang.reflect.Field:代表類的成員變量java.lang.reflect.Constructor:代表類的構造器…5.4 Class類通過Class對象可以得知某個類的屬性,方法,構造器,註解,以及實現了哪些接口等等信息
  • java如何通過反射操作欄位
    java創建對象除了new,還有其他辦法嗎?java方法還可以這樣調用》、《我照樣要訪問》這三篇文章,描述了通過java反射創建對象以及調用方法,有興趣的朋友可以翻閱一下。今天我再來寫寫怎麼通過反射操作欄位吧。老規矩,先上我們要操作的類的代碼。這個demo類比較簡單,一個非靜態的欄位name,一個靜態的欄位staticName,都賦值了初始值。
  • Java面試高頻考點:反射機制使用大全
    作為一個Java開發工程師,在面試的過程中,反射機制也是經常會被問到的一個問題。例如Spring的IOC實現機制,其底層都是依賴於java的反射機制,因此,這是一個非常重要的知識點。對於初學java的同學來說,掌握其使用方法很有必要。
  • 反射——Java高級開發必須懂得
    如果是類形式的,名稱為類的全稱,例如:c1的類類型名稱為int,c2的類類型名稱為java.lang.String。getSimpleName():不包含包名的類的名稱。描述:創建一個工具類名稱為ClassUtil,並且有一個靜態方法,參數為Object類型參數,首先獲取該對象的類類型,這裡使用第二種獲取方式,傳遞的是哪個對象,Class對象就是該對象類類型(這個功能是由native聲明的一個方法實現的,java中jni就是做本地方法的,該方法是由java來聲明,用C語言來實現),萬事萬物皆對象,方法同樣是對象
  • 深入理解Java虛擬機:類加載機制
    調用一個類型的靜態方法的時候使用java.lang.reflect包的方法對類型進行反射調用的時候。當初始化類的時候,發現其父類還沒有進行過初始化的時候。當虛擬機啟動時,用戶需要制定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
  • Java反射是什麼?看這篇絕對會了!
    反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法並且顯示出來。Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。
  • 面試官:Java 反射是什麼?我回答不上來!
    一.概念反射就是把Java的各種成分映射成相應的Java類。Class類的構造方法是private,由JVM創建。反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。
  • Java語言的反射機制
    Java 反射是Java語言的一個很重要的特徵,它使得Java具有了「動態性」。Java提供了一套機制來動態獲取類的信息以及動態調用對象的方法的功能,這套機制就叫——反射反射機制是如今很多流行框架的實現基礎,其中包括SSH(Struts、Spring、Hibernate)和SSM(SpringMVC、Spring、MyBatis)等。