java學習筆記——反射


  Java中類是由JVM在運行中動態加載的。JVM在第一次讀取到這個類時,會將它加載進內存,每加載一個類,JVM會創建一個Class類的實例( java.lang.Class ),這個類的實例只有JVM能夠創建。

  在Class類的實例中,會保存加載的類的所有信息,包括類名、方法、包名等。通過這些實例,就能獲取到一個類的所有信息,這就是反射

動態加載機制

  JVM只會加載用到的類,還未運行到或者沒用到的類,並不會加載進內存

獲取 Class 類的實例

通過類的class靜態變量獲取

Class cls = String.class;
System.out.println(cls.toString());
//class java.lang.String

通過類的實例獲取

String string = "Hello World";
Class cls = string.getClass();
System.out.println(cls.toString());
//class java.lang.String

從完整類名獲取

Class cls = Class.forName("java.lang.String");
System.out.println(cls.toString());
//class java.lang.String

  除了類和接口外,數組和基本數據類型也有對應的Class實例

String[] strings = {"Hello","World"};
Class cls = strings.getClass();
System.out.println(cls.toString());
//class [Ljava.lang.String;
cls = int.class;
System.out.println(cls.toString());
//int

訪問、修改字段內容

public Field getField(String name) (Class.java: 1991)(jdk13)

 Returns a Field object that reflects the specified public member field of the class or interface represented by this Class object. The name parameter is a String specifying the simple name of the desired field.
 The field to be reflected is determined by the algorithm that follows. Let C be the class or interface represented by this object:
 If C declares a public field with the name specified, that is the field to be reflected.
 If no field was found in step 1 above, this algorithm is applied recursively to each direct superinterface of C. The direct superinterfaces are searched in the order they were declared.
 If no field was found in steps 1 and 2 above, and C has a superclass S, then this algorithm is invoked recursively upon S. If C has no superclass, then a NoSuchFieldException is thrown.
 If this Class object represents an array type, then this method does not find the length field of the array type.

  該方法需要一個 String 參數,代表一個具體的公共字段名。查找順序爲,先在類中查找,沒有的話按順序查找這個類實現的接口,還找不到的話就去它的父類重複這個過程。對於數組字段,不會返回數組大小

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class cls = C.class;
        try {
            System.out.println(cls.getField("a"));
            //public int example.C.a
            System.out.println(cls.getField("b"));
            //public static final int example.A.b
            System.out.println(cls.getField("c"));
            //public int example.B.c
            System.out.println(cls.getField("array"));
            //public int[] example.C.array
        } catch (NoSuchFieldException e) {
            System.out.println(e.toString());
        }
    }
}
interface A{
    int a = 101;
    int b = 101;
}
class B{
    public int a = 202;
    public int b = 202;
    public int c = 202;
}
class C extends B implements A{
    public int a = 303;
    public int[] array = {1,2,3,4,5};
}

public Field[] getFields()(Class.java: 1810)(jdk13)

 Returns an array containing Field objects reflecting all the accessible public fields of the class or interface represented by this Class object.
 If this Class object represents a class or interface with no accessible public fields, then this method returns an array of length 0.
 If this Class object represents a class, then this method returns the public fields of the class and of all its superclasses and superinterfaces.
 If this Class object represents an interface, then this method returns the fields of the interface and of all its superinterfaces.
 If this Class object represents an array type, a primitive type, or void, then this method returns an array of length 0.
 The elements in the returned array are not sorted and are not in any particular order.

  返回一個類或接口對象能訪問的所有公共字段,包括父類和父接口的公共字段;對於沒有可訪問公共字段的類或接口,還有數組類型、基元類型或void,返回長度爲0的數組。返回的結果不帶有任何排序

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class cls = C.class;
        for(var field:cls.getFields())
            System.out.println(field);
    }
}
interface A{
    int a = 101;
    int b = 101;
}
class B{
    public int a = 202;
    public int b = 202;
    protected int c = 202;
}
class C extends B implements A{
    public int a = 303;
    private int d = 303;
    public int[] array = {1,2,3,4,5};
}
//打印結果
public int example.C.a
public int[] example.C.array
public static final int example.A.a
public static final int example.A.b
public int example.B.a
public int example.B.b

public Field getDeclaredField(String name) (Class.java: 2403)(jdk13)

 Returns a Field object that reflects the specified declared field of the class or interface represented by this Class object. The name parameter is a String that specifies the simple name of the desired field.
If this Class object represents an array type, then this method does not find the length field of the array type.

  輸入一個字段名稱,如果在該類中能找到,返回一個 Field 對象。這個字段不再限於公共字段,但是也不會再去它的接口和父類去查找

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class cls = C.class;
        System.out.println(cls.getDeclaredField("d"));
        //private int example.C.d
    }
}
class C{
    public int a = 303;
    private int d = 303;
    public int[] array = {1,2,3,4,5};
}

public Field[] getDeclaredFields() (Class.java: 2244)(jdk13)

Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private fields, but excludes inherited fields.
If this Class object represents a class or interface with no declared fields, then this method returns an array of length 0.
If this Class object represents an array type, a primitive type, or void, then this method returns an array of length 0.
The elements in the returned array are not sorted and are not in any particular order.

  返回一個 Field 數組,包含這個類或接口的所有字段,對於沒有字段的對象返回長度爲0的數組,返回結果不帶任何排序

public class Main {
    public static void main(String[] args){
        Class cls = C.class;
        for(var val:cls.getDeclaredFields())
        {
            System.out.println(val);
            //public int example.C.a
			//private int example.C.d
			//protected int example.C.e
			//public int[] example.C.array
        }
    }
}
class C{
    public int a = 303;
    private int d = 303;
    protected int e = 303;
    public int[] array = {1,2,3,4,5};
}

使用 Field 實例訪問修改對象對應字段的值

  通過上述四種方法可以獲得 Field 對象,就可以用來修改相應的類的實例的對應字段的內容

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.rmi.MarshalledObject;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Object object = new C();
        Class cls = object.getClass();
        Field field = cls.getField("a");
        System.out.println("a: "+field.get(object));//a: 303
        field.set(object,321);
        System.out.println("a: "+field.get(object));//a: 321
        //private字段只能通過帶Declared的方法獲取
        Field privateField = cls.getDeclaredField("d");
        //判斷該字段的訪問權限
        System.out.println(Modifier.isPrivate(privateField.getModifiers()));//true
        //不調用該方法,修改private對象會拋出IllegalAccessException異常
        privateField.setAccessible(true);
        //修改private字段內容
        privateField.set(object,404);
        System.out.println(privateField.get(object));//404
    }
}
class C{
    public int a = 303;
    private int d = 303;
    protected int e = 303;
    public int[] array = {1,2,3,4,5};
}

小結

  • 獲取某一類的 Class 實例 cls
  • 通過 cls.getFields() 等方法獲得object的屬性字段 field
  • 通過 field.get(object) 獲取 object 對應字段的值
  • 通過 field.set(object,value) 將 object 對應字段的值設置爲 value

調用方法

  • public Method[] getMethods() (Class.java: 1900)(jdk13)

  • public Method getMethod(String name, Class<?>… parameterTypes) (Class.java: 2100)(jdk13)

  • public Method[] getDeclaredMethods() (Class.java: 2305)(jdk13)

  • public Method getDeclaredMethod(String name, Class<?>… parameterTypes) (Class.java: 2467)(jdk13)

  這四個方法與上面四個獲取字段的方法類似,參數爲方法名和參數表,返回 Method 實例

使用 Method 實例調用方法

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object object = new A();
        Class cls = object.getClass();
        //根據方法名和參數表獲取方法
        Method method = cls.getDeclaredMethod("fun", int.class, int.class, C.class);
        //調用方法
        int ans = (int) method.invoke(object,2, 3, new C(4));
        System.out.println(ans);
        //24
    }
}
class A {
    public int fun(int a, int b, C c) {
        return a * b * c.getC();
    }
}
class C {
    private int c;
    C(int c) {
        this.c = c;
    }
    int getC() {
        return c;
    }
}

小結

  • 獲取某一類的 Class 實例 cls
  • 通過 cls.getMethods() 等方法獲得 object 能夠調用的方法 method
  • 通過 method.invoke(object,args) 調用object的對應方法

創建新實例

調用無參構造函數創建實例

Class cls = object.getClass();
Object c = cls.newInstance();

  在JDK13中, newInstance() 方法已被廢棄,替代的方法爲以下創建新實例的一般方法

調用構造函數創建實例

  通過 getDeclaredConstructor() 等方法獲得 Constructor 對象後再調用 newInstance(args) 方法創建,也可以對返回單個 Constructor 對象的方法後面鏈式調用 newInstance(args)

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class cls = C.class;
        Object object = cls.getDeclaredConstructor().newInstance();
        Object object1 = cls.getDeclaredConstructor(String.class).newInstance("object1");
        //用Constructor對象構造
        Constructor constructor = cls.getDeclaredConstructor(String.class);
        Object object2 = constructor.newInstance("object2");
    }
}
class C {
    C(){
        System.out.println("使用無參構造方法產生C的的新實例");
    }
    C(String name) {
        System.out.println("C的新實例: "+name);
    }
}

小結

  • 獲取某一類的 Class 實例 cls
  • 通過 cls.getDeclaredConstructor() 等方法獲得 Constructor 實例 constructor
  • 使用 constructor.newInstance(args) 創建新的實例

參考資料

反射 - 廖雪峯的官方網站

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章