java-反射机制

后端 / 笔记 / 2021-09-21

发生什么事了

反射机制,是在程序运行时动态加载类并获取类的详细信息,从而操作类的属性和方法。
使用反射机制,可以动态的获取当前calss信息,比如方法信息注解信息还有方法参数等等。

不讲武德

虽然反射机制很灵活,但是他有一定的资源开销,会一定程度上降低程序的性能.

基础知识

字段解释

  • Class 类实体
  • Field 类属性变量
  • Method 类方法
  • Constructor 类构造方法
  • getField,getMethod,getConstructor 可以获取指定方法的域,方法和构造器,不包括 private内容
  • getDeclated... 可以获取类全部域,方法和构造器,包括 private内容

创建反射的三种方式

  • Object.getClass()
Entity entity = new Entity();
Class<? extends Entity> class1 = entity.getClass();
  • ClassName.class
Class<Entity> class2 = Entity.class;
  • Class.forName() 推荐
Class<?> class3 = Class.forName("reflect.Entity");

Field对象

field对象是反射中的字段对象。

  • getName() 获取字段名称
  • getType() 获取字段类型
  • setAccessible() 设置访问权限
  • set(obj,val) 给对象设值
  • get(obj) 获取对象值

Method 对象

Method 对象是反射中的方法对象

  • getName 获取方法名称
  • getReturnType 获取返回值类型
  • invoke 用来执行对象方法

构造器操作

调用无参构造-newInstance()

Class<?> aClass = Class.forName("reflect.Entity");
// 调用无参构造函数初始化
Entity entity = (Entity) aClass.newInstance();
entity.sayHello();
你好,null

调用有参构造-getConstructor()

  1. getConstructor(TypeName.class)
  2. newInstance()
class Entity {
    public String str;
    private String name;

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

    Entity() {
    }

    public void sayHello() {
        System.out.println(String.format("你好,%s", this.name));
    }

    public void hello() {
        System.out.println("hello");
    }

    public int sum(int a, int b) {
        return a + b;
    }
}
// 获取有参构造
Constructor<?> constructor = aClass.getConstructor(String.class);
// 调用有参构造
Entity tom = ((Entity) constructor.newInstance("tom"));
tom.sayHello();
你好,tom

字段操作

获取指定公有字段-getField()

Class<?> aClass = Class.forName("reflect.Entity");
Entity entity = (Entity) aClass.newInstance();
Field str = aClass.getField("str");

// set 方法给字段设置值
str.set(entity, "publicStr");
// 输出值
System.out.println(entity.str);
publicStr

获取所有共有字段-getFields()

getFields()方法来获取所有公有字段。

Class<?> aClass = Class.forName("reflect.Entity");
for (Field field : aClass.getFields()) {
  System.out.println(field);
}

获取指定私有字段-getDeclaredField()

需要注意的是 私有字段访问需要调用 setAccessible()方法来指定权限

Class<?> aClass = Class.forName("reflect.Entity");
Entity entity = (Entity) aClass.newInstance();
Field name = aClass.getDeclaredField("name");
// 开启暴力反射 指定访问权限
name.setAccessible(true);
name.set(entity,"jerry");
entity.sayHello();
你好,jerry

获取所私有有字段-getDeclaredFields()

getFields() 只能获取 public 属性。
如果要获取 private 属性,则需要 getDeclaredFields() 方法。

Class<?> aClass = Class.forName("reflect.Entity");
for (Field declaredField : aClass.getDeclaredFields()) {
  System.out.println(declaredField);
}

方法调用

获取所有公有方法-getMethods()

Class<?> aClass = Class.forName("reflect.Entity");

for (Method method : aClass.getMethods()) {
  System.out.println(method);
}
public void reflect.Entity.sayHello()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

获取指定公有方法-getMethod()

通过getMethod()方法来获取一个方法的Method对象,然后通过invoke()来实现方法的调用。

Class<?> aClass = Class.forName("reflect.Entity");

Entity entity = (Entity) aClass.getConstructor(String.class).newInstance("tom");
Method method = aClass.getMethod("sayHello");
// 调用方法
method.invoke(entity);

你好,tom

获取指定公有方法-getMethod()

Class<?> aClass = Class.forName("reflect.Entity");
Entity entity = (Entity) aClass.getConstructor(String.class).newInstance("tom");
Method hello = aClass.getMethod("hello");
hello.invoke(entity);
hello

获取所有私有方法-getDeclaredMethods()

Class<?> aClass = Class.forName("reflect.Entity");

for (Method declaredMethod : aClass.getDeclaredMethods()) {
  System.out.println(declaredMethod);
}
public void reflect.Entity.sayHello()

获取指定私有方法-getDeclaredMethod()

Class<?> aClass = Class.forName("reflect.Entity");
Entity entity = (Entity) aClass.getConstructor(String.class).newInstance("tom");
Method sayHello = aClass.getDeclaredMethod("sayHello");
// 设置访问权限
sayHello.setAccessible(true);
sayHello.invoke(entity);
你好,tom

调用有参方法

Class<?> aClass = Class.forName("reflect.Entity");
Entity entity = (Entity) aClass.getConstructor(String.class).newInstance("tom");

// 调用有参方法
int result = ((int) aClass.getMethod("sum", int.class, int.class).invoke(entity,1, 2));
System.out.println(result);
3

黑魔法:通过反射越过泛型检查

原理,泛型检查属于编译期,而反射属于runtime所以可以越过泛型检查。

System.out.println("=======");
List<String> list = new ArrayList<>();
Class<? extends List> aClass = list.getClass();
Method add = aClass.getDeclaredMethod("add", Object.class);
add.invoke(list, 1);
add.invoke(list, "1");
add.invoke(list, 1.23);
for (Object s : list) {
  System.out.println(s);
}
=======
1
1
1.23