参考文档—-廖雪峰的Java教程
参考教学视频—-黑马Java进阶教程,轻松掌握Java反射原理
本文所有字节码文件对象都命名为cls,获取全部的是以数组的形式

概念

  • 介绍:反射就是Reflection,Java的反射是指允许对字段(成员变量),成员方法和构造方法的信息进行编程访问
  • 目的:反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。

    idea中的代码提示就是一种反射

  • 获取
    1. 字段(成员变量)-获取修饰符、获取名字、获取类型、赋值/获取值
    2. 构造方法-获取修饰符、获取名字、获取形参、创建对象
    3. 成员方法-获取修饰符、获取名字、获取形参、获取返回值、抛出的异常、获取注解、运行方法

获取Class对象

  1. 直接通过一个Class的静态变量class获取:

    一般更多时当作参数进行传递

1
Class cls = String.class;
  1. 如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:

    当我们已经有这个类对象才使用

1
2
String s = "Hello";
Class cls = s.getClass();
  1. 如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:

    最常用的

1
2
3
Class cls = Class.forName("全类名");
Class cls = Class.forName("java.lang.String"); //eg
// 全类名=包名+类名

反射获取构造方法

  1. 获取构造方法

    获取单个的时候保持字节码和构造方法中的参数一一对应

1
2
3
4
Constructor cons  = cls.getConstructor(???.class);//获取某个`public`的`Constructor`;
Constructor cons = cls.getDeclaredConstructor(???.class);//获取某个`Constructor`;
Constructor[] cons = cls.getConstructors();//获取所有`public`的`Constructor`;
Constructor[] cons = cls.getDeclaredConstructors();//获取所有`Constructor`。
  1. 用法:newInstance(Object... parameters)

    用法具体见暴力反射

反射获取成员变量

  1. 获取成员变量
1
2
3
4
Field field = cls.getField(String name);//根据字段名获取某个public的field(包括父类)
Field field = cls.getDeclaredField(String name);//根据字段名获取当前类的某个field(不包括父类)
Field[] fields = cls.getFields();//获取所有public的field(包括父类)
Field[] fields = cls.getDeclaredFields();//获取当前类的所有field(不包括父类)
  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
// 运行上述代码,打印的name字段从Xiao Ming变成了Xiao Hong,说明通过反射可以直接修改字段的值。
import java.lang.reflect.Field;

public class Main {

public static void main(String[] args) throws Exception {
Person p = new Person("Xiao Ming");
System.out.println(p.getName()); // "Xiao Ming"
Class c = p.getClass();
Field f = c.getDeclaredField("name");
f.setAccessible(true);//参考暴力反射
f.set(p, "Xiao Hong");
System.out.println(p.getName()); // "Xiao Hong"
}
}

class Person {
private String name;

public Person(String name) {
this.name = name;
}

public String getName() {
return this.name;
}
}
  1. 用法:getName(),getType(),getModifiers()

    具体参考成员方法的用法

反射获取成员方法

  1. 获取方法
1
2
3
4
Method method = cls.getMethod(name,???.class);//获取某个public的Method(包括父类)
Method method = cls.getDeclaredMethod(name,???.class);//获取当前类的某个Method(不包括父类)
Method[] methods = cls.getMethods();//获取所有public的Method(包括父类)
Method[] methods = cls.getDeclaredMethods();//获取当前类的所有Method(不包括父类)
  1. 用法
    • 获取修饰符
    1
    int modifiers = method.getModifiers();
    • 获取名字
    1
    String name = method.getName();
    • 获取形参
    1
    Parameter[] parameters = method.getParameters();
    • 获取返回值
    1
    2
    3
    4
     ```
    - 获取抛出的异常
    ```java
    Class[] exceptionTypes = method.getExcptionTypes();
  • 方法运行->Student默认是已经创建好的类,eat是其中的private成员方法
    1
    2
    3
    4
    Student s  = new Student();
    method.setAccessible(true);
    method.invoke(s,"含八嘎");
    Obkect result =method.invoke(s,"含八嘎");//如果有返回值

暴力反射

eg,获取的学生构造函数是private修饰的,此处以构造方法为案例

1
2
3
4
Constructor con = cls.getDeclaredConstructor(String.class,int.class);
con.setAccessible(true);//临时取消权限校验
Student stu = (Student) con.newInstance("zhangsan",23);
System.out.println(stu);

动态代理

分析

  • 特点:无侵入式的给代码添加额外的功能
  • 如何保证代理:通过接口,后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法

实现

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}

interface Hello {
void morning(String name);
}
  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
  3. 使用的ClassLoader,通常就是接口类的ClassLoader;
  4. 需要实现的接口数组,至少需要传入一个接口进去;
  5. 用来处理接口方法调用的InvocationHandler实例。
  6. 将返回的Object强制转型为接口。