Java反射

Class對象的獲取

三種方式獲取Class對象java

  1. 調用對象的getClass方法
  2. 調用類名.class
  3. Class.forName("類的全名"), 這種方式是去JVM中查找,可能會拋出ClassNotFoundException
public class SubPerson extends Person {

    public Long subId;
    private String subName;
    private SubPerson() {
    }
    public SubPerson(Long subId, String subName) {
        this.subId = subId;
        this.subName = subName;
    }

}

public class Person {

    public Long id;
    private String name;
    public Person() {
    }

    class Card{
        private String id;
        public Card() {
        }
        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
    }
}


//1
Class personClass1 = new Person().getClass();
 System.out.println(personClass1);
 
 //2
 Class personClass2 = Person.class;
 System.out.println(personClass2);
 
 //3
 try {
     Class personClass3 = Class.forName("com.banary.reflect.Person");
     System.out.println(personClass3);
 }catch (ClassNotFoundException e){
     e.printStackTrace();
 }

獲取類名

public static void ClassMethodTest(){
    //獲取全類名
    System.out.println(Person.class.getName());
    System.out.println(int.class.getName());
    System.out.println(int[][][].class.getName());
    System.out.println(Person.Card.class.getName());
    System.out.println(new Runnable() {
        @Override
        public void run() {
        }
    }.getClass().getName());

    Runnable runnable = ()-> System.out.println("test");
    System.out.println(runnable.getClass().getName());

    System.out.println("------------分割線------------");

    //獲取類名
    System.out.println(Person.class.getSimpleName());
    System.out.println(int.class.getSimpleName());
    System.out.println(int[][][].class.getSimpleName());
    System.out.println(Person.Card.class.getSimpleName());
    System.out.println(new Runnable() {
        @Override
        public void run() {
        }
    }.getClass().getSimpleName());
    System.out.println(runnable.getClass().getSimpleName());

    System.out.println("------------分割線------------");

    /**
     * 獲取官方類名
     * 1. getCanonicalName() 返回的也是全限定類名,可是對於內部類,不用 $ 開頭,而用 .。
     * 2. getCanonicalName() 對於數組類型的 Class,同 simplename 同樣直接在後面添加 [] 。
     * 3. getCanonicalName() 不一樣於 simplename 的地方是,不存在 canonicalName 的時候返回 null 而不是空字符串。
     * 4. 局部類和匿名內部類不存在 canonicalName。
     */
    System.out.println(Person.class.getCanonicalName());
    System.out.println(int.class.getCanonicalName());
    System.out.println(int[][][].class.getCanonicalName());
    System.out.println(Person.Card.class.getCanonicalName());
    System.out.println(new Runnable() {
        @Override
        public void run() {
        }
    }.getClass().getCanonicalName());
    System.out.println(runnable.getClass().getCanonicalName());
}

獲取修飾符

  1. java的修飾符
  • 用來限制做用域,如 public、protected、priviate
  • 用來提示子類複寫,abstract
  • 用來標記爲靜態類 static
  • 註解
  • 定義常量,final
  1. 獲取類的修飾符
// 十進制
System.out.println(Modifier.class.getModifiers());
// 十六進制
System.out.println(Integer.toHexString(Modifier.class.getModifiers()));
// 對應的真實修飾符
System.out.println(java.lang.reflect.Modifier.toString(Modifier.class.getModifiers()));

java中修飾符的表示方式

經過上述的方法,可見java中是採用二進制位來表示修飾符,下列爲java中修飾符實現數組

public class Modifier {

    // 2^0
    public static final int PUBLIC           = 0x00000001;
    // 2^1
    public static final int PRIVATE          = 0x00000002;
    // 2^2
    public static final int PROTECTED        = 0x00000004;
    public static final int STATIC           = 0x00000008;
    public static final int FINAL            = 0x00000010;
    public static final int SYNCHRONIZED     = 0x00000020;
    public static final int VOLATILE         = 0x00000040;
    public static final int TRANSIENT        = 0x00000080;
    public static final int NATIVE           = 0x00000100;
    public static final int INTERFACE        = 0x00000200;
    public static final int ABSTRACT         = 0x00000400;
    public static final int STRICT           = 0x00000800;
    
    public static String toString(int mod) {
        StringBuilder sb = new StringBuilder();
        int len;

        if ((mod & PUBLIC) != 0)        sb.append("public ");
        if ((mod & PROTECTED) != 0)     sb.append("protected ");
        if ((mod & PRIVATE) != 0)       sb.append("private ");

        /* Canonical order */
        if ((mod & ABSTRACT) != 0)      sb.append("abstract ");
        if ((mod & STATIC) != 0)        sb.append("static ");
        if ((mod & FINAL) != 0)         sb.append("final ");
        if ((mod & TRANSIENT) != 0)     sb.append("transient ");
        if ((mod & VOLATILE) != 0)      sb.append("volatile ");
        if ((mod & SYNCHRONIZED) != 0)  sb.append("synchronized ");
        if ((mod & NATIVE) != 0)        sb.append("native ");
        if ((mod & STRICT) != 0)        sb.append("strictfp ");
        if ((mod & INTERFACE) != 0)     sb.append("interface ");

        if ((len = sb.length()) > 0)    /* trim trailing space */
            return sb.toString().substring(0, len-1);
        return "";
    }

}

獲取類的屬性

類的屬性包括Field、Method、Constructorapp

  1. Field
  • 獲取Field
Class personClass = SubPerson.class;
//獲取自身的全部的 public 屬性,包括從父類繼承下來的, 可能會拋出運行時異常SecurityException
Field[] fields = personClass.getFields();
for (Field field : fields) {
    System.out.println(field.getName());
}
//根據field名獲取, 可能會拋出NoSuchFieldException(檢查型), SecurityException
try {
    Field field1 = personClass.getField("subId");
    System.out.println(field1.getName());
} catch (NoSuchFieldException e){
    e.printStackTrace();
}

System.out.println("------------分割線------------");

//獲取全部的屬性,但不包括從父類繼承下來的屬性
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
    System.out.println(declaredField.getName());
}

System.out.println("------------分割線------------");

try{
    Field field2 = personClass.getDeclaredField("subName");
    System.out.println(field2.getName());
} catch (NoSuchFieldException e){
    e.printStackTrace();
}
  • 獲取Field類型
# 可以獲取到泛型類型
public Type getGenericType() {}

public Class<?> getType() {}
  • 獲取修飾符
# 同class和Method的修飾符同樣
public int getModifiers() {}
  • 獲取值和設置值
#設置值
public Object get(Object obj);
public int getInt(Object obj);
public long getLong(Object obj) throws IllegalArgumentException, IllegalAccessException;
public float getFloat(Object obj) throws IllegalArgumentException, IllegalAccessException;
public short getShort(Object obj) throws IllegalArgumentException, IllegalAccessException;
public double getDouble(Object obj) throws IllegalArgumentException, IllegalAccessException;
public char getChar(Object obj) throws IllegalArgumentException, IllegalAccessException;
public byte getByte(Object obj) throws IllegalArgumentException, IllegalAccessException;
public boolean getBoolean(Object obj) throws IllegalArgumentException, IllegalAccessException
public void set(Object obj, Object value);
# 獲取值
public void getInt(Object obj,int value);
public void getLong(Object obj,long value) throws IllegalArgumentException, IllegalAccessException;
public void getFloat(Object obj,float value) throws IllegalArgumentException, IllegalAccessException;
public void getShort(Object obj,short value) throws IllegalArgumentException, IllegalAccessException;
public void getDouble(Object obj,double value) throws IllegalArgumentException, IllegalAccessException;
public void getChar(Object obj,char value) throws IllegalArgumentException, IllegalAccessException;
public void getByte(Object obj,byte b) throws IllegalArgumentException, IllegalAccessException;
public void getBoolean(Object obj,boolean b) throws IllegalArgumentException, IllegalAccessException

Class 自己不對成員進行儲存,它只提供檢索,因此須要用 Field、Method、Constructor 對象來承載這些成員,因此,針對成員的操做時,通常須要爲成員指定類的實例引用。ide

  1. Method
    同獲取Field相似
  • 方法名
getName()
  • 方法參數
#獲取全部的參數
public Parameter[] getParameters() {}
// 獲取全部的參數類型
public Class<?>[] getParameterTypes() {}
// 獲取全部的參數類型,包括泛型
public Type[] getGenericParameterTypes() {}
  • 方法返回值
// 獲取返回值類型
public Class<?> getReturnType() {}
// 獲取返回值類型包括泛型
public Type getGenericReturnType() {}
  • 方法的修飾符
public int getModifiers() {}
  • 方法可能會拋出的異常
public Class<?>[] getExceptionTypes() {}
# 包括泛型
public Type[] getGenericExceptionTypes() {}
  • 執行方法
#invoke() 方法中第一個參數 Object 實質上是 Method 所依附的 Class 對應的類的實例,若是這個方法是一個靜態方法,那麼 ojb 爲 null,後面的可變參數 Object 對應的天然就是參數
#invoke() 返回的對象是 Object,因此實際上執行的時候要進行強制轉換。
#在對 Method 調用 invoke() 的時候,若是方法自己會拋出異常,那麼這個異常就會通過包裝,由 Method 統一拋出 InvocationTargetException。而經過 InvocationTargetException.getCause() 能夠獲取真正的異常。
public Object invoke(Object obj, Object... args) {}
  1. Constructor
  • 獲取構造器方法
    同獲取Field相似,可是因爲構造器不能繼承,因此getConstructor()方法獲取不到父類的構造器方法
public static void getConstructor(){
        Class subPersonClass = SubPerson.class;

        //獲取全部的public構造器,因爲父類的構造器不能繼承,因此獲取不到
        Constructor[] constructors = subPersonClass.getConstructors();
        for (Constructor c : constructors) {
            System.out.println(c.toString());
        }

        System.out.println("------------分割線------------");

        //獲取全部的構造器,包括私有和公開的
        Constructor[] declaredConstructors = subPersonClass.getDeclaredConstructors();
        for (Constructor declaredConstructor :declaredConstructors) {
            System.out.println(declaredConstructor.toString());
        }
    }
  • 建立對象
    在 Java 反射機制中有兩種方法能夠用來建立類的對象實例:Class.newInstance() 和 Constructor.newInstance(),緣由:
# Class.newInstance() 只能調用無參的構造方法,而 Constructor.newInstance() 則能夠調用任意的構造方法。
# Class.newInstance() 經過構造方法直接拋出異常,而 Constructor.newInstance() 會把拋出來的異常包裝到 InvocationTargetException 裏面去,這個和 Method 行爲一致。
# Class.newInstance() 要求構造方法可以被訪問,而 Constructor.newInstance() 卻可以訪問 private 修飾的構造器。

反射中的數組

  • 數組本質上是一個 Class,而在 Class 中存在一個方法isArray()用來識別它是否爲一個數組。
getName();
#獲取數組的裏面的元素的類型,好比 int[] 數組的 componentType 天然就是 int
getComponentType();
  • 反射中動態建立數組
    反射建立數組是經過 Array.newInstance() 這個方法
public static Object newInstance(Class<?> componentType, int... dimensions)
        throws IllegalArgumentException, NegativeArraySizeException {}

e.g.ui

# 建立一個 int[2][3] 的數組
Array.newInstance(int.class,2,3);
  • Array 的讀取與賦值
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException;

public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException;

public static void set(Object array, int index, Object value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException;

public static void setBoolean(Object array, int index, boolean z) throws IllegalArgumentException, ArrayIndexOutOfBoundsException;

public static Object get(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException;

public static short getShort(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException;

反射中的枚舉

  • 同數組同樣,枚舉本質上也是一個 Class 而已,但反射中仍是把它單獨提出來了
// 用來斷定 Class 對象是否是枚舉類型
Class.isEnum()

// 獲取全部的枚舉常量
Class.getEnumConstants()

// 判斷一個 Field 是否是枚舉常量
Field.isEnumConstant()
  • 枚舉的獲取與設定
    由於等同於 Class,因此枚舉的獲取與設定就能夠經過 Field 中的 get() 和 set() 方法。須要注意的是,若是要獲取枚舉裏面的 Field、Method、Constructor 能夠調用 Class 的通用 API,值得注意的是,枚舉類的Constructor能夠獲取,可是不能實例化
public static void reflectEnum() throws Exception{
        Class colorClass = Color.class;
        if(colorClass.isEnum()){
            System.out.println(String.format("class Color %s is Enum", colorClass.getName()));
            Field[] fields = colorClass.getDeclaredFields();
            for (Field field:fields) {
                if(field.isEnumConstant()){
                    System.out.println(String.format("%s is EnumConstant", field.getName()));
                }else{
                    System.out.println(String.format("%s is not EnumConstant", field.getName()));
                }
            }
        }

        System.out.println("_____________________");

        Constructor[] constructors = colorClass.getDeclaredConstructors();
        for(Constructor constructor : constructors){
            System.out.println(constructor.getName());
            constructor.setAccessible(true);
            Color color = (Color) constructor.newInstance("123123");
            System.out.println(color.getKey());
        }
        Color color = (Color) colorClass.newInstance();
        System.out.println(color.getKey());
    }
相關文章
相關標籤/搜索