1. 簡介
JAVA反射機制是在運行狀態中。
對於任意一個類,都能夠知道這個類的所有屬性和方法。
對於任意一個對象,都能夠調用它的任意一個方法和屬性。
這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
2. Class
Class類其實也是一個Java類,存在於JDK的java.lang包中。
java在運行時的類信息就是通過Class對象表示的。它包含了類的所有信息。
所有的類都是在對其第一次使用時,動態加載到JVM中的(懶加載)。當程序創建第一個對類的靜態成員的引用時,就會加載這個類。使用new創建類對象的時候也會被當作對類的靜態成員的引用。因此java程序程序在它開始運行之前並非被完全加載,其各個類都是在必需時才加載的。
所以,當我們寫的.java文件被加載到JVM的后,會在方法區生成該類的Class實例。此實例包含了該類的所有信息。
2.1 如何獲取Class實例
- 類型.class
- 類實例.getClass()
- Class.forName(類的權限定類名)
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class<Test> testClass = Test.class;
Test test = new Test();
Class<? extends Test> testClass1 = test.getClass();
Class<Test> testClass2 = (Class<Test>) Class.forName("com.ldx.test.Test");
}
}
2.2 使用Class實例
創建一個User
類
package com.ldx.test;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class User {
private String name;
private Integer age;
public boolean isAdmin(String name) {
if("admin".equals(name)) {
return true;
}
return false;
}
}
創建一個測試類Test
package com.ldx.test;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
@Slf4j
public class Test {
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
Field[] declaredFields = userClass.getDeclaredFields();
log.info("User類的名稱:{}", userClass.getSimpleName());
log.info("User類的權限定類名:{}", userClass.getName());
log.info("User類的字段有:{}", Arrays.toString(declaredFields));
log.info("User類的方法有:{}", Arrays.toString(userClass.getDeclaredMethods()));
Method isAdmin = userClass.getMethod("isAdmin", String.class);
log.info("User::isAdmin方法返回值為:" + isAdmin.invoke(userClass.newInstance(), "admin"));
}
}
輸出內容如下:
com.ldx.test.Test - User類的名稱:User
com.ldx.test.Test - User類的權限定類名:com.ldx.test.User
com.ldx.test.Test - User類的字段有:[private java.lang.String com.ldx.test.User.name, private java.lang.Integer com.ldx.test.User.age]
com.ldx.test.Test - User類的方法有:[public boolean com.ldx.test.User.isAdmin(java.lang.String), public java.lang.Integer com.ldx.test.User.getAge(), public void com.ldx.test.User.setAge(java.lang.Integer), public java.lang.String com.ldx.test.User.toString(), public java.lang.String com.ldx.test.User.getName(), public void com.ldx.test.User.setName(java.lang.String)]
com.ldx.test.Test - User::isAdmin方法返回值為:true
2.3 常用方法摘要
返回值 | 方法說明 |
---|---|
<U> Class<? extends U> |
asSubclass(Class<U> clazz) 強制轉換該 Class 對象,以表示指定的 class 對象所表示的類的一個子類。 |
static Class<?> |
forName(String className) 返回與帶有給定字符串名的類或接口相關聯的 Class 對象。 |
<A extends Annotation>A |
getAnnotation(Class<A> annotationClass) 如果存在該元素的指定類型的注解,則返回這些注解,否則返回 null。 |
Annotation[] |
getAnnotations() 返回此元素上存在的所有注解。 |
ClassLoader |
getClassLoader() 返回該類的類加載器。 |
Class<?> |
getComponentType() 返回表示數組組件類型的 Class 。 |
Annotation[] |
getDeclaredAnnotations() 返回直接存在於此元素上的所有注解。 |
Class<?> |
getDeclaringClass() 如果此 Class 對象所表示的類或接口是另一個類的成員,則返回的 Class 對象表示該對象的聲明類。 |
T[] |
getEnumConstants() 如果此 Class 對象不表示枚舉類型,則返回枚舉類的元素或 null。 |
Type[] |
getGenericInterfaces() 返回表示某些接口的 Type ,這些接口由此對象所表示的類或接口直接實現。 |
Type |
getGenericSuperclass() 返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的直接超類的 Type 。 |
Class<?>[] |
getInterfaces() 確定此對象所表示的類或接口實現的接口。 |
int |
getModifiers() 返回此類或接口以整數編碼的 Java 語言修飾符。 |
String |
getName() 以 String 的形式返回此 Class 對象所表示的實體(類、接口、數組類、基本類型或 void)名稱。 |
String |
getCanonicalName() 返回 Java Language Specification 中所定義的底層類的規范化名稱。主要用於輸出(toString)或log打印,大多數情況下和 getName() 一樣,但是在內部類、數組等類型的表示形式就不同了。不能用getCanonicalName() 去加載類對象,必須用getName() , |
Package |
getPackage() 獲取此類的包。 |
InputStream |
getResourceAsStream(String name) 查找具有給定名稱的資源。 |
String |
getSimpleName() 返回源代碼中給出的底層類的簡稱。 |
Class<? super T> |
getSuperclass() 返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的超類的 Class 。 |
boolean |
isAnnotation() 如果此 Class 對象表示一個注解類型則返回 true。 |
boolean |
isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定類型的注解存在於此元素上,則返回 true,否則返回 false。 |
boolean |
isAnonymousClass() 當且僅當底層類是匿名類時返回 true 。 |
boolean |
isArray() 判定此 Class 對象是否表示一個數組類。 |
boolean |
isAssignableFrom(Class<?> cls) 判定此 Class 對象所表示的類或接口與指定的 Class 參數所表示的類或接口是否相同,或是否是其超類或超接口。 |
boolean |
isEnum() 當且僅當該類聲明為源代碼中的枚舉時返回 true。 |
boolean |
isInstance(Object obj) 如果obj是這個類的一個實例此方法返回true。 |
boolean |
isInterface() 判定指定的 Class 對象是否表示一個接口類型。 |
boolean |
isLocalClass() 當且僅當這個類是局部類此方法返回true。 |
boolean |
isMemberClass() 當且僅當底層類是成員類時返回 true 。 |
boolean |
isPrimitive() 判定指定的 Class 對象是否表示一個基本類型。 |
T |
newInstance() 創建此 Class 對象所表示的類的一個新實例。 |
3. new Instance
想通過反射創建對象大概有以下幾種方式:
- 通過
Class.newInstance()
直接創建對象。 - 通過Class實例獲取到
Constructor
(構造器),通過構造器創建對象。
獲取構造方法的途徑有以下幾種:
返回值 | 方法說明 |
---|---|
Constructor |
getConstructor(Class<?>... parameterTypes) 返回一個 Constructor 對象,它反映此 Class 對象所表示的類的public構造方法。 |
Constructor<?>[] |
getConstructors() 返回所有 Constructor 對象,它反映此 Class 對象所表示的類的public構造方法。 |
Constructor |
getDeclaredConstructor(Class<?>... parameterTypes) 返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類的public/private構造方法。 |
Constructor<?>[] |
getDeclaredConstructor() 返回所有 Constructor 對象,該對象反映此 Class 對象所表示的類的public/private構造方法。 |
示例代碼如下:
@Getter
@Setter
@ToString
public class User {
private String name;
private Integer age;
public User() {
this.name = "張三";
this.age = 24;
}
public boolean isAdmin(String name) {
if("admin".equals(name)) {
return true;
}
return false;
}
}
@Slf4j
public class Test {
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
User user = userClass.newInstance();
Constructor<User> constructor = userClass.getConstructor();
User user1 = constructor.newInstance();
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
// 如果構造器為private,則可以設置訪問權限為true,即可newInstance
declaredConstructor.setAccessible(true);
User user2 = declaredConstructor.newInstance();
log.info(user.toString());
log.info(user1.toString());
log.info(user2.toString());
}
}
輸出內容如下:
15:59:01.653 [main] INFO com.ldx.test.Test - User(name=張三, age=24)
15:59:01.663 [main] INFO com.ldx.test.Test - User(name=張三, age=24)
15:59:01.663 [main] INFO com.ldx.test.Test - User(name=張三, age=24)
4. Method
返回值 | 方法說明 |
---|---|
Method |
getMethod(String name, Class<?>... parameterTypes) 返回一個 Method 對象,它反映此 Class 對象所表示的類的public方法。(可以獲取父類的方法) |
Method[] |
getMethods() 返回所有 Method 對象,它反映此 Class 對象所表示的類的public方法。(可以獲取父類的方法) |
Method |
getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一個 Method 對象,該對象反映此 Class 對象所表示的類的public/private方法。(只獲取當前類的方法) |
Method[] |
getDeclaredMethods() 返回所有 Method 對象,它反映此 Class 對象所表示的類的public/private方法。(只獲取當前類的方法) |
示例代碼如下:
@Slf4j
public class Test {
public static void main(String[] args) throws Exception {
Class<Man> manClass = Man.class;
Man man = manClass.newInstance();
Method getHairColor = manClass.getMethod("getHairColor");
Method getHairColor1 = manClass.getDeclaredMethod("getHairColor");
// 如果方法為private,則可以設置訪問權限為true,即可newInstance
getHairColor1.setAccessible(true);
log.info(getHairColor.invoke(man).toString());
log.info(getHairColor1.invoke(man).toString());
// 可以調用到父類方法
Method isAdmin = manClass.getMethod("isAdmin", String.class);
log.info(isAdmin.invoke(man, "admin").toString());
// 獲取不到父類方法
Method isAdmin1 = manClass.getDeclaredMethod("isAdmin", String.class);
log.info(isAdmin1.invoke(man, "admin").toString());
}
}
class Man extends User {
public String getHairColor() {
return "一頭黑色秀發";
}
}
輸出內容如下:
16:30:49.809 [main] INFO com.ldx.test.Test - 一頭黑色秀發
16:30:49.818 [main] INFO com.ldx.test.Test - 一頭黑色秀發
16:30:49.818 [main] INFO com.ldx.test.Test - true
Exception in thread "main" java.lang.NoSuchMethodException: com.ldx.test.Man.isAdmin(java.lang.String)
at java.lang.Class.getDeclaredMethod(Class.java:2130)
at com.ldx.test.Test.main(Test.java:31)
Method類除了有上面的invoke
方法,常用方法還有如下:
返回值 | 方法說明 |
---|---|
<T extends Annotation>T |
getAnnotation(Class<T> annotationClass) 如果存在該元素的指定類型的注解,則返回這些注釋,否則返回 null。 |
Annotation[] |
getDeclaredAnnotations() 返回直接存在於此元素上的所有注解。 |
Class<?> |
getDeclaringClass() 返回表示聲明由此 Method 對象表示的方法的類或接口的 Class 對象。 |
Type[] |
getGenericParameterTypes() 按照聲明順序返回 Type 對象的數組,這些對象描述了此 Method 對象所表示的方法的形參類型的。 |
Type |
getGenericReturnType() 返回表示由此 Method 對象所表示方法的正式返回類型的 Type 對象。 |
int |
getModifiers() 以整數形式返回此 Method 對象所表示方法的 Java 語言修飾符。 |
String |
getName() 以 String 形式返回此 Method 對象表示的方法名稱。 |
Annotation[][] |
getParameterAnnotations() 返回表示按照聲明順序對此 Method 對象所表示方法的形參進行注解的那個數組的數組。 |
Class<?>[] |
getParameterTypes() 按照聲明順序返回 Class 對象的數組,這些對象描述了此 Method 對象所表示的方法的形參類型。 |
Class<?> |
getReturnType() 返回一個 Class 對象,該對象描述了此 Method 對象所表示的方法的正式返回類型。 |
Object |
invoke(Object obj, Object... args) 對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。 |
boolean |
isVarArgs() 如果將此方法聲明為帶有可變數量的參數,則返回 true ;否則,返回 false 。 |
String |
toGenericString() 返回描述此 Method 的字符串,包括類型參數。 |
void |
setAccessible(boolean flag) 將此對象的 accessible 標志設置為指示的布爾值,即設置其可訪問性。 |
其中:getReturnType/getGenericReturnType
都是獲取Method對象表示的方法的返回類型,只不過前者返回的Class類型后者返回的Type,Type就是一個接口而已,在Java8中新增一個默認的方法實現,返回的就參數類型信息。getParameterTypes/getGenericParameterTypes
亦是如此。
其返回結果和Class.getTypeName()
放回結果相同,就是輸出返回值的類的name
(帶包名)。
public interface Type {
default String getTypeName() {
return toString();
}
}
5. Field
返回值 | 方法說明 |
---|---|
Field |
getField(String name) 返回一個 Field 對象,它反映此 Class 對象所表示的類的public屬性。(可以獲取父類的方法) |
Field[] |
getFields() 返回所有 Field 屬性,它反映此 Class 對象所表示的類的public屬性。(可以獲取父類的方法) |
Field |
getDeclaredField(String name) 返回一個 Field 對象,該對象反映此 Class 對象所表示的類的public/private屬性。(只獲取當前類的方法) |
Field[] |
getDeclaredFields() 返回所有 Field 屬性,該對象反映此 Class 對象所表示的類的public/private屬性。(只獲取當前類的方法) |
示例代碼如下:
@Slf4j
public class Test {
public static void main(String[] args) throws Exception {
Class<Man> manClass = Man.class;
Man man = manClass.newInstance();
Field hair = manClass.getDeclaredField("hair");
hair.setAccessible(true);
hair.set(man, "濃密的黑發");
log.info(hair.get(man).toString());
log.info(man.getHair());
Class<?> type = hair.getType();
Class<?> declaringClass = hair.getDeclaringClass();
log.info(type.getCanonicalName());
log.info(declaringClass.getCanonicalName());
}
}
@Getter
class Man extends User {
private String hair;
}
輸出內容如下:
17:13:17.903 [main] INFO com.ldx.test.Test - 濃密的黑發
17:13:17.909 [main] INFO com.ldx.test.Test - 濃密的黑發
17:13:17.909 [main] INFO com.ldx.test.Test - java.lang.String
17:13:17.909 [main] INFO com.ldx.test.Test - com.ldx.test.Man
Field類除了有上面寫到的方法,常用方法還有如下:
返回值 | 方法說明 |
---|---|
Object |
get(Object obj) 返回指定對象上此 Field 表示的字段的值。 |
<T extends Annotation>T |
getAnnotation(Class<T> annotationClass) 如果存在該元素的指定類型的注解,則返回這些注釋,否則返回 null。 |
boolean |
getBoolean(Object obj) 獲取一個靜態或實例 boolean 字段的值。 |
byte |
getByte(Object obj) 獲取一個靜態或實例 byte 字段的值。 |
char |
getChar(Object obj) 獲取 char 類型或另一個通過擴展轉換可以轉換為 char 類型的基本類型的靜態或實例字段的值。 |
Annotation[] |
getDeclaredAnnotations() 返回直接存在於此元素上的所有注解。 |
Class<?> |
getDeclaringClass() 返回表示類或接口的 Class 對象,該類或接口聲明由此 Field 對象表示的字段。 |
double |
getDouble(Object obj) 獲取 double 類型或另一個通過擴展轉換可以轉換為 double 類型的基本類型的靜態或實例字段的值。 |
float |
getFloat(Object obj) 獲取 float 類型或另一個通過擴展轉換可以轉換為 float 類型的基本類型的靜態或實例字段的值。 |
Type |
getGenericType() 返回一個 Type 對象,它表示此 Field 對象所表示字段的聲明類型。 |
int |
getInt(Object obj) 獲取 int 類型或另一個通過擴展轉換可以轉換為 int 類型的基本類型的靜態或實例字段的值。 |
long |
getLong(Object obj) 獲取 long 類型或另一個通過擴展轉換可以轉換為 long 類型的基本類型的靜態或實例字段的值。 |
int |
getModifiers() 以整數形式返回由此 Field 對象表示的字段的 Java 語言修飾符。 |
String |
getName() 返回此 Field 對象表示的字段的名稱。 |
short |
getShort(Object obj) 獲取 short 類型或另一個通過擴展轉換可以轉換為 short 類型的基本類型的靜態或實例字段的值。 |
Class<?> |
getType() 返回一個 Class 對象,它標識了此 Field 對象所表示字段的聲明類型。 |
boolean |
isEnumConstant() 如果此字段表示枚舉類型的元素,則返回 true ;否則返回 false 。 |
void |
set(Object obj, Object value) 將指定對象變量上此 Field 對象表示的字段設置為指定的新值。 |
void |
setBoolean(Object obj, boolean z) 將字段的值設置為指定對象上的一個 boolean 值。 |
void |
setByte(Object obj, byte b) 將字段的值設置為指定對象上的一個 byte 值。 |
void |
setChar(Object obj, char c) 將字段的值設置為指定對象上的一個 char 值。 |
void |
setDouble(Object obj, double d) 將字段的值設置為指定對象上的一個 double 值。 |
void |
setFloat(Object obj, float f) 將字段的值設置為指定對象上的一個 float 值。 |
void |
setInt(Object obj, int i) 將字段的值設置為指定對象上的一個 int 值。 |
void |
setLong(Object obj, long l) 將字段的值設置為指定對象上的一個 long 值。 |
void |
setShort(Object obj, short s) 將字段的值設置為指定對象上的一個 short 值。 |
String |
toGenericString() 返回一個描述此 Field (包括其一般類型)的字符串。 |
void |
setAccessible(boolean flag) 將此對象的 accessible 標志設置為指示的布爾值,即設置其可訪問性。 |