java 反射 Constructor、Method、Field 基本用法
java反射主要從以下幾個方面理解
- 理解 Class 類
- 理解 Java 的類加載機(jī)制
- 學(xué)會使用 ClassLoader 進(jìn)行類加載
- 理解反射的機(jī)制
- 掌握 Constructor、Method、Field 類的用法
- 理解并掌握動態(tài)代理
1、理解Class類
Java程序在運(yùn)行時,Java運(yùn)行時系統(tǒng)一直對所有的對象進(jìn)行所謂的運(yùn)行時類型標(biāo)識,即所謂的RTTI。這項(xiàng)信息紀(jì)錄了每個對象所屬的類。虛擬機(jī)通常使用運(yùn)行時類型信息選準(zhǔn)正確方法去執(zhí)行,用來保存這些類型信息的類是Class類。Class類封裝一個對象和接口運(yùn)行時的狀態(tài),當(dāng)裝載類時,Class類型的對象自動創(chuàng)建。
簡單解釋上面一段話
1、Class類也是類的一種,只是名字和class關(guān)鍵字高度相似。Java是大小寫敏感的語言。
2、Class類的對象內(nèi)容是你創(chuàng)建的類的類型信息,比如你創(chuàng)建一個shapes類,那么,Java會生成一個內(nèi)容是shapes的Class類的對象。
3、Class類的對象不能像普通類一樣,以 new shapes() 的方式創(chuàng)建,它的對象只能由JVM創(chuàng)建,因?yàn)檫@個類沒有public構(gòu)造函數(shù)。
4、Class類的作用是運(yùn)行時提供或獲得某個對象的類型信息。
Class原理
所有java類都是繼承了object這個類,在object這個類中有一個方法:getclass().這個方法是用來取得該類已經(jīng)被實(shí)例化了的對象的該類的引用,這個引用指向的是Class類的對象。
怎么獲取Class對象
1、Class類的forName方法
Class<?> aClass = Class.forName("com.sl.reflect.Student");2、對象的getClass()方法
Student student = new Student();Class<? extends Student> aClass1 = student.getClass();3、使用類名加.class
Class classes = Student.class.getClass();4、通過ClassLoader對象的loadClass()方法
Class<?> aClass2 = ClassLoader.getSystemClassLoader().loadClass("com.sl.reflect.Student");Class類的常用方法
image.png
敲黑板調(diào)用newInstance()方法時,調(diào)用的是無參構(gòu)造器,所以每個類中一定要聲明一個無參構(gòu)造器
ClassLoader加載
類裝載器是用來把類(class)裝載進(jìn) JVM 的。JVM 規(guī)范定義了兩種類型的類裝載器:啟動類裝載器(bootstrap)和用戶自定義裝載器(user-defined class loader)。 JVM在運(yùn)行時會產(chǎn)生3個類加載器組成的初始化加載器層次結(jié)構(gòu) ,如下圖所示:
?
image.png
//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 獲取類路徑下的文件對應(yīng)的輸入流。
//文件夾在src下InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("text1.txt");//文件夾在包名下InputStream resourceAsStream1 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/sl/reflect/text2.txt");反射
Java反射機(jī)制主要提供了以下功能
- 在運(yùn)行時構(gòu)造任意一個類的對象
- 在運(yùn)行時獲取任意一個類所具有的成員變量和方法
- 在運(yùn)行時調(diào)用任意一個對象的方法(屬性)
- 生成動態(tài)代理
Student測試類
/*** @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對應(yīng)類中的所有方法--方法數(shù)組(一)// 不能獲取private方法,并且獲取從父類繼承來的所有方法Method[] methods = aClass.getMethods();for (Method method:methods) {System.out.println(method);}System.out.println("================================");//2.獲取方法// 獲取取clazz對應(yīng)類中的所有方法--方法數(shù)組(一)// 不能獲取private方法,不獲取從父類繼承來的所有方法Method[] declaredMethods = aClass.getDeclaredMethods();for (Method method:declaredMethods) {System.out.println(method);}System.out.println("=================================");// 1.3.獲取指定的方法// 需要參數(shù)名稱和參數(shù)列表,無參則不需要寫// 對于方法public void setName(String name) { }Method method = aClass.getDeclaredMethod("setName", String.class);System.out.println(method);// 而對于方法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第一個參數(shù)表示執(zhí)行哪個對象的方法,剩下的參數(shù)是執(zhí)行方法時需要傳入的參數(shù)Object obje = aClass.newInstance();method.invoke(obje,2);//如果一個方法是私有方法,第三步是可以獲取到的,但是這一步卻不能執(zhí)行//私有方法的執(zhí)行,必須在調(diào)用invoke之前加上一句method.setAccessible(true);}/*** 把類對象和類方法名作為參數(shù),執(zhí)行方法** 把全類名和方法名作為參數(shù),執(zhí)行方法* 可變參數(shù)可以放數(shù)組* @param obj: 方法執(zhí)行的那個對象.* @param methodName: 類的一個方法的方法名. 該方法也可能是私有方法.* @param args: 調(diào)用該方法需要傳入的參數(shù)* @return: 調(diào)用方法后的返回值**/public Object invoke(Object obj, String methodName, Object ... args) throws Exception{//1. 獲取 Method 對象// 因?yàn)間etMethod的參數(shù)為Class列表類型,所以要把參數(shù)args轉(zhuǎn)化為對應(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獲取指定對象的指定字段的值Object o = field.get(student);System.out.println(o);System.out.println("==========================");// 2.2設(shè)置指定對象的指定對象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 對象// 1.1 獲取全部Constructor<?>[] constructors = aClass.getConstructors();for (Constructor constructor:constructors) {System.out.println(constructor);}System.out.println("============================");// 1.2獲取某一個,需要參數(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)建對象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é)
封裝了描述方法的 Method,
描述字段的 Filed,
描述構(gòu)造器的 Constructor 等屬性.
2.1 Person.class
2.2 person.getClass()
2.3 Class.forName("com.atguigu.javase.Person")
3.1 如何獲取 Method:
1). getDeclaredMethods: 得到 Method 的數(shù)組.
2). getDeclaredMethod(String methondName, Class ... parameterTypes)
3.2 如何調(diào)用 Method
1). 如果方法時 private 修飾的, 需要先調(diào)用 Method 的 setAccessible(true), 使其變?yōu)榭稍L問
2). method.invoke(obj, Object ... args);
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)
6.1 getGenericSuperClass: 獲取帶泛型參數(shù)的父類
6.2 Type 的子接口: ParameterizedType
6.3 可以調(diào)用 ParameterizedType 的 Type[] getActualTypeArguments() 獲取泛型參數(shù)的數(shù)組.
總結(jié)
以上是生活随笔為你收集整理的java 反射 Constructor、Method、Field 基本用法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅析数据库case when 用法
- 下一篇: spring boot 所有引用jar包