JAVA反射

1.简介

  • Java Reflection

    1. 反射机制允许程序在执行期借助于Reflection
      API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到

    2. 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射

image-20240129123321412

  • Java程序计算器的三个阶段

image-20240204155134537

2.简单案例

  • Cat.java
1
2
3
4
5
6
7
8
9
10
11
package com.gaomu.reflection.mode;

import java.io.IOException;

public class Cat {
private String name = "招财猫";
public void hi(){
System.out.println(name +": 汪汪!");
}
}

  • ReflectionQuestion.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.gaomu.reflection.question;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;

public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// Cat cat = new Cat();
// cat.hi();

Properties properties = new Properties();
properties.load(Files.newInputStream(Paths.get("src\\main\\resources\\re.properties")));
String classFullPath = properties.getProperty("classfullpath");
String methodName = properties.getProperty("method");
System.out.println(classFullPath);
System.out.println(methodName);
// 利用反射加载类
//1.加载类,返回Clas类型的对象cls
Class<?> cls = Class.forName(classFullPath);
//2.通过cls得到加载的类
Object o = cls.newInstance();
System.out.println("o的运行类型=" + o.getClass());
//3. 通过cls得到的加载类的方法对象
Method method1 = cls.getMethod(methodName);
//4.通过method1调用方法
method1.invoke(o);
}

}

  • re.properties
1
2
classfullpath=com.gaomu.reflection.mode.Cat
method=hi

3.JAVA反射方法调用

  • 可完成
    1. 在运行时判断任意一个对象所属的类
    2. 在运行时构造任意一个类的对象
    3. 在运行时得到任意一个类所具有的成员变量和方法
    4. 在运行时调用任意一个对象的成员变量和方法
    5. 生成动态代理
  • 反射相关类
    • java.lang.Class: 代表一个类,Class对象表示某个类加载后在堆中的对象
    • java.lang.reflect.Method: 代表类的方法
    • java.lang.reflect.Field: 代表类的成员变量
    • java.lang.reflect.Constructor: 代表类的构造方法
  • 演示
1
2
3
4
5
6
7
8
9
10
11
12
13
//获取类方法
Method method1 = cls.getMethod(methodName);
//获取类成员变量
//getField可以得到类得非私用属性
Field nameFiled = cls.getField("age");
System.out.println(nameFiled.get(o));

//Constructor代表类的构造方法
Constructor<?> constructor = cls.getConstructor();//()中指定构造器的参数类型,返回无参构造
System.out.println(constructor);//Cat()
//获取有参构造
Constructor<?> constructor2 = cls.getConstructor(String.class);//参数为String类型
System.out.println(constructor2);//Cat(String name)

4.Class类

  1. Class也是类,因此也继承Object类[类图]

  2. Class类对象不是new出来的,而是系统创建的[演示]

  3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次[演示]

  4. 每个类的实例都会记得自己是由哪个Class实例所生成

  5. 通过Class可以完整地得到一个类的完整结构,通过一系列API

  6. Class对象是存放在堆的

  7. 类的字节码二进制数据,是存放在方法区的,有的地方称为类的元数据(方法代码、变量名、方法名、访问权限等等)

4.1.Class类的常用方法

方法名 功能说明
static Class forName(String name) 返回指定类名name的Class对象
Object newInstance() 调用缺省构造函数,返回该Class对象的实例
getName() 返回此Class对象所有表示的实体(类、接口、数组、基本类型、)名称
Class [] getInterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的加载器
Class getSuperclass() 返回表示此Class所表示的实体的超类的Class
Constructor[] getConstructors() 返回包含某些Constructor对象的数组
Filed[] getDeclaredFields() 返回Field对象的数组
Method getMethod(String name,Class … paramTypes) 返回一个Method对象,此对象的形参类型为paramType
  • 简单demo

  • Class01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.gaomu.reflection.question;

import com.gaomu.reflection.mode.Car;

import java.lang.reflect.Field;

public class Class01 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
String classAllPath = "com.gaomu.reflection.mode.Car";
//1.获取Car类 对应的Class对象
Class<?> cls = Class.forName(classAllPath);
//2.输出cls
//显示cls对象,是哪个类的Class对象com.gaomu.reflection.mode.car
System.out.println(cls);
//输出cls运行类型 java.lang.Class
System.out.println(cls.getClass());
//3.得到包名
System.out.println(cls.getPackage().getName());
//4.得到全类名
System.out.println(cls.getName());
//5.通过cls对象创建实例
Car car = (Car)cls.newInstance();
System.out.println(car);
//6.通过反射获取属性brand
Field brand = cls.getField("brand");
System.out.println(brand.get(car));
//7.通过反射给属性复制
brand.set(car,"奔驰");
System.out.println(brand.get(car));
//8.得到所有属性(字段)
Field[] fields = cls.getFields();
for(Field f: fields) {
System.out.println(f.getName());
}
}

}

  • Car
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.gaomu.reflection.mode;

public class Car {
public String brand = "宝马";
public int price = 50000;
public String color = "白色";

@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
", color='" + color + '\'' +
'}';
}
}

4.2.获取Class对象的六种方式

  • GetClass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.gaomu.reflection.question;

import com.gaomu.reflection.mode.Car;

public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName
String classAllPath = "com.gaomu.reflection.mode.Car";
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);

//2.类名,class, 用于参数传递
Class<?> cls2 = Car.class;
System.out.println(cls2);

//3.对象.getClass(),有对象实例
Car car = new Car();
Class<? extends Car> cls3 = car.getClass();
System.out.println(cls3);

//4.通过类加载器【4种】来获取到类的Class对象
//先得到加载器car
ClassLoader classLoader = car.getClass().getClassLoader();
//通过加载器得到Class对象
Class<?> cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
//cls1-4为同一个对象
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());

//5.基本数据类型按如下方式得到Class类对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass);
System.out.println(characterClass);
System.out.println(booleanClass);

//6.基本数据类型对应的包装类,可以通过.TYPE获取到Class类对象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE;
System.out.println(type1);
System.out.println(type2);

}
}

4.3.哪些类型有Class对象

  • 如图

image-20240205104245456

  • AllTypeClass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.gaomu.reflection.question;

import java.io.Serializable;

public class AllTypeClass {
public static void main(String[] args) {
Class<String> cls1 = String.class;//外部类
Class<Serializable> cls2 = Serializable.class;//接口
Class<Integer[]> cls3 = Integer[].class;//数组
Class<float[][]> cls4 = float[][].class;//二维数组
Class<Deprecated> cls5 = Deprecated.class;//注解
Class<Thread.State> cls6 = Thread.State.class;//枚举
Class<Long> cls7 = long.class;//基本数据类型
Class<Void> cls8 = void.class;//void数据类型
Class<Class> cls9 = Class.class;
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
System.out.println(cls5);
System.out.println(cls6);
System.out.println(cls7);
System.out.println(cls8);
System.out.println(cls9);

}
}

5.类加载

  • 动态加载在运行阶段加载类,依赖性低。
  • 静态加载在编译阶段加载类,依赖性高。
  • 类加载的三个阶段
    • 加载
    • 连接
    • 初始化

image-20240205121608065

image-20240205121959549

  1. 加载阶段:JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代码该类的java.lang.Class对象。

  2. 连接阶段:

    1. 验证
      1. 目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并不会危害虚拟机自身的安全。
      2. 包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
      3. 可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
    2. 准备
      1. jvm会在该阶段对静态变量,分配内存并默认初始化(对数据类型的默认初始值,如0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配
    3. 解析
      1. 虚拟机将常量池内的符号引用替换为直接引用的过程。
  3. 初始化阶段

    1. 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行()方法的过程。
    2. ()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
    3. 虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。

image-20240205123952928

  • 直接使用类的静态属性会导致类的加载。

6.Class反射API调用获取类信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package com.gaomu.reflection.api;


import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.spec.RSAOtherPrimeInfo;

public class ReflectionUtils {
public static void main(String[] args) throws ClassNotFoundException {
//得到class对象
Class<?> personCls = Class.forName("com.gaomu.reflection.api.Person");
//获取本类种所有的属性
//默认修饰符为0,public 1,private 2, protected 4, static 8, final 16。
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("获取本类种所有的属性: " + declaredField.getName()
+ " 该属性的修饰符值: " + declaredField.getModifiers()
+ " 该属性的类型: " + declaredField.getType());
}

//获取本类所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类中所有方法: " + declaredMethod.getName()
+ " 该方法的访问修饰符:" + declaredMethod.getModifiers()
+ " 该方法的返回值类型: " + declaredMethod.getReturnType());

//输出当前方法的形参数组情况
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该方法的形参类型:" + parameterType);
}
}

//获取本类中所有的构造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
System.out.println("获取本类中所有的构造器: =========");
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());

Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该构造器的形参类型: " + parameterType);
}
}

}
public static void main2(String[] args) throws ClassNotFoundException {
//得到class对象
Class<?> personCls = Class.forName("com.gaomu.reflection.api.Person");
//获取全类名
System.out.println("全类名: " + personCls.getName());
//获取简单类名
System.out.println("获取简单类名: " + personCls.getSimpleName());
//获取所有的public修饰的属性,包含本类已经父类
Field[] fields = personCls.getFields();
System.out.println("本类即父类属性: ");
for (Field field : fields) {
System.out.println(field.getName());
}
//获取本类种所有的属性
Field[] declaredFields = personCls.getDeclaredFields();
System.out.println("获取本类种所有的属性:");
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName());
}
//获取所有public修饰的方法,包含本类以及父类的
Method[] methods = personCls.getMethods();
System.out.println("获取所有public修饰的方法,包含本类以及父类的: ");
for (Method method : methods) {
System.out.println(method.getName());
}
//获取本类种的所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
System.out.println("获取本类种的所有方法: ");
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
//获取所有public修饰的构造器,包含本类
Constructor<?>[] constructors = personCls.getConstructors();
System.out.println("获取所有public修饰的构造器,包含本类: ");
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName());
}
//获取本类中所有的构造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
System.out.println("获取本类中所有的构造器: ");
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}
//返回包信息
Package aPackage = personCls.getPackage();
System.out.println("返回包名: " + aPackage.getName());
//以class形式返回父类信息
System.out.println("返回父类信息: ");
Class<?> superclass = personCls.getSuperclass();
System.out.println(superclass.getName());
//以Class[]形式返回接口信息
Class<?>[] interfaces = personCls.getInterfaces();
System.out.println("返回接口名: ");
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface.getName());
}
//返回注解信息
Annotation[] annotations = personCls.getAnnotations();
System.out.println("返回注解名:");
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}

}

class A {
public String hobby;

public void hi() {

}
public A() {

}

}

interface IA {

}

interface IB {

}

@Deprecated
class Person extends A implements IA, IB {
//属性
public String name;
protected int age;
String job;
private double sal;

//构造器
public Person () {

}
public Person (String name) {

}

private Person (String name, int age) {

}
//方法
public void m1(String name, int age) {

}

protected void m2() {

}

void m3() {

}
private void m4() {

}

}

7.反射实操

7.1.通过反射创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.gaomu.reflection.question;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflecCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.获取User类的Class对象
Class<?> userClass = Class.forName("com.gaomu.reflection.question.User");
//2.通过public的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println(o);
//3.通过public的有参构造器创建实例
Constructor<?> constructor = userClass.getConstructor(String.class);
Object user1 = constructor.newInstance("takake");
System.out.println("gao: " + user1);
//4.通过非public的有参构造器创建实例
Constructor<?> declaredConstructor = userClass.getDeclaredConstructor(int.class, String.class);
declaredConstructor.setAccessible(true);
Object user2 = declaredConstructor.newInstance(100, "雪怪");
System.out.println("xueguai: " + user2);
}

}

class User {
private int age = 50;
private String name = "gaomu";

public User() {

}
public User(String name) {

}
private User(int age, String name) {
this.age = age;
this.name = name;
}

@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}


7.2.通过反射访问类属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.gaomu.reflection.question;

import java.lang.reflect.Field;

public class ReflecAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.得到Student类对象的Class对象
Class<?> stuClass = Class.forName("com.gaomu.reflection.question.Student");
//2.创建对象
Object o = stuClass.newInstance();
System.out.println(o.getClass());
//3.使用反射得到age属性对象
Field age = stuClass.getField("age");
age.set(o, 88);//反射操作属性
System.out.println(o);
//4.操作私有静态属性
Field name = stuClass.getDeclaredField("name");
name.setAccessible(true);
name.set(null, "takake");//static属性才能操作空,否则传入对象
System.out.println(o);
System.out.println(name.get(null));
}
}

class Student {
public int age;
private static String name;
public Student() {

}

@Override
public String toString() {
return "Student{" +
"age=" + age + ",name=" + name +
'}';
}
}

7.3.通过反射操作方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.gaomu.reflection.question;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflecAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.得到Boss类对应的Class对象
Class<?> bossClass = Class.forName("com.gaomu.reflection.question.Boss");
Object o = bossClass.newInstance();
//3. 调用public的hi方法对象
Method hi = bossClass.getMethod("hi", String.class);
hi.invoke(o, "gaomu");
//4.调用private static 方法
//得到say方法对象
Method say = bossClass.getDeclaredMethod("say", int.class, String.class, char.class);
say.setAccessible(true);
//因为say方法是static的,可以直接传入null,否则 需要传入对象
System.out.println(say.invoke(null, 100, "薛宝坤", 'm'));
//5.在反射中,如果方法有返回值,统一返回Object
Object reVal = say.invoke(null, 300, "王八", 'w');
System.out.println("reVal的运行类型为:" + reVal.getClass());
}
}

class Boss {
public int age;
private static String name;
public Boss() {

}

private static String say(int n, String s, char c) {
return n + " " + s + " " + c;
}

public void hi(String s) {
System.out.println("hi " + s);
}
}