java反射主要從以下幾個(gè)方面理解
- 理解 Class 類
- 理解 Java 的類加載機(jī)制
- 學(xué)會(huì)使用 ClassLoader 進(jìn)行類加載
- 理解反射的機(jī)制
- 掌握 Constructor魂莫、Method、Field 類的用法
- 理解并掌握動(dòng)態(tài)代理
1迷郑、理解Class類
Java程序在運(yùn)行時(shí)恒傻,Java運(yùn)行時(shí)系統(tǒng)一直對(duì)所有的對(duì)象進(jìn)行所謂的運(yùn)行時(shí)類型標(biāo)識(shí),即所謂的RTTI。這項(xiàng)信息紀(jì)錄了每個(gè)對(duì)象所屬的類塞椎。虛擬機(jī)通常使用運(yùn)行時(shí)類型信息選準(zhǔn)正確方法去執(zhí)行唱凯,用來保存這些類型信息的類是Class類羡忘。Class類封裝一個(gè)對(duì)象和接口運(yùn)行時(shí)的狀態(tài),當(dāng)裝載類時(shí)磕昼,Class類型的對(duì)象自動(dòng)創(chuàng)建。
簡單解釋上面一段話
1节猿、Class類也是類的一種票从,只是名字和class關(guān)鍵字高度相似。Java是大小寫敏感的語言滨嘱。
2峰鄙、Class類的對(duì)象內(nèi)容是你創(chuàng)建的類的類型信息,比如你創(chuàng)建一個(gè)shapes類太雨,那么吟榴,Java會(huì)生成一個(gè)內(nèi)容是shapes的Class類的對(duì)象。
3囊扳、Class類的對(duì)象不能像普通類一樣吩翻,以 new shapes() 的方式創(chuàng)建,它的對(duì)象只能由JVM創(chuàng)建锥咸,因?yàn)檫@個(gè)類沒有public構(gòu)造函數(shù)狭瞎。
4、Class類的作用是運(yùn)行時(shí)提供或獲得某個(gè)對(duì)象的類型信息搏予。
Class原理
所有java類都是繼承了object這個(gè)類熊锭,在object這個(gè)類中有一個(gè)方法:getclass().這個(gè)方法是用來取得該類已經(jīng)被實(shí)例化了的對(duì)象的該類的引用,這個(gè)引用指向的是Class類的對(duì)象雪侥。
怎么獲取Class對(duì)象
1碗殷、Class類的forName方法
Class<?> aClass = Class.forName("com.sl.reflect.Student");
2、對(duì)象的getClass()方法
Student student = new Student();
Class<? extends Student> aClass1 = student.getClass();
3速缨、使用類名加.class
Class classes = Student.class.getClass();
4锌妻、通過ClassLoader對(duì)象的loadClass()方法
Class<?> aClass2 = ClassLoader.getSystemClassLoader().loadClass("com.sl.reflect.Student");
Class類的常用方法
敲黑板調(diào)用newInstance()方法時(shí),調(diào)用的是無參構(gòu)造器鸟廓,所以每個(gè)類中一定要聲明一個(gè)無參構(gòu)造器
ClassLoader加載
類裝載器是用來把類(class)裝載進(jìn) JVM 的从祝。JVM 規(guī)范定義了兩種類型的類裝載器:啟動(dòng)類裝載器(bootstrap)和用戶自定義裝載器(user-defined class loader)。 JVM在運(yùn)行時(shí)會(huì)產(chǎn)生3個(gè)類加載器組成的初始化加載器層次結(jié)構(gòu) 引谜,如下圖所示:
//1牍陌、獲取系統(tǒng)類的加載器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//2. 獲取系統(tǒng)類加載器的父類加載器(擴(kuò)展類加載器,可以獲仍毖省).
classLoader = classLoader.getParent();
System.out.println(classLoader);
//3. 獲取擴(kuò)展類加載器的父類加載器(引導(dǎo)類加載器毒涧,不可獲取).
classLoader = classLoader.getParent();
System.out.println(classLoader);
注意系統(tǒng)類加載器可以加載當(dāng)前項(xiàng)目src目錄下面的所有類贝室,如果文件也放在src下面契讲,也可以用類加載器來加載調(diào)用 getResourceAsStream 獲取類路徑下的文件對(duì)應(yīng)的輸入流仿吞。
//文件夾在src下
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("text1.txt");
//文件夾在包名下
InputStream resourceAsStream1 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/sl/reflect/text2.txt");
反射
Java反射機(jī)制主要提供了以下功能
- 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
- 在運(yùn)行時(shí)獲取任意一個(gè)類所具有的成員變量和方法
- 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法(屬性)
- 生成動(dòng)態(tài)代理
Student測(cè)試類
/**
* @author shuliangzhao
* @Title: Student
* @ProjectName design-parent
* @Description: TODO
* @date 2019/6/15 23:08
*/
public class Student {
private String name;
private Integer age;
public Student() {
}
public Student(String name,Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
Method類
public static void testMethod() throws Exception {
Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
//1.獲取方法
// 獲取取clazz對(duì)應(yīng)類中的所有方法--方法數(shù)組(一)
// 不能獲取private方法,并且獲取從父類繼承來的所有方法
Method[] methods = aClass.getMethods();
for (Method method:methods) {
System.out.println(method);
}
System.out.println("================================");
//2.獲取方法
// 獲取取clazz對(duì)應(yīng)類中的所有方法--方法數(shù)組(一)
// 不能獲取private方法,不獲取從父類繼承來的所有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method:declaredMethods) {
System.out.println(method);
}
System.out.println("=================================");
// 1.3.獲取指定的方法
// 需要參數(shù)名稱和參數(shù)列表,無參則不需要寫
// 對(duì)于方法public void setName(String name) { }
Method method = aClass.getDeclaredMethod("setName", String.class);
System.out.println(method);
// 而對(duì)于方法public void setAge(int age) { }
method = aClass.getDeclaredMethod("setAge", Integer.class);
System.out.println(method);
// 這樣寫是獲取不到的捡偏,如果方法的參數(shù)類型是int型
// 如果方法用于反射唤冈,那么要么int類型寫成Integer: public void setAge(Integer age) { }
// 要么獲取方法的參數(shù)寫成int.class
//2.執(zhí)行方法
// invoke第一個(gè)參數(shù)表示執(zhí)行哪個(gè)對(duì)象的方法,剩下的參數(shù)是執(zhí)行方法時(shí)需要傳入的參數(shù)
Object obje = aClass.newInstance();
method.invoke(obje,2);
//如果一個(gè)方法是私有方法银伟,第三步是可以獲取到的你虹,但是這一步卻不能執(zhí)行
//私有方法的執(zhí)行,必須在調(diào)用invoke之前加上一句method.setAccessible(true);
}
/**
* 把類對(duì)象和類方法名作為參數(shù)彤避,執(zhí)行方法
*
* 把全類名和方法名作為參數(shù)傅物,執(zhí)行方法
* 可變參數(shù)可以放數(shù)組
* @param obj: 方法執(zhí)行的那個(gè)對(duì)象.
* @param methodName: 類的一個(gè)方法的方法名. 該方法也可能是私有方法.
* @param args: 調(diào)用該方法需要傳入的參數(shù)
* @return: 調(diào)用方法后的返回值
*
*/
public Object invoke(Object obj, String methodName, Object ... args) throws Exception{
//1. 獲取 Method 對(duì)象
// 因?yàn)間etMethod的參數(shù)為Class列表類型,所以要把參數(shù)args轉(zhuǎn)化為對(duì)應(yīng)的Class類型琉预。
Class [] parameterTypes = new Class[args.length];
for(int i = 0; i < args.length; i++){
parameterTypes[i] = args[i].getClass();
System.out.println(parameterTypes[i]);
}
Method method = obj.getClass().getDeclaredMethod(methodName, parameterTypes);
//如果使用getDeclaredMethod董饰,就不能獲取父類方法,如果使用getMethod圆米,就不能獲取私有方法
//
//2. 執(zhí)行 Method 方法
//3. 返回方法的返回值
return method.invoke(obj, args);
}
Field類
public static void testField() throws Exception {
Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
//1.獲取字段
// 1.1 獲取所有字段 -- 字段數(shù)組
// 可以獲取公用和私有的所有字段卒暂,但不能獲取父類字段
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field:declaredFields) {
System.out.println(field);
}
System.out.println("=============================");
// 1.2獲取指定字段
Field field = aClass.getDeclaredField("name");
System.out.println(field.getName());
System.out.println("==============================");
Student student = new Student();
//如果字段是私有的,不管是讀值還是寫值榨咐,都必須先調(diào)用setAccessible(true)方法
field.setAccessible(true);
student.setAge(1);
student.setName("張三");
//2.使用字段
// 2.1獲取指定對(duì)象的指定字段的值
Object o = field.get(student);
System.out.println(o);
System.out.println("==========================");
// 2.2設(shè)置指定對(duì)象的指定對(duì)象Field值
field.set(student, "DEF");
System.out.println(student.getName());
}
Constructor類
public static void testConstructor() throws Exception{
Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
//1. 獲取 Constructor 對(duì)象
// 1.1 獲取全部
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor constructor:constructors) {
System.out.println(constructor);
}
System.out.println("============================");
// 1.2獲取某一個(gè)介却,需要參數(shù)列表
Constructor<Student> constructor = aClass.getConstructor(String.class, Integer.class);
System.out.println(constructor);
System.out.println("============================");
//2. 調(diào)用構(gòu)造器的 newInstance() 方法創(chuàng)建對(duì)象
Object obj = constructor.newInstance("zhagn", 1);
}
Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD})
public @interface AgeValidator {
public int min();
public int max();
}
public static void testAnnotation() throws Exception{
Class<?> aClass = Class.forName("com.sl.reflect.Student");
Object o = aClass.newInstance();
Method method = aClass.getDeclaredMethod("setAge", Integer.class);
int val = 6;
AgeValidator annotation = method.getAnnotation(AgeValidator.class);
if (annotation != null) {
if (annotation instanceof AgeValidator) {
AgeValidator ageValidator = annotation;
if (val < ageValidator.min() || val > ageValidator.max()) {
throw new RuntimeException("年齡非法");
}
}
}
method.invoke(o,20);
System.out.println(o);
}
小結(jié)
- Class: 是一個(gè)類; 一個(gè)描述類的類.
封裝了描述方法的 Method,
描述字段的 Filed,
描述構(gòu)造器的 Constructor 等屬性. - 如何得到 Class 對(duì)象:
2.1 Person.class
2.2 person.getClass()
2.3 Class.forName("com.atguigu.javase.Person") - 關(guān)于 Method:
3.1 如何獲取 Method:
1). getDeclaredMethods: 得到 Method 的數(shù)組.
2). getDeclaredMethod(String methondName, Class ... parameterTypes)
3.2 如何調(diào)用 Method
1). 如果方法時(shí) private 修飾的, 需要先調(diào)用 Method 的 setAccessible(true), 使其變?yōu)榭稍L問
2). method.invoke(obj, Object ... args); - 關(guān)于 Field:
4.1 如何獲取 Field: getField(String fieldName)
4.2 如何獲取 Field 的值:
1). setAccessible(true)
2). field.get(Object obj)
4.3 如何設(shè)置 Field 的值:
field.set(Obejct obj, Object val) - 了解 Constructor 和 Annotation
- 反射和泛型.
6.1 getGenericSuperClass: 獲取帶泛型參數(shù)的父類
6.2 Type 的子接口: ParameterizedType
6.3 可以調(diào)用 ParameterizedType 的 Type[] getActualTypeArguments() 獲取泛型參數(shù)的數(shù)組.