在 Java
中有兩種方式可讓咱們在運行時識別對象和類的信息。一種是 RTTI
(運行時類型識別:Run-Time Type Identification),它假定了咱們在編譯時已經知道了全部的類型;另外一種是咱們本文要說的反射機制
,它容許咱們在運行時獲取和使用類的信息。不管是 RTTI
仍是反射
,其本質都是同樣的,都是去動態的獲取類的信息。它們惟一不一樣的是,RTTI
在編譯時期知道要解析的類型,而反射
是在運行時才知道要解析的類型。java
反射就是把 Java
類中的各個部分(屬性、方法、構造方法等)映射成一個個對象。Class
類與 java.lang.reflect
類庫一塊兒對反射的概念提供了支持,類庫中包含了 Field
、Method
及 Constructor
類,每一個類都實現了 Member
接口。這些類型的對象都是由 JVM
運行時建立的,用來表示未知類裏對應的成員。這樣咱們就可使用 Constructor
建立新的對象,用 get
和 set
方法讀取和修改類中與 Field
對象關聯的字段,用 invoke
方法調用類中與 Method
對象關聯的方法等。Java
反射機制是在運行狀態中的,對於任意一個類咱們能夠經過反射獲取這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性。重要的是,要認識到反射機制並無什麼特別之處,當咱們經過反射和一個未知類型的對象打交道時,JVM
只是簡單的對這個對象作檢查,看它屬於哪一個類,在用它作其它事情以前必須先加載那個類 Class
對象。因此那個類的字節碼文件對象對於 JVM
來講必須是可獲取的,要麼在本地機器上,要麼經過網絡獲取。數組
<!-- more -->網絡
想要經過反射獲取一個類的信息以前,首先要先獲取這個類的 Class
對象,在 Java
中全部類型都有與之關聯的 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();
二者的區別是,經過 Class
的 newInstance
方法只能經過無參構造方法建立,這就要求這個類必須有一個無參的構造方法,而經過 Constructor
的 newInstance
能夠指定參數來選擇特定的構造方法來建立對象。如下代碼就是指定參數而後經過特定的構造方法建立對象的。
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
類來獲取一個類的修飾符,也就是咱們熟知的 public
、protected
、private
等關鍵字,經過調用 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
數組只有一個值,這個數組中惟一的值是 Integer
的 Class
類的實例,同時 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
只有一個元素,它表明類 Integer
的 Class
類的實例。咱們能夠得出經過反射獲取泛型信息的套路都是先獲取 Class
類對象,而後經過該對象獲取相應的類,若是是要獲取變量的泛型信息就先獲取到 Field
類,若是是要獲取方法的泛型信息就先獲取到 Method
類,最後再經過是不是 ParameterizedType
的實例來判斷是不是泛型類型。
咱們介紹了 Java
泛型的基本使用,反射可能在咱們平常的工做中不怎麼接觸到,可是,在不少框架中都有運用,好比,Spring
的 IOC/DI
也是反射;還有 JDBC
的 classForName
也是反射。全部深刻了解 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() | 返回全部成員變量組成的數組(全部的類型) |