反射
# 反射
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能成为
java
的反射机制。在编译后产生的字节码文件时,类加载器子系统通过二进制字节流,负责从文件系统加载
class
文件。在执行java.exe
程序的时候,将字节码文件读入 JVM 中 --> 这个过程叫类的加载。然后在内存中对应创建一个
java.lang.Class
对象 -> 这个对象会被放入字节码信息中,这个Class
对象,就对应加载那个字节码信息,这个对象被作为程序访问方法区中的这个类的各种数据的外部接口。所以,通过这个对象看到的类的结构,我们形象的称反射这种“看透”类的能力,被称之为"introspection"(内省、内观、反省)。
提示
说明:在运行期间,如果我们要产生某个类的对象,Java 虚拟机(JVM)会检查该类型的 Class 对象是否已被加载。如果没有被加载,JVM 会根据类的名称找到.class
文件并加载它。一旦某个类型的Class
对象已被加载到内存,可以用它来产生该类型的所有对象。
案例: 美团外卖 -> 付款 -> 选择支付 -> 要么微信支付,要么支付宝支付
美团支付接口
// 接口的制定方:美团外卖
public interface Mtwm {
// 在线支付
void payOnline();
}
2
3
4
5
别的支付实现方法
public class WeChat implements Mtwm {
@Override
public void payOnline() {
System.out.println("我已经点了外卖,使用了微信支付");
}
}
public class AliPay implements Mtwm {
@Override
public void payOnline() {
System.out.println("我也点了外卖,使用了支付宝支付");
}
}
public class BankCard implements Mtwm {
@Override
public void payOnline() {
System.out.println("我也点了外卖,使用了招商银行卡支付");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
测试类
public class Test {
public static void main(String[] args) {
// 定义一个字符串,用来模拟前台的支付方式
String str = "微信";
if ("微信".equals(str)) {
pay(new WeChat());
}
if ("支付宝".equals(str)) {
pay(new AliPay());
}
if ("招商银行".equals(str)) {
pay(new BankCard());
}
}
public static void pay(WeChat wc) {
wc.payOnline();
}
public static void pay(AliPay aliPay) {
aliPay.payOnline();
}
public static void pay(BankCard bankCard) {
bankCard.payOnline();
}
}
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
使用多态优化
public class Test {
public static void main(String[] args) {
// 定义一个字符串,用来模拟前台的支付方式
String str = "微信";
if ("微信".equals(str)) {
pay(new WeChat());
}
if ("支付宝".equals(str)) {
pay(new AliPay());
}
if ("招商银行".equals(str)) {
pay(new BankCard());
}
}
// 方法的形参是接口,具体传入的是接口实现类的对象
public static void pay(Mtwm mt) {
mt.payOnline();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
提示
多态确实可以提高代码的扩展性,但是扩展性没有提高到最好,上面这一块,如果支付方式越来越多,或者某一个不继续使用了,添加分支和删除分支,都是一个不好的操作。
解决办法:反射机制
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 这个是我的包的地址下的WeChat类
String str = "com.wx.virus.test01.WeChat";
// 利用反射
Class<?> clzz = Class.forName(str);
Object o = clzz.newInstance();
Method method = clzz.getMethod("payOnline");
method.invoke(o);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# Class 类
将具体的实例向上抽取形成一个”类“的,这个类就是
java.lang.Class
,可以通过Class
类的字节码信息可以获取对象内容,以获取方法吗和属性等。
想要获取属性:Class 类->得到具体的实例(对象)->通过对象得到属性、方法、构造器
# 获取字节码信息的四种方式
先定义几个类
public class Person {
private int age;
public String name;
private void eat() {
System.out.println("person eat");
}
public void sleep() {
System.out.println("person sleep");
}
}
public class Student extends Person {
private int sno;
double height;
protected double weight;
public double score;
public String showInfo() {
return "我是一名三号学生";
}
private void work() {
System.out.println("我是程序员");
}
public Student() {
}
public Student(int sno) {
this.sno = sno;
}
Student(int sno, double weight) {
this.sno = sno;
this.weight = weight;
}
}
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
# 通过getClass
方法获取
public class Test {
public static void main(String[] args) {
// 获取Person的字节码信息
// 方式1 通过getClass 方法获取
Person p = new Person();
Class<? extends Person> pClass = p.getClass();
System.out.println(pClass);
}
}
// 输出
class com.wx.virus.test02.Person
2
3
4
5
6
7
8
9
10
11
12
13
# 通过内置 class 属性
public class Test {
public static void main(String[] args) {
// 获取Person的字节码信息
// 方式2 通过内置的 class 属性
Class c2 = Person.class;
System.out.println(c2);
}
}
2
3
4
5
6
7
8
提示
System.out.println(pClass == c2); // true
方式 1 和方式 2 不常用
# 通过 Class.forName("字符串")方法获取(用的最多)
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// 方式3
Class<?> c3 = Class.forName("class com.wx.virus.test02.Person");
}
}
2
3
4
5
6
# 利用类的加载器(了解技能点)
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// 方式4 通过类加载器
ClassLoader classLoader = Test.class.getClassLoader();
Class<?> c4 = classLoader.loadClass("class com.wx.virus.test02.Person");
}
}
2
3
4
5
6
7
# Class 类的具体的实例
- 类:外部类、内部类
- 接口
- 注解
- 数组
- 基本数据类型
- void
public class Test {
public static void main(String[] args) {
Class<Comparable> c5 = Comparable.class;
Class c6 = Override.class;
int[] arr1 = {1, 2, 3};
Class c7 = arr1.getClass();
int[] arr2 = {5, 6, 7};
Class c8 = arr2.getClass();
System.out.println(c7 == c8); // true
Class c9 = int.class;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@MyAnnotation("hello")
public class Student extends Person implements MyInterface {
private int sno;
double height;
protected double weight;
public double score;
@MyAnnotation("showInfo")
public String showInfo() {
return "我是一个学生";
}
public String showInfo(int a, int b) {
return "重载,我是一个学生";
}
void happy() {
System.out.println("开心最重要");
}
@Override
@MyAnnotation("student my method")
public void myMethod() throws RuntimeException {
System.out.println("student my method");
}
protected int getSno() {
return sno;
}
public Student() {
}
private Student(int sno) {
this.sno = sno;
}
protected Student(int sno, double weight) {
this.sno = sno;
this.weight = weight;
}
private void work() {
System.out.println("学生的工作是学习");
}
@Override
public String toString() {
return "Student{" +
"sno=" + sno +
", height=" + height +
", weight=" + weight +
", score=" + score +
'}';
}
}
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
public class Person implements MyInterface {
public String name;
@Override
public void myMethod() {
System.out.println("重写方法");
}
}
2
3
4
5
6
7
8
9
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
2
3
4
5
6
7
8
9
10
11
12
public interface MyInterface {
// 随便定义一个抽象方法
void myMethod();
}
2
3
4
# 获取构造器和创建对象
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) {
// 获取字节码信息
Class<Student> studentClass = Student.class;
// 通过字节码信息获取构造器
// 获取所有的构造器
// getConstructors 只能获取当前运行时类被public修饰的构造器
Constructor<?>[] studentClassConstructors = studentClass.getConstructors();
for (Constructor<?> studentClassConstructor : studentClassConstructors) {
System.out.println(studentClassConstructor);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public com.wx.virus.test03.Student()
提示
getConstructors
只能获取当前运行时类被 public 修饰的构造器
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) {
// 获取字节码信息
Class<Student> studentClass = Student.class;
// getDeclaredConstructors 获取运行时的全部的构造器
Constructor<?>[] declaredStudentConstructors = studentClass.getDeclaredConstructors();
for (Constructor<?> declaredStudentConstructor : declaredStudentConstructors) {
System.out.println(declaredStudentConstructor);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
private com.wx.virus.test03.Student(int)
protected com.wx.virus.test03.Student(int,double)
public com.wx.virus.test03.Student()
2
3
提示
getDeclaredConstructors 获取运行时的全部的构造器
剩下的获取方法
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取字节码信息
Class<Student> studentClass = Student.class;
// 获取指定的构造器
Constructor<Student> studentClassConstructor = studentClass.getConstructor();
// 什么都不传,得到一个空构造器
System.out.println(studentClassConstructor);
System.out.println("--------------------------");
// 得到两个参数的有参构造器
Constructor<Student> constructor2Parameter = studentClass.getConstructor(double.class, double.class);
System.out.println(constructor2Parameter);
System.out.println("--------------------------");
// 得到一个参数的有参构造器,private修饰
Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);
System.out.println("--------------------------");
// 有了构造器之后就可以创建对象了
Student student1 = studentClassConstructor.newInstance();
System.out.println("空构造器: " + student1);
System.out.println("--------------------------");
Student student2 = constructor2Parameter.newInstance(180.5, 170.6);
System.out.println("两个参数构造器: " + student2);
}
}
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
# 获取属性和对属性进行赋值
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Demo {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Class<Student> studentClass = Student.class;
// getFields 获取运行时类和父类的public修饰的属性
Field[] allFields = studentClass.getFields();
for (Field allField : allFields) {
System.out.println(allField);
}
System.out.println("------------------------------------");
// getDeclaredFields 获取是运行时类中的所有属性
Field[] declaredFields = studentClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("------------------------------------");
// 获取指定属性
Field score = studentClass.getField("score");
System.out.println(score);
Field sno = studentClass.getDeclaredField("sno");
System.out.println(sno);
System.out.println("------------------------------------");
// 属性的具体结构
// 获取修饰符
int modifiers = sno.getModifiers(); // 2 对应的是private
System.out.println(Modifier.toString(modifiers));
System.out.println(modifiers);
// 获取属性的数据类型
Class<?> type = sno.getType();
System.out.println(type.getName());
// 获取属性的名字
String name = sno.getName();
System.out.println(name);
System.out.println("------------------------------------");
// 给属性赋值
Student student = studentClass.newInstance();
// 给student这个对象的score 属性设置具体的值 这个值为 98
score.set(student, 98);
System.out.println(student);
}
}
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
# 获取方法和调用方法
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 获取字节码信息
Class<Student> studentClass = Student.class;
// 获取方法
// getMethods 获取运行时类的方法和所有的父类的方法且都是被public修饰的
Method[] methods = studentClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("=================================");
// getDeclaredMethods 只能获取运行时类的所有方法
Method[] declaredMethods = studentClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("=================================");
// 获取指定方法
Method showInfo1 = studentClass.getMethod("showInfo");
System.out.println(showInfo1);
Method showInfo2 = studentClass.getMethod("showInfo", int.class, int.class);
System.out.println(showInfo2);
// 获取private修饰的方法
Method work = studentClass.getDeclaredMethod("work");
System.out.println(work);
System.out.println("=================================");
// 获取方法的具体的结构
System.out.println(work.getName());
// 修饰符
int modifiers = work.getModifiers();
System.out.println(Modifier.toString(modifiers));
// 返回值
Class<?> returnType = work.getReturnType();
System.out.println(returnType);
// 参数列表
Class<?>[] parameterTypes = work.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
System.out.println("=================================");
// 获取注解
// 运行时的注解可以获取
Method myMethod = studentClass.getMethod("myMethod");
Annotation[] annotations = myMethod.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
System.out.println("=================================");
// 获取异常
Class<?>[] exceptionTypes = myMethod.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
System.out.println("=================================");
// 调用方法
Student student = studentClass.newInstance();
myMethod.invoke(student); // 调用 student 对象的 myMethod 方法
System.out.println(showInfo2.invoke(student, 12, 45));
}
}
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
# 获取类的接口、所在包、注解
import java.lang.annotation.Annotation;
public class Majaro {
public static void main(String[] args) {
Class<Student> studentClass = Student.class;
// 获取运行时类的接口
Class<?>[] interfaces = studentClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
// 得到父类的接口
// 先得到父类的字节码信息
Class<? super Student> superclass = studentClass.getSuperclass();
// 得到父类接口
Class<?>[] interfaces1 = superclass.getInterfaces();
for (Class<?> aClass : interfaces1) {
System.out.println(aClass);
}
// 获取运行时类所在的包
Package aPackage = superclass.getPackage();
System.out.println(aPackage);
System.out.println(aPackage.getName()); // 包名
// 获取运行时类的注解
Annotation[] annotations = studentClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
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
# 反射的面试题
# 1. 创建 Person 对象,以后用 new Person()创建,还是用反射创建
(问题转换:即什么时候用反射?)
- 用于某些模块集成场合
- 当不能在开发时即得到其目标类完整接口定义,只能根据命名规则去进行集成时
- 做基础框架的时候会用上
- 用于扩展性功能的时候
# 2. 反射是否破坏了面向对象的封装性
封装性是指对外隐藏对象的属性和实现细节,仅对外提供公共的访问方式。反射是通过对象找到类,既然找到类了,那么我们就可以得到这个类的成员结构了,例如这个类的属性和方法,即使是 private 的也能得到,你想,现在这个类我都得到了,那么这个类中的所以东西我肯定是都得到了,我现在只是得到了这个类的成员,并没有说是在外部访问这个类的 private 的东西。这并没有破坏面向对象的封装性。
private 修饰的东西,只是程序上告诉你建议你别用,但是拦不住你用反射可以获取到这些属性进行修改,简单来说,如果你一定要破坏,还是能破坏。就像女厕所上贴着只有女的可以进,但是总有"女装大佬"会进去,你拦得住吗?