Java 反射機制(一)

前言

Java 中有兩種方式可讓咱們在運行時識別對象和類的信息。一種是 RTTI(運行時類型識別:Run-Time Type Identification),它假定了咱們在編譯時已經知道了全部的類型;另外一種是咱們本文要說的反射機制,它容許咱們在運行時獲取和使用類的信息。不管是 RTTI 仍是反射,其本質都是同樣的,都是去動態的獲取類的信息。它們惟一不一樣的是,RTTI 在編譯時期知道要解析的類型,而反射是在運行時才知道要解析的類型。java

反射概述

反射就是把 Java 類中的各個部分(屬性、方法、構造方法等)映射成一個個對象。Class 類與 java.lang.reflect 類庫一塊兒對反射的概念提供了支持,類庫中包含了 FieldMethodConstructor 類,每一個類都實現了 Member 接口。這些類型的對象都是由 JVM 運行時建立的,用來表示未知類裏對應的成員。這樣咱們就可使用 Constructor 建立新的對象,用 getset 方法讀取和修改類中與 Field 對象關聯的字段,用 invoke 方法調用類中與 Method 對象關聯的方法等。
Java 反射機制是在運行狀態中的,對於任意一個類咱們能夠經過反射獲取這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性。重要的是,要認識到反射機制並無什麼特別之處,當咱們經過反射和一個未知類型的對象打交道時,JVM 只是簡單的對這個對象作檢查,看它屬於哪一個類,在用它作其它事情以前必須先加載那個類 Class 對象。因此那個類的字節碼文件對象對於 JVM 來講必須是可獲取的,要麼在本地機器上,要麼經過網絡獲取。數組

<!-- more -->網絡

反射 API 的使用

想要經過反射獲取一個類的信息以前,首先要先獲取這個類的 Class 對象,在 Java 中全部類型都有與之關聯的 Class 對象。框架

獲取類的 Class 對象

Java 中獲取一個類的 Class 對象有三種方式:
第 ① 種 使用 Class 類的 forName 靜態方法,當咱們知道一個類的全路徑時,能夠經過 Class.forName 方法獲取類的 Class 對象。this

Class stringClass = Class.forName("java.lang.String");
System.out.println(stringClass);

運行結果code

class java.lang.String

第 ② 種 使用 .class 獲取,這種方式只適合在編譯前就已經知道了要操做的 Class對象

Class stringClass = String.class;
System.out.println(stringClass);

運行結果blog

class java.lang.String

第 ③ 種 使用 getClass() 方法獲取繼承

Class stringClass = "mghio".getClass();
System.out.println(stringClass);

運行結果接口

class java.lang.String
經過反射建立類對象

經過反射建立類對象有兩種方式:

第 ① 種 經過調用 Class 對象的 newInstance() 方法建立

Class<Person> personClass = Person.class;
Person person = personClass.newInstance();

第 ② 種 經過調用 Constructor 對象的 newInstance() 方法建立

Class<Person> personClass = Person.class;
Constructor personConstructor = personClass.getConstructor();
Person person = (Person) personConstructor.newInstance();

二者的區別是,經過 ClassnewInstance 方法只能經過無參構造方法建立,這就要求這個類必須有一個無參的構造方法,而經過 ConstructornewInstance 能夠指定參數來選擇特定的構造方法來建立對象。如下代碼就是指定參數而後經過特定的構造方法建立對象的。

Class<Person> personClass = Person.class;
Constructor personConstructor = personClass.getConstructor();
Person person = (Person) personConstructor.newInstance("mghio", "中國上海");
經過反射獲取類的屬性

Class 類提供了兩種方式獲取一個類的屬性,第 ① 種是經過 Class 對象的 getFields 方法獲取類的屬性,該方法只能獲取類的 public 屬性。

Class<Person> personClass = Person.class;
Field[] fields = personClass.getFields();
System.out.println(Arrays.toString(fields));

運行結果

[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.id, 
public java.lang.String cn.mghio.blogmghiocode.reflect.Person.name]

第 ② 種是經過 Class 對象的 getDeclaredFields 方法獲取類的屬性,該方法能夠獲取類的全部屬性(包括 private 修飾的屬性)。

Class<Person> personClass = Person.class;
Field[] fields = personClass.getDeclaredFields();
System.out.println(Arrays.toString(fields));

運行結果

[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.id, 
public java.lang.String cn.mghio.blogmghiocode.reflect.Person.name, 
protected java.lang.Integer cn.mghio.blogmghiocode.reflect.Person.age, 
private java.lang.String cn.mghio.blogmghiocode.reflect.Person.address]
經過反射獲取類的方法

Class 也提供了兩種方式獲取類的方法,第 ① 種是經過 Class 對象的 getMethods 方法獲取類的方法(包括繼承而得的方法)。

Class<Person> personClass = Person.class;
Method[] methods = personClass.getMethods();
System.out.println(Arrays.toString(methods));

運行結果

[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.toString(), 
public java.lang.String cn.mghio.blogmghiocode.reflect.Person.getAddress(), 
...
public final native java.lang.Class java.lang.Object.getClass(), 
public final native void java.lang.Object.notify()]

第 ② 種是經過 Class 對象的 getDeclaredMethods 方法獲取類的方法(只包含類中定義的方法,不包含繼承而來的方法)。

Class<Person> personClass = Person.class;
Method[] methods = personClass.getDeclaredMethods();
System.out.println(Arrays.toString(methods));

運行結果

[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.toString(), 
public java.lang.String cn.mghio.blogmghiocode.reflect.Person.getAddress(), 
... 
protected void cn.mghio.blogmghiocode.reflect.Person.protectedMethod(), 
private void cn.mghio.blogmghiocode.reflect.Person.privateMethod()]

從以上結果能夠看出這個方法只獲取當前類中定義的方法,包含 private 方法,不會獲取從父類中繼承而來的方法。

經過反射獲取類的構造方法

Class 也提供了兩種方式獲取類的構造方法,第 ① 種是經過 Class 對象的 getConstructors 方法獲取類的構造方法(只能獲取當前類的 public 構造方法)。

Class<Person> personClass = Person.class;
Constructor[] constructors = personClass.getConstructors();
System.out.println(Arrays.toString(constructors));

運行結果

[public cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String)]

第 ② 種是經過 Class 對象的 getDeclaredConstructors 方法獲取類的構造方法(只包含類中定義的全部構造方法)。

Class<Person> personClass = Person.class;
Constructor[] constructors = personClass.getDeclaredConstructors();
System.out.println(Arrays.toString(constructors));

運行結果

[public cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String), 
protected cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String), 
private cn.mghio.blogmghiocode.reflect.Person()]
經過反射獲取類的類名

Class 類提供了兩種方式獲取類的類名,第 ① 種是經過 getName 方法獲取類的全限定名(包含包名)。

Class<Person> personClass = Person.class;
String fullPersonClassName = personClass.getName();
System.out.println(fullPersonClassName);

運行結果

cn.mghio.blogmghiocode.reflect.Person

第 ② 種是經過 Class 對象的 getSimpleName 方法獲取類的類名(不包含包名)。

Class<Person> personClass = Person.class;
String fullPersonClassName = personClass.getSimpleName();
System.out.println(fullPersonClassName);

運行結果

Person
經過反射獲取類的修飾符

能夠經過 Class 類來獲取一個類的修飾符,也就是咱們熟知的 publicprotectedprivate 等關鍵字,經過調用 getModifiers 方法來獲取一個類的修飾符。

Class<Person> personClass = Person.class;
int modifyInt = personClass.getModifiers();
System.out.println(modifyInt);

運行結果

1

返回 1 表示類 Person 的修飾符爲 public,修飾符在 Modifier 類中都被包裝成一個 int 類型的數字,部分修飾符定義以下

/**
  * The {@code int} value representing the {@code public}
  * modifier.
  */
public static final int PUBLIC           = 0x00000001;

/**
  * The {@code int} value representing the {@code private}
  * modifier.
  */
public static final int PRIVATE          = 0x00000002;

/**
  * The {@code int} value representing the {@code protected}
  * modifier.
  */
public static final int PROTECTED        = 0x00000004;
經過反射獲取類的包信息

Class 對象經過 getPackage 方法獲取類的包相關信息,可使用 Class 對象經過以下的方式獲取包信息

Class<Person> personClass = Person.class;
Package packageClazz = personClass.getPackage();
System.out.println(packageClazz.getName());

運行結果

cn.mghio.blogmghiocode.reflect
經過反射獲取類的父類

能夠經過 Class 類來獲取一個類的父類,經過調用 getModifiers 方法來獲取一個類的父類。

Class<Person> personClass = Person.class;
Class superclass = personClass.getSuperclass();
System.out.println(superclass.getName());

運行結果

java.lang.Object

以上運行結果表示 Person 類的父類是 Object 類,能夠看到 superclass 對象其實就是一個 Class 類的實例,因此也能夠繼續在這個對象上進行反射操做。

經過反射獲取類的實現接口

能夠經過 Class 類來獲取一個類的父類,經過調用 getInterfaces 方法來獲取一個類實現的接口。

Class<Person> personClass = Person.class;
Class<?>[] interfaces = personClass.getInterfaces();
System.out.println(Arrays.toString(interfaces));

運行結果

[interface cn.mghio.blogmghiocode.reflect.IPerson]

Java 中一個類能夠實現多個接口,所以 getInterfaces 方法返回一個 Class 數組,在 Java 中接口也一樣有對應的 Class 對象。這個方法須要注意的是,getInterfaces 方法僅僅只返回當前類所實現的接口。當前類的父類若是實現了接口,這些接口是不會在返回的 Class 集合中的,儘管實際上當前類其實已經實現了父類接口。

經過反射獲取泛型信息

當咱們在聲明一個類或者接口的時候能夠指定它能夠參數化,經常使用的 List 接口就是一個參數化接口的例子。好比想要檢查 List 接口的參數化類型,咱們是沒有辦法能知道它具體的參數化類型是什麼。這個類型就能夠是一個應用中全部的類型。可是,當你檢查一個使用了被參數化的類型的變量或者方法,你能夠得到這個被參數化類型的具體參數。
第 ① 種 泛型方法返回類型 當你得到了 Method 對象,那麼就能夠獲取到這個方法的泛型返回類型信息。若是方法是在一個被參數化類型之中(例如: T foo()),那麼將沒法得到它的具體類型,可是若是方法返回的是一個泛型類(例如:List<String> foo()),那麼就能夠得到這個泛型類的具體參數化類型。下面這個例子中的類定義了一個返回類型是泛型的方法。

/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 經過反射獲取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemo {

  protected List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81);

  public List<Integer> getStringList(){
    return this.stringList;
  }

}

咱們能夠獲取上面這個類 ReflectGenericDemo 的方法 getStringList 的泛型返回類型。

/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 經過反射獲取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemoTests {

  @Test
  public void testMethodReturnGenericType() throws NoSuchMethodException {
    Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class;
    Method method = reflectClass.getMethod("getStringList", (Class<?>) null);
    Type returnType = method.getGenericReturnType();
    if (returnType instanceof ParameterizedType) {
      ParameterizedType type = (ParameterizedType) returnType;
      Type[] typeArguments = type.getActualTypeArguments();
      for (Type typeArgument : typeArguments) {
        Class typeArgumentClass = (Class) typeArgument;
        System.out.println("typeArgumentClass = " + typeArgumentClass);
      }
    }
  }

}

運行結果

typeArgumentClass = class java.lang.Integer

typeArguments 數組只有一個值,這個數組中惟一的值是 IntegerClass 類的實例,同時 Class 類也實現了 Type 接口。

第 ② 種 泛型方法返回類型 泛型方法參數類型,咱們也能夠經過反射來獲取方法參數的泛型類型。

/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 經過反射獲取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemo {

  protected List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81);

  public void setStringList(List<Integer> stringList) {
    this.stringList = stringList;
  }
}

能夠經過如下方式獲取方法參數的泛型類型。

/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 經過反射獲取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemoTests {

  @Test
  public void testMethodParameterGenericType() throws NoSuchMethodException {
    Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class;
    Method method = reflectClass.getMethod("setStringList", List.class);
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    for (Type genericParameterType : genericParameterTypes) {
      if (genericParameterType instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
        Type[] parameterArgTypes = parameterizedType.getActualTypeArguments();
        for (Type parameterArgType : parameterArgTypes) {
          Class parameterArgClass = (Class) parameterArgType;
          System.out.println("parameterArgClass = " + parameterArgClass);
        }
      }
    }
  }

}

運行結果

parameterArgClass = class java.lang.Integer

第 ③ 種 泛型變量類型 能夠經過反射來訪問類中定義變量的泛型類型,無論這個變量是一個類的靜態成員變量或是實例成員變量。

/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 經過反射獲取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemo {

  private List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81);

}

咱們能夠經過如下代碼來獲取類 ReflectGenericDemo 的私有變量 stringList 的泛型變量類型。

/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 經過反射獲取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemoTests {

  @Test
  public void testFieldGenericType() throws NoSuchFieldException {
    Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class;
    Field field = reflectClass.getDeclaredField("stringList");
    Type type = field.getGenericType();
    if (type instanceof ParameterizedType) {
      ParameterizedType fieldGenericType = (ParameterizedType) type;
      Type[] fieldGenericTypes = fieldGenericType.getActualTypeArguments();
      for (Type genericType : fieldGenericTypes) {
        Class fieldGenericTypeClass = (Class) genericType;
        System.out.println(fieldGenericTypeClass);
      }
    }
  }

}

運行結果

class java.lang.Integer

數組 fieldGenericTypes 只有一個元素,它表明類 IntegerClass 類的實例。咱們能夠得出經過反射獲取泛型信息的套路都是先獲取 Class 類對象,而後經過該對象獲取相應的類,若是是要獲取變量的泛型信息就先獲取到 Field 類,若是是要獲取方法的泛型信息就先獲取到 Method 類,最後再經過是不是 ParameterizedType 的實例來判斷是不是泛型類型。

總結

咱們介紹了 Java 泛型的基本使用,反射可能在咱們平常的工做中不怎麼接觸到,可是,在不少框架中都有運用,好比,SpringIOC/DI 也是反射;還有 JDBCclassForName 也是反射。全部深刻了解 Java 反射機制頗有必要。

方法 描述
Constructor getConstructor(Class[] params) 根據構造方法的參數,返回一個 public 類型的構造方法
Constructor getConstructors() 返回全部 public 類型的構造方法數組
Constructor getDeclaredConstructor(Class[] params) 根據構造方法的參數,返回一個具體的構造方法(全部的類型)
Constructor getDeclaredConstructors() 返回該類中全部的構造方法數組(全部的類型)
Method getMethod(String name, Class[] params) 根據方法名和參數,返回一個 public 類型的方法
Method[] getMethods() 返回全部 public 類型的方法數組
Method getDeclaredMethod(String name, Class[] params) 根據方法名和參數,返回一個具體的方法(全部的類型)
Method[] getDeclaredMethods() 返回該類中的全部的方法數組(全部的類型)
Field getField(String name) 根據變量名,返回一個 public 類型的成員變量
Field[] getFields() 返回 public 類型的成員變量的數組
Field getDeclaredField(String name) 根據變量名,返回一個成員變量(全部的類型)
Field[] getDelcaredField() 返回全部成員變量組成的數組(全部的類型)
相關文章
相關標籤/搜索