反射(reflect) 1. Class對象 1.1 什麼是Class對象 當JVM載入某個class文件的時候,會自動創建一個唯一的Class對象(註意:由同一個類載入器載入的class文件),這個Class對象包含了整個class類的信息(例如:類的名稱、訪問修飾符、欄位、欄位描述、方法等等一切 ...
反射(reflect)
1. Class對象
1.1 什麼是Class對象
當JVM載入某個class文件的時候,會自動創建一個唯一的Class對象(註意:由同一個類載入器載入的class文件),這個Class對象包含了整個class類的信息(例如:類的名稱、訪問修飾符、欄位、欄位描述、方法等等一切信息)。當使用new關鍵字創建這個類的實例時,jvm都會使用這個Class對象來創建類的實例。
1.1 獲取Class對象方式
方式 | 說明 |
---|---|
Test.class | 通過類名.class直接獲取 |
t1.getClass() | 通過對象的getClass()方法獲取 |
Class.forName("...") | 使用Class類的forName靜態方法獲取,參數給的是完整類名(包名+類名) |
2. 反射
2.1 什麼是反射
所謂的反射,是在程式運行時動態獲得任何一個Class對象的成員信息(類信息、欄位、構造方法、成員方法等),並且能在運行時依據某個Class對象創建當前類的實例。
2.2 Class常用API
方法 | 說明 |
---|---|
newInstance() | 創建類實例 |
getName() | 獲取類的完整類名 |
getSimpleName() | 獲取類的簡單類名 |
getPackage() | 獲取類對應的包信息 |
getSuperclass() | 獲取父類的Class對象 |
getInterfaces() | 獲取實現的所有介面 |
getClassLoader() | 獲取當前類的類載入器 |
getConstructor(..) | 獲取公共且明確參數類型的構造方法 |
getDeclaredConstructor(..) | 獲取受保護且明確參數類型的構造方法 |
getConstructors() | 獲取所有公共的構造方法 |
getDeclaredConstructors() | 獲取所有(包括受保護)的構造方法 |
getField(..) | 根據欄位名稱獲取某一個公共的欄位 |
getDeclaredField(..) | 根據欄位名稱獲取當前類某一個的欄位(包括受保護的) |
getFields() | 獲取所有公共的欄位,包括繼承自父類的公共欄位 |
getDeclaredFields | 獲取當前類所有欄位,包括受保護的(不包括繼承父類的欄位) |
getMethod(..) | 根據方法名以及參數類型獲取一個公共的方法 |
getDeclaredMethod(..) | 根據方法名以及參數類型獲取當前類的一個受保護的方法 |
getMethods() | 獲取所有公共的方法(包括繼承自父類的公共方法) |
getDeclaredMethods() | 獲取當前類的所有方法,包括受保護的(不包括繼承父類的方法) |
isAnnotation() | 此Class是否是一個註解 |
isAnnotationPresent(..) | 當前Class時是否定義了某個註解 |
getAnnotation(..) | 獲取當前類上定義的某個註解 |
getAnnotations() | 獲取當前類上定義的所有註解 |
isEnum() | 此Class是否是一個枚舉 |
isArray() | 此Class是否是一個數組類型 |
isInterface() | 此Class是否是一個介面 |
... | 其他API請參照官方文檔 |
public class TestClass { public static void main(String[] args) throws Exception{ Class<?> clazz = Class.forName("edu.nf.reflect.Users"); //通過Class對象來創建實例,當前類必須提供一個公開並且無參的構造方法 Users user = (Users) clazz.newInstance(); //獲取類的完整類名 System.out.println(clazz.getName()); //獲取類的簡單類名 System.out.println(clazz.getSimpleName()); //獲取類所在的包名 System.out.println(clazz.getPackage().getName()); //獲取當前類的父類的Class System.out.println(clazz.getSuperclass()); //獲取當前類實現的所有介面 System.out.println(clazz.getInterfaces()); //獲取載入這個類的類載入器 System.out.println(clazz.getClassLoader()); //獲取公共並且參數類型為String的構造方法 clazz.getConstructor(String.class); //獲取私有並且參數為int類型的構造方法 clazz.getDeclaredConstructor(Integer.TYPE); //獲取所有公共的構造方法 Constructor[] consArray1 = clazz.getConstructors(); //獲取所有構造方法,包括公共和是有的 Constructor[] consArray2 = clazz.getDeclaredConstructors(); //獲取某一個公共的欄位,參數指定欄位的名字 Field f1 = clazz.getField("address"); System.out.println(f1.getName()); //獲取某一個受保護的欄位 Field f2 = clazz.getDeclaredField("tel"); System.out.println(f2.getName()); System.out.println("-----------------------"); //獲取所有公共的欄位,包括繼承自父類的公共欄位 Field[] fieldArray1 = clazz.getFields(); for (Field field : fieldArray1) { System.out.println(field.getName()); } System.out.println("-----------------------"); //獲取所有欄位,包括受保護的(不包括繼承父類的欄位) Field[] fieldArray2 = clazz.getDeclaredFields(); for (Field field : fieldArray2) { System.out.println(field.getName()); } System.out.println("-----------------------"); //獲取某一個公共的方法(第一個參數指定方法名,第二個參數指定方法參數的類型,這是一個可變參數,有多少個參數就要指定多少個類型) Method m1 = clazz.getMethod("say", String.class); System.out.println(m1.getName()); //獲取某一個受保護的方法 Method m2 = clazz.getDeclaredMethod("call"); System.out.println(m2.getName()); System.out.println("-----------------------"); //獲取所有公共的方法(包括繼承自父類的公共方法) Method[] methodArray1 = clazz.getMethods(); for (Method method : methodArray1) { System.out.println(method.getName()); } System.out.println("-----------------------"); //獲取本類的所有方法,包括受保護的(不包括父類的方法) Method[] methodArray2 = clazz.getDeclaredMethods(); for (Method method : methodArray2) { System.out.println(method.getName()); } } }
2.3 Class對象中的成員
成員 | 說明 |
---|---|
Constructor | 用於描述類的構造方法 |
Field | 用於描述類的欄位 |
Method | 用於描述類的方法 |
Parameter | 用於描述方法或構造方法的參數信息 |
2.3.1 Constructor常用API
API | 說明 |
---|---|
getName() | 獲取當前構造方法的名稱 |
getParameterCount() | 獲取構造方法的參數個數 |
getDeclaringClass() | 獲取聲明此構造方法的Class類 |
setAccessible(..) | 設置訪問開關 |
newInstance(..) | 使用當前的Constructor構建實例 |
... | 其他API請參照官方文檔 |
public class TestCons { public static void main(String[] args) throws Exception{ //載入Users的class信息,並構建Class對象 Class<?> clazz = Class.forName("edu.nf.reflect.Users"); //通過class對象訪問構造方法信息, // 根據構造方法的參數類型獲取某一個公共的構造方法 Constructor cons1 = clazz.getConstructor(int.class); // 訪問受保護的構造方法 Constructor cons2 = clazz.getDeclaredConstructor(int.class); //構造方法的名稱 System.out.println(cons1.getName()); //獲取構造方法的參數個數 System.out.println(cons1.getParameterCount()); //獲取定義這個構造方法的Class類 System.out.println(cons1.getDeclaringClass()); //通過構造方法來創建類的實例 //由於構這個造方法是私有的,因此必須強制打開訪問開關 cons1.setAccessible(true); //然後通過newInstance方法來創建類的實例,並傳入構造方法所需的參數 Users user = (Users)cons1.newInstance(21); //獲取所有的構造方法(包括私有和公有的) Constructor[] cons = clazz.getDeclaredConstructors(); for (Constructor con : cons) { System.out.println("參數個數: "+con.getParameterCount()); } } }
2.3.2 Field常用API
API | 說明 |
---|---|
getType() | 獲取當前欄位類型 |
getName() | 獲取當前欄位名稱 |
get(..) | 獲取當前欄位的值 |
set(..) | 給當前欄位賦值 |
setAccessible(..) | 設置訪問開關 |
... | 其他API請參照官方文檔 |
public class TestField { public static void main(String[] args) throws Exception{ Class<?> clazz = Class.forName("edu.nf.reflect.Users"); //創建類的實例 Object instance = clazz.newInstance(); //獲取一個受保護的欄位 Field f = clazz.getDeclaredField("tel"); //打開訪問開關 f.setAccessible(true); //給欄位賦值(第一個參數指定當前類的實例,第二個參數是具體的值) f.set(instance, "123456"); //取值(get方法的參數也是指定當前類的實例) System.out.println(f.get(instance)); //獲取欄位的類型 System.out.println(f.getType()); //獲取欄位的名稱 System.out.println(f.getName()); } }
2.3.3 Method常用API
API | 說明 |
---|---|
getName() | 獲取當前方法名稱 |
getReturnType() | 獲取當前方法的返回值 |
getParameterCount() | 獲取當前方法參數的個數 |
getParameters() | 獲取當前方法的參數對象 |
getParameterTypes() | 獲取當前方法的所有參數類型 |
setAccessible(..) | 設置訪問開關 |
invoke(..) | 回調當前的method |
... | 其他API請參照官方文檔 |
public class TestMethod { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("edu.nf.reflect.Users"); //構建當前類的實例 Object instance = clazz.newInstance(); //獲取受保護的call方法 Method method = clazz.getDeclaredMethod("call", String.class); //獲取方法的名稱 System.out.println(method.getName()); //獲取方法的返回值類型 System.out.println(method.getReturnType()); //獲取方法參數的個數 System.out.println(method.getParameterCount()); //獲取方法的所有參數對象 System.out.println(method.getParameters()); //獲取方法中所有參數的類型 Class<?>[] paramsType = method.getParameterTypes(); for (Class<?> aClass : paramsType) { System.out.println(aClass); } //調用當前方法(第一個參數是當前類的實例,第二個參數開始是方法所需的參數) //invoke方法返回的是當前調用的方法的返回值 //這個方法是受保護的,因此需要先打開訪問開關 method.setAccessible(true); Object returnValue = method.invoke(instance, "12345678"); System.out.println(returnValue); } }
2.3.4 Parameter常用API
API | 說明 |
---|---|
getType() | 獲取參數類型 |
getName() | 獲取參數的名稱(說明:JDK8預設編譯的時候是不會將參數名信息保存在位元組碼中,如果想要獲取參數名,那麼在編譯時就需要加上編譯參數 ,如:javac -parameters Users.java) |
... | 其他API請參照官方文檔 |
public class TestParameter { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("edu.nf.reflect.Users"); //先獲取指定的受保護的方法 Method callMethod = clazz.getDeclaredMethod("call", String.class); //獲取該方法的參數信息 Parameter[] params = callMethod.getParameters(); for (Parameter param : params) { //獲取參數的類型 System.out.println(param.getType()); //獲取參數的名稱(在JDK8之前是沒有辦法獲取參數名的,必須通過第三方位元組碼工具來獲取) //JDK8預設編譯的時候是不會講參數名信息保存在位元組碼中 //如果想要獲取參數名,那麼在編譯時就需要加上編譯參數 //如:javac -parameters Users.java System.out.println(param.getName()); } } }