反射
作用:
把一个对象的成员变量、构造方法、成员函数(方法)转换为对象,然后进行动态调用。反射是框架设计的“灵魂”技术!
注意:
代码对象从创建到产生结果的过程中,要经历过编译,加载,运行,三个阶段。
编译:将 .Java 文件转换为字节码 .class 文件。(此时代码还存在于硬盘中)
加载:将 .class 文件加载到内存中,以便编辑器进行操作。
运行:使内存中的代码产生结果。
类加载器:
加载:
指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。
通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有如下几种来源。
从本地文件系统加载class文件。
从JAR包加载class文件。
通过网络加载class文件。
把一个Java源文件动态编译,并执行加载。
类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。
作用:
将硬盘上的Class文件加载到内存(方法区)以便后续的操作。
类加载器加载完成后会在堆区创建一个class对象,用来表示加载的“类结构”。
反射的第一步:获取到堆中的class对象
方法:
Class.forName (全限定类名) 获取Class对象(然后cls.newInstance() 获取实例化对象),这种一般配合配置文件使用
类名.Class 获取响应类型的class对象,常用于参数列表用于传参
对象名.getClass() 用于方法内部,用于确定传入参数的具体类型
代码案例:
1 package com.reflex.bean;2
3 public classPerson {4 privateString Id;5 publicString name;6 publicString age;7 protectedString gender;8
9 publicString getId() {10 returnId;11 }12
13 publicString getName() {14 returnname;15 }16
17 publicString getAge() {18 returnage;19 }20
21 publicString getGender() {22 returngender;23 }24
25 public voidsetId(String id) {26
27 Id =id;28 }29
30 public voidsetName(String name) {31 this.name =name;32 }33
34 public voidsetAge(String age) {35 this.age =age;36 }37
38 public voidsetGender(String gender) {39 this.gender =gender;40 }41
42 publicPerson(String id, String name, String age, String gender) {43
44 Id =id;45 this.name =name;46 this.age =age;47 this.gender =gender;48 }49
50 publicPerson() {51
52 }53
54 private voidreflexDemo() {55 System.out.println("this is a test!");56 }57
58 @Override59 publicString toString() {60 return "Person{" +
61 "Id='" + Id + '\'' +
62 ", name='" + name + '\'' +
63 ", age='" + age + '\'' +
64 ", gender='" + gender + '\'' +
65 '}';66 }67 private voideat(){68 System.out.println("eat food!");69 }70 }
1 package com.reflex.demo;2
3 import com.reflex.bean.Person;4
5 public classdemo1 {6 public static voidmain(String[] args) throws Exception {7 //1、 Class.forName (全限定类名) 获取Class对象(然后cls.newInstance() 获取实例化对象),这种一般配合配置文件使用
8 Class cls1 = Class.forName("com.reflex.bean.Person");9 //2、 类名.Class 获取响应类型的clszz 对象,常用于参数列表用于传参
10 Class cls2 = Person.class;11 //3、 对象名.getClass() 用于方法内部,用于确定传入参数的具体类型
12 Person person = newPerson();13 Class cls3 =person.getClass();14 System.out.println(cls1);15 System.out.println(cls2);16 System.out.println(cls3);17 System.out.println(cls1 ==cls2);18 System.out.println(cls2 ==cls3);19 System.out.println(cls1 ==cls3);20 System.out.println(cls1.getName());21 System.out.println(cls1.getClassLoader());22 }23 }
cls1 == cls2 == cls3 说明:无论通过这三种方法的哪一种方法,获取的Class对象有且只有一个!(class对象只创建一次)
反射的第二步:通过Class对象获取对应的构造函数、成员变量、成员方法对象
常用方法:
Field getField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定公共成员字段。
Field[] getFields() 返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段。
Field getDeclaredField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定声明字段。
Field[] getDeclaredFields() 返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段。
Method getMethod(String name, Class>... parameterTypes) 返回一个 方法对象,该对象反映由该 Class对象表示的类或接口的指定公共成员方法。
Method[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类。
Method getDeclaredMethod(String name, Class>... parameterTypes) 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 Class对象。
Method[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映由 Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法。
Constructor getConstructor(Class>... parameterTypes) 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数。
Constructor>[] getConstructors() 返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数。
Constructor getDeclaredConstructor(Class>... parameterTypes) 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数。
Constructor>[] getDeclaredConstructors() 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组。
注意:可获取到public修饰的方法和属性(public),getDeclareXXX获取到对象中所有修饰符修饰的方法和属性(public,protect,private)。
代码案例:
1 package com.reflex.demo;2
3 import java.lang.reflect.Constructor;4 import java.lang.reflect.Field;5 import java.lang.reflect.Method;6
7
8 public classdemo2 {9 public static voidmain(String[] args) throws Exception {10 //获取class对象
11 Class cls = Class.forName("com.reflex.bean.Person");12 String name =cls.getName();13 System.out.println(name);14 //获取构造函数对象
15 System.out.println("---------分割线-----------");16 Constructor[] constructors =cls.getConstructors();17 for(Constructor con : constructors) {18 System.out.println(con);19 }20 System.out.println("---------分割线-----------");21 Constructor[] declaredConstructors =cls.getDeclaredConstructors();22 for(Constructor Dcon : declaredConstructors) {23 System.out.println(Dcon);24 }25 //获取方法对象
26 System.out.println("---------分割线-----------");27 System.out.println("会获取到继承Object类的方法");28 Method[] methods =cls.getMethods();29 for(Method me : methods) {30 System.out.println(me);31 }32 System.out.println("---------分割线-----------");33 Method[] declaredMethods =cls.getDeclaredMethods();34 for(Method Dme : declaredMethods) {35 System.out.println(Dme);36 }37 //获取属性对象
38 System.out.println("---------分割线-----------");39 Field[] fields =cls.getFields();40 for(Field fi : fields) {41 System.out.println(fi);42 }43 System.out.println("---------分割线-----------");44 Field[] declaredFields =cls.getDeclaredFields();45 for(Field Dfi : declaredFields) {46 System.out.println(Dfi);47 }48 System.out.println("---------分割线-----------");49 Object per =cls.newInstance();50 Field id = cls.getDeclaredField("Id");51 //获取和修改private修饰的方法和属性,需要设置暴力反射
52 id.setAccessible(true);53 id.set(per,"123456");54 System.out.println(per);55 }56 }
反射的使用案例:在不改变代码的情况下获取到不同对象和方法
实现:配置文件+反射(框架设计的雏形)
代码案例:
1 package com.reflex.bean;2
3 public classPerson {4 privateString Id;5 publicString name;6 publicString age;7 protectedString gender;8
9 publicString getId() {10 returnId;11 }12
13 publicString getName() {14 returnname;15 }16
17 publicString getAge() {18 returnage;19 }20
21 publicString getGender() {22 returngender;23 }24
25 public voidsetId(String id) {26
27 Id =id;28 }29
30 public voidsetName(String name) {31 this.name =name;32 }33
34 public voidsetAge(String age) {35 this.age =age;36 }37
38 public voidsetGender(String gender) {39 this.gender =gender;40 }41
42 publicPerson(String id, String name, String age, String gender) {43
44 Id =id;45 this.name =name;46 this.age =age;47 this.gender =gender;48 }49
50 publicPerson() {51
52 }53
54 private voidreflexDemo() {55 System.out.println("this is a test!");56 }57
58 @Override59 publicString toString() {60 return "Person{" +
61 "Id='" + Id + '\'' +
62 ", name='" + name + '\'' +
63 ", age='" + age + '\'' +
64 ", gender='" + gender + '\'' +
65 '}';66 }67 private voideat(){68 System.out.println("eat food!");69 }70 }
1 package com.reflex.bean;2
3
4 public classStudent {5 privateString Id;6 publicString name;7 publicString age;8 protectedString gender;9
10 publicString getId() {11 returnId;12 }13
14 publicString getName() {15 returnname;16 }17
18 publicString getAge() {19 returnage;20 }21
22 publicString getGender() {23 returngender;24 }25
26 public voidsetId(String id) {27
28 Id =id;29 }30
31 public voidsetName(String name) {32 this.name =name;33 }34
35 public voidsetAge(String age) {36 this.age =age;37 }38
39 public voidsetGender(String gender) {40 this.gender =gender;41 }42
43 publicStudent(String id, String name, String age, String gender) {44
45 Id =id;46 this.name =name;47 this.age =age;48 this.gender =gender;49 }50
51 publicStudent() {52
53 }54
55 private voidreflexDemo() {56 System.out.println("this is a test!");57 }58
59 @Override60 publicString toString() {61 return "Person{" +
62 "Id='" + Id + '\'' +
63 ", name='" + name + '\'' +
64 ", age='" + age + '\'' +
65 ", gender='" + gender + '\'' +
66 '}';67 }68 private voidstudy(){69 System.out.println("study...");70 }71 }
1 package com.reflex.demo;2
3 import java.io.InputStream;4 import java.lang.reflect.Method;5 import java.util.Properties;6
7
8 public classdemo3 {9 public static voidmain(String[] args) throws Exception{10 //创建properties对象
11 Properties prop = newProperties();12 //获取类加载器
13 ClassLoader classLoader = demo3.class.getClassLoader();14 //通过类加载器获取配置文件
15 InputStream in = classLoader.getResourceAsStream("prop.properties");16 //加载配置文件
17 prop.load(in);18 //通过配置文件获取对象的属性
19 String className = prop.getProperty("className");20 String methodName = prop.getProperty("methodName");21 //通过反射获取对象
22 Class cls =Class.forName(className);23 Object obj =cls.newInstance();24 //获取方法对象
25 Method method =cls.getDeclaredMethod(methodName);26 //因为方法修饰为private,设置暴力反射(不设置无权访问会报错)
27 method.setAccessible(true);28 //调用方法
29 method.invoke(obj);30 }31 }
配置文件:
名字:
prop.properties
内容:
#className=com.reflex.bean.Person
#methodName=eat
className=com.reflex.bean.Student
methodName=study
获取构造函数、成员属性和成员方法对象的相关操作,建议结合着API文档进行拓展学习!