13.Java的反射
一.Java反射
1.概念:反射是框架设计的灵魂
- 框架:半成品软件。可以在框架的基础上进行软件开发,简化代码。
- 反射:将类的各个组成部分封装为其他对象,这就是反射机制。
- 可以在程序运行过程中操作这些对象。
- 可以解耦提高程序的可扩展行。
2.Java代码在计算机中经历的三个阶段:
- Source源代码阶段
- Class类对象阶段
- Runtime运行时阶段
3.获取class对象的三种方式:
- Class.forName("全类名"):将字节码文件加载进内存返回class对象。对应Java代码的Source源代码阶段。
- 用于配置文件:将类名定义在配置文件中,读取配置文件加载类。
- 类名.class:通过类名的属性class获取。对应Java代码的Class类对象阶段。
- 用于参数传递。
- 对象.getClass():getClass()方法在Object类中定义。对应Java代码的Runtime阶段。
- 用于对象的获取字节码的方式。
- 结论:对于同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪种方式获取的Class对象都是同一个。
Person类:
1 public class Person {
2
3 private String name;
4 private int age;
5
6 public String a;
7
8 public Person() {
9 }
10
11 private Person(String name) {
12 this.name = name;
13 }
14
15 public Person(String name, int age) {
16 this.name = name;
17 this.age = age;
18 }
19
20 public Person(String name, int age, String a) {
21 this.name = name;
22 this.age = age;
23 this.a = a;
24 }
25
26
27 public String getName() {
28 return name;
29 }
30
31 public void setName(String name) {
32 this.name = name;
33 }
34
35 public int getAge() {
36 return age;
37 }
38
39 public void setAge(int age) {
40 this.age = age;
41 }
42
43 @Override
44 public String toString() {
45 return "Person{" +
46 "name='" + name + '\'' +
47 ", age=" + age +
48 ", a='" + a + '\'' +
49 '}';
50 }
51
52 public void publicMethod1() {
53 System.out.println("publicMethod1");
54 }
55
56 public void publicMethod2(String s) {
57 System.out.println("publicMethod2:参数为" + s);
58 }
59
60 private void privateMethod1() {
61 System.out.println("privateMethod1");
62 }
63
64 private void privateMethod2(String s) {
65 System.out.println("privateMethod2:参数为" + s);
66 }
67 }
主函数:
1 public class ReflectDemo1 {
2
3 public static void main(String[] args) throws ClassNotFoundException {
4
5 //1.Class.forName("全类名")
6 Class cls1 = Class.forName("day08.Person");
7 System.out.println(cls1);
8
9 //2.类名.class
10 Class cls2 = Person.class;
11 System.out.println(cls2);
12
13 //3.对象.getClass()
14 Person p = new Person();
15 Class cls3 = p.getClass();
16 System.out.println(cls3);
17
18
19 //比较三个对象是否为同一对象
20 System.out.println(cls1 == cls2);
21 System.out.println(cls1 == cls3);
22 }
23 }
结果:
1 class day08.Person
2 class day08.Person
3 class day08.Person
4 true
5 true
4.Class对象功能
- 获取功能:
- 获取成员变量们:Field类
- Field[] getFields():获取所有public修饰的成员变量
- Field getField(String name):利用成员变量名获取public的成员变量
- Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
- Field getDeclaredField(String name):利用成员变量名获取任意的成员变量
- 获取构造函数们:Constructor类
- Constructor<?>[] getConstructors():获取空参数public构造函数
- Constructor<T> getConstructor(类<?>... parameterTypes):根据参数获取到类的public构造函数
- Constructor<?>[] getDeclaredConstructors():获取空参数任意构造函数
- Constructor<T> getDeclaredConstructor(类<?>... parameterTypes):根据参数获取到类的任意构造函数
- 获取成员方法们:Method类
- Method[] getMethods():获取自己以及基类所有public方法
- Method getMethod(String name,类<?>... parameterTypes):根据参数获得public方法
- Method[] getDeclaredMethods():获取自己以及基类所有方法
- Method getDeclaredMethod(String name,类<?>... parameterTypes):根据参数获得任意方法
- 获取类名:
- String getName()
- 获取成员变量们:Field类
- Field:成员变量
- 操作:
- 设置值:void set(Object obj, Object value)
- 获取值:get(Object obj)
- 忽略访问权限修饰符的安全检查(暴力反射):setAccessible(true)
- 操作:
- Constructor:构造方法
- 创建对象:
- T newInstance(Object... initargs)
- 如果使用空参数构造方法,则操作可以简化:Class对象的newInstance方法
- 忽略访问权限修饰符的安全检查(暴力反射):setAccessible(true)
- 创建对象:
- Method:方法对象
- 执行方法:Object invoke(Object obj, Object... args)
- 获取方法名:String getName()
- 忽略访问权限修饰符的安全检查(暴力反射):setAccessible(true)
Field使用:
1 public class ReflectDemo2 {
2
3 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
4
5 Class personClass = Person.class;
6
7 //1.getFields:获取所有public修饰的成员变量
8 System.out.println("1.getFields:获取所有public修饰的成员变量");
9 Field[] fields = personClass.getFields();
10 for (Field f :fields) {
11 System.out.println(f);
12 }
13 /**输出:
14 * 1.getFields:获取所有public修饰的成员变量
15 * public java.lang.String day08.Person.a
16 */
17
18 //2.getField:利用成员变量名获取public的成员变量
19 System.out.println("2.getField:利用成员变量名获取public的成员变量");
20 Field a = personClass.getField("a");
21 //获取成员变量值
22 Person p = new Person();
23 Object aValue = a.get(p);
24 System.out.println(aValue);
25 //设置成员变量值
26 a.set(p,"111");
27 System.out.println(p);
28 /**输出:
29 * 2.getField:利用成员变量名获取public的成员变量
30 * null
31 * Person{name='null', age=0, a='111'}
32 */
33
34 //3.getDeclaredFields:获取所有的成员变量,不考虑修饰符
35 System.out.println("3.getDeclaredFields:获取所有的成员变量,不考虑修饰符");
36 Field[] declaredFields = personClass.getDeclaredFields();
37 for (Field f :declaredFields) {
38 System.out.println(f);
39 }
40 /**输出:
41 * 3.getDeclaredFields:获取所有的成员变量,不考虑修饰符
42 * private java.lang.String day08.Person.name
43 * private int day08.Person.age
44 * public java.lang.String day08.Person.a
45 */
46
47 //4.getDeclaredField:利用成员变量名获取任意的成员变量
48 System.out.println("4.getDeclaredField:利用成员变量名获取任意的成员变量");
49 Field name = personClass.getDeclaredField("name");
50 //忽略访问权限修饰符的安全检查
51 name.setAccessible(true);//暴力反射
52 Object nameValue = name.get(p);
53 System.out.println(nameValue);
54 name.set(p,"王");
55 System.out.println(p);
56 /**输出:
57 * 4.getDeclaredField:利用成员变量名获取任意的成员变量
58 * null
59 * Person{name='王', age=0, a='111'}
60 */
61 }
62 }
Constructor使用:
1 public class ReflectDemo2 {
2
3 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
4
5 Class personClass = Person.class;
6
7 //1.getConstructor():获取空参数public构造函数
8 System.out.println("1.getConstructor(Class<?>... parameterTypes):获取空参数public构造函数");
9 Constructor constructor1 = personClass.getConstructor();
10 System.out.println(constructor1);
11 //创建对象
12 Object person1 = constructor1.newInstance();
13 System.out.println(person1);
14
15 //简化空参
16 System.out.println("简化空参");
17 Object o = personClass.newInstance();
18 System.out.println(o);
19 /**输出:
20 * 1.getConstructor(Class<?>... parameterTypes):获取空参数构造函数
21 * public day08.Person()
22 * Person{name='null', age=0, a='null'}
23 * 简化空参
24 * Person{name='null', age=0, a='null'}
25 */
26
27 //2.getConstructor(Class<?>... parameterTypes):根据参数获取到类的public构造函数
28 System.out.println("2.getConstructor(Class<?>... parameterTypes):根据参数获取到类的public构造函数");
29 Constructor constructor2 = personClass.getConstructor(String.class, int.class);
30 System.out.println(constructor2);
31 //创建对象
32 Object person2 = constructor2.newInstance("王",23);
33 System.out.println(person2);
34 /**输出:
35 * 2.getConstructor(Class<?>... parameterTypes):根据参数获取到类的构造函数
36 * public day08.Person(java.lang.String,int)
37 * Person{name='王', age=23, a='null'}
38 */
39
40 //3.getDeclaredConstructor(Class<?>... parameterTypes):根据参数获取到类的任意构造函数
41 System.out.println("3.getDeclaredConstructor(Class<?>... parameterTypes):根据参数获取到类的任意构造函数");
42 Constructor constructor3 = personClass.getDeclaredConstructor(String.class);
43 //忽略访问权限修饰符的安全检查
44 constructor3.setAccessible(true);//暴力反射
45 System.out.println(constructor3);
46 //创建对象
47 Object person3 = constructor3.newInstance("张");
48 System.out.println(person3);
49 /**输出:
50 * 3.getDeclaredConstructor(Class<?>... parameterTypes):根据参数获取到类的任意构造函数
51 * private day08.Person(java.lang.String)
52 * Person{name='张', age=0, a='null'}
53 */
54 }
55 }
Method使用:
1 public class ReflectDemo2 {
2
3 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
4
5 Class personClass = Person.class;
6
7 //1.getMethods:获取自己以及基类所有public方法
8 System.out.println("1.getMethods:获取自己以及基类所有public方法");
9 Method[] methods1 = personClass.getMethods();
10 for (Method m : methods1) {
11 System.out.println(m);
12 System.out.println(m.getName());
13 }
14 /**输出:
15 * 1.getMethods:获取自己以及基类所有public方法
16 * public java.lang.String day08.Person.toString()
17 * toString
18 * public java.lang.String day08.Person.getName()
19 * getName
20 * public void day08.Person.setName(java.lang.String)
21 * setName
22 * public void day08.Person.publicMethod2(java.lang.String)
23 * publicMethod2
24 * public void day08.Person.publicMethod1()
25 * publicMethod1
26 * public int day08.Person.getAge()
27 * getAge
28 * public void day08.Person.setAge(int)
29 * setAge
30 * public final void java.lang.Object.wait() throws java.lang.InterruptedException
31 * wait
32 * public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
33 * wait
34 * public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
35 * wait
36 * public boolean java.lang.Object.equals(java.lang.Object)
37 * equals
38 * public native int java.lang.Object.hashCode()
39 * hashCode
40 * public final native java.lang.Class java.lang.Object.getClass()
41 * getClass
42 * public final native void java.lang.Object.notify()
43 * notify
44 * public final native void java.lang.Object.notifyAll()
45 * notifyAll
46 */
47
48 //2.getMethod(String name, Class<?>... parameterTypes):根据参数获得public方法
49 System.out.println("2.getMethod(String name, Class<?>... parameterTypes):根据参数获得任意方法");
50 //无参
51 Method method2_1 = personClass.getMethod("publicMethod1");
52 Person p2_1 = new Person();
53 //执行方法
54 method2_1.invoke(p2_1);
55
56 //有参
57 Method method2_2 = personClass.getMethod("publicMethod2", String.class);
58 Person p2_2 = new Person();
59 //执行方法
60 method2_2.invoke(p2_2, "这是有参的public方法");
61 /**输出:
62 * 2.getMethod(String name, Class<?>... parameterTypes):根据参数获得public方法
63 * publicMethod1
64 * publicMethod2:参数为这是有参的public方法
65 */
66
67 //3.getDeclaredMethods:获取自己以及基类所有方法
68 System.out.println("3.getMethods:获取自己以及基类所有方法");
69 Method[] methods2 = personClass.getDeclaredMethods();
70 for (Method m : methods2) {
71 System.out.println(m);
72 System.out.println(m.getName());
73 }
74 /**输出:
75 * 3.getMethods:获取自己以及基类所有方法
76 * public java.lang.String day08.Person.toString()
77 * toString
78 * public java.lang.String day08.Person.getName()
79 * getName
80 * public void day08.Person.setName(java.lang.String)
81 * setName
82 * private void day08.Person.privateMethod1()
83 * privateMethod1
84 * public void day08.Person.setAge(int)
85 * setAge
86 * public void day08.Person.publicMethod1()
87 * publicMethod1
88 * private void day08.Person.privateMethod2(java.lang.String)
89 * privateMethod2
90 * public void day08.Person.publicMethod2(java.lang.String)
91 * publicMethod2
92 * public int day08.Person.getAge()
93 * getAge
94 */
95
96 //4.getDeclaredMethod(String name, Class<?>... parameterTypes):根据参数获得任意方法
97 System.out.println("4.getDeclaredMethod(String name, Class<?>... parameterTypes):根据参数获得public方法");
98 //无参
99 Method method4 = personClass.getDeclaredMethod("privateMethod2", String.class);
100 //忽略访问权限修饰符的安全检查
101 method4.setAccessible(true);//暴力反射
102 Person p4 = new Person();
103 //执行方法
104 method4.invoke(p4, "这是有参的private方法");
105 /**输出:
106 * 4.getDeclaredMethod(String name, Class<?>... parameterTypes):根据参数获得public方法
107 * privateMethod2:参数为这是有参的private方法
108 */
109 }
110 }
获取类名:
1 public class ReflectDemo2 {
2
3 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
4
5 Class personClass = Person.class;
6
7 //获取类名
8 String className = personClass.getName();
9 System.out.println(className);
10 /**输出:
11 * day08.Person
12 */
13 }
14 }
5.案例:读取配置文件生成类执行方法
需求:不改变任何代码的前提下,可以帮我们创建任意类的对象,并执行其中的任意方法。
实现:
- 配置文件
- 反射
步骤:
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 创建对象
- 执行方法
目录配置:
主程序类:ReflectDemo2
public class ReflectDemo2 { public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //1.加载配置文件 //1.1创建Properties对象 Properties pro = new Properties(); //1.2加载配置文件,转换为一个集合 //1.2.1获取class目录下的配置文件 ClassLoader classLoader = ReflectDemo2.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("config.properties"); pro.load(is); //2.获取配置文件中定义的数据 String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); Class parameterType = Class.forName(pro.getProperty("parameterType")); //3.加载该类进内存 Class cls = Class.forName(className); //4.创建对象 Object obj = cls.newInstance(); //5.获取方法对象 Method method = cls.getDeclaredMethod(methodName,parameterType); method.setAccessible(true); //执行方法 method.invoke(obj,"执行私有方法"); } }
配置文件:config.properties
1 className=day08.Person 2 methodName=privateMethod2 3 parameterType=java.lang.String