Java 反射基本API

76 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看活动详情

最近碰到一个问题:数据库字段变更,所有字段都变为 not null。那么程序里面对应的插入/更新操作的代码就需要改变。需要对实体类进行初始化。

刚开始在方法内,我习惯性的想写一个公共方法 ,进入方法的时候把实体类传过去,初始化实体类的值。但是我发现我不知道怎么处理,虽然隐约感觉使用反射可以解决。但是不会啊。0.0

这就为学习反射埋下伏笔,借此总结一下反射的学习心得。(当然这里更好的方法是直接改实体类的代码)

反射概念

Java 的反射是指程序在运行期可以拿到一个对象的所有信息。也就是我们不用具体的 new 一个对象,而是在运行时期才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

Class 类

如何获取 Class

获取 Class 对象的三种方式:定义一个 Foo.java

class Foo{
	public String publicString;
    protected String protectedString;
    String defaultString;
    private String privateString;
    
    //创建了三个构造函数
    public Foo(){

    }

    private Foo(String publicString, String protectedString) {
        this.publicString = publicString;
        this.protectedString = protectedString;
    }

    public Foo(String publicString, String protectedString, String defaultString, String privateString) {
        this.publicString = publicString;
        this.protectedString = protectedString;
        this.defaultString = defaultString;
        this.privateString = privateString;
    }
    
    //下面省略 get/set 方法以及 toString() 方法
    
}
  • Class.forName("完整的包名"); //编译前
    • 最熟悉的就是加载数据库驱动,多用于配置文件
  • Foo.class:我们将 .java 文件编译之后,就是 .class 文件 //编译后
    • 一般用在作为参数传递
  • new Foo.getClass(); //运行时
    • 对象获取字节码方式

Class 常用方法

获取 Field 对象

  • getFields()
  • getDeclaredFields()
//Class 获取 Field 对象
//以 Class.foeName("") 方式获取 Class 对象
Class foo = Class.forName("com.practice.thinkinbasic.invoke.Foo");
Field[] fields = foo.getFields();  
for(Field f:fields){
    System.out.println(f);
}

//打印日志如下:
public java.lang.String com.practice.thinkinbasic.invoke.Foo.publicString

Field[] fields = foo.getDeclaredFields();
//打印日志如下:
public java.lang.String com.practice.thinkinbasic.invoke.Foo.publicString
protected java.lang.String com.practice.thinkinbasic.invoke.Foo.protectedString
java.lang.String com.practice.thinkinbasic.invoke.Foo.defaultString
private java.lang.String com.practice.thinkinbasic.invoke.Foo.privateString

getFields():只能获取 public 修饰的属性
getDeclaredFields():可以获取所有修饰符修饰的属性

//获取单个属性:
Field field = foo.getDeclaredField("privateString");  //正常获取
Field field1 = foo.getField("privateString");  //会报错:java.lang.NoSuchFieldException: privateString

设置 Field 对象的值

Foo f = new Foo();

//Class 获取 Field 对象
//以 Class.foeName("") 方式获取 Class 对象
Class foo = Class.forName("com.practice.thinkinbasic.invoke.Foo");

Field field = foo.getDeclaredField("publicString");
field.set(f,"1234");

Field field1 = foo.getDeclaredField("privateString");
field1.setAccessible(true); //暴力反射(必须加上才能给非 public 修饰的属性设置值)
field1.set(f,"4321");
System.out.println(f);

获取 Constructor 对象

getConstructors()         //只能获取 public 修饰的构造函数
getDeclaredConstructors() //能获取所有的构造函数

//获取单个 Constructor 对象在下面设置 Constructor 里面介绍

设置 Constructor 对象

//Class 获取 Constructor 对象
//这里的 c 是接受其他方法传递过来的 Class 对象
Constructor cons = c.getDeclaredConstructor(String.class,String.class);
cons.setAccessible(true);
Object obj=cons.newInstance("1234","4321");
System.out.println(obj);

//获取单个的构造函数需要指定参数列表(当你有多个参数的时候)


//这里就可以解答我最开始提出的问题,传一个 .class 参数到公共方法,然后使用 getDeclaredConstructor() 初始化这个实体类

获取 Method 对象

getMethods()         //获取自己和父类的所有 public 方法
getDeclaredMethods() //获取自己的所有方法,但是没有父类的

调用 Method 方法

//Class 获取 Method 对象
//这里的 foo 是使用对象.方法获取的。(这里只是演示,实际不可能这么写)
Foo f = new Foo();
Class foo = f.getClass();
Method methods = foo.getMethod("publicMethod");
//methods.setAccessible(true); 如果需要调用私有方法,则需要设置暴力反射
methods.invoke(f);

//就会调用 Foo 类下的 publicMethod() 方法

桥接方法

public interface SuperClass<T> {
    void method(T t);
}

class ziClass implements SuperClass<String> {
    @Override
    public void method(String o) {
        System.out.println("abc");
    }
}
1.先使用 javac SupperClass.java 生成 class 文件
2.javap -p ziClass.class 查看反编译后的信息

image.png 上面的 Object 入参的方法就是桥接方法。实际没有使用的。

isAssignableFrom

methodType.isAssignableFrom(getterType)
getterType 是不是 methodType 的子类或子接口

总结

对于一个类来说,无非就是 成员变量(Field)、成员方法(Method)、构造函数(Constructor) 三种。掌握这三种,基本就可以了解反射。

像 IDEA 开发工具中的方法提示,其实就是使用了反射的功能;Spring IOC/DI ; MyBatis 等等,更是反射的良好体现。

参考:www.bilibili.com/video/BV1C4…