【学习笔记】Java反射

作为一门静态语言,反射机制让Java有了准动态的特性。

静态与动态

作为一个从2017年进入大学并开始学习计算机科学的学生,在我入门计算机的时代,中文互联网上使用广泛的著名语言有:C、C++、Java、Python等。我很早就知道了前三者属于静态语言,而Python属于动态语言。但是很久之后,主观上只觉得作为动态语言的Python写起来更加方便,并没有真正理解动静态语言的本质区别。而理解其中真正的区别,是很有必要的。

动态语言:动态语言在运行时代码可以根据某些条件改变自身结构。

静态语言:运行时结构不可变的语言。

反射与动静态:反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。反射可以实现动态创建对象和编译,体现出很大的灵活性。但是反射对性能有影响。

正常编程方式与反射的区别

方式使用过程
正常方式引入需要的“包类”名称 -> 通过new实例化 -> 取得实例化对象
反射方式实例化对象 -> getClass()方法 -> 得到完整的“包类”名称

初试Reflection API

单看概念还是过于晦涩难懂,不如用代码来尝试一下反射的基础使用。

我们以如下POJO类为例:

package main.java.reflection;

//User类
class User {
  private String name;
  private int id;
  private int age;

  //无参和有参构造方法
  public User() {
  }

  public User(String name, int id, int age) {
    this.name = name;
    this.id = id;
    this.age = age;
  }
    
  //get方法
  public String getName() {
    return name;
  }

  public int getId() {
    return id;
  }

  public int getAge() {
    return age;
  }

  //set方法
  public void setName(String name) {
    this.name = name;
  }

  public void setId(int id) {
    this.id = id;
  }

  public void setAge(int age) {
    this.age = age;
  }

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

利用API获取包类:

public class RTest1 {
  public static void main(String[] args) throws ClassNotFoundException {
    Class c1 = Class.forName("main.java.reflection.User");
    System.out.println(c1);
  }
}

运行结果:

class main.java.reflection.User

以上只是Java反射的一个小小使用,反射机制提供的功能远不止于此。

获取类的运行时结构

我们接着用代码来尝试获取类的运行时结构:

User user = new User();
Class c1 = user.getClass();

//获得类的名字
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());

//获得类的属性
Field[] fields = c1.getFields(); //只能找到public属性

fields = c1.getDeclaredFields(); //找到全部属性
for (Field field : fields) {
  System.out.println(field);
}

//获取指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);

//获得类的方法
Method[] methods = c1.getMethods();//获得本类及其父类的全部public方法
for (Method method : methods) {
  System.out.println("getMethods:" + method);
}

methods = c1.getDeclaredMethods();//或得本类的所有方法
for (Method method : methods) {
  System.out.println("getDeclaredMethods:" + method);
}

结合上述的示例,我们对利用Java反射获取属性、方法、构造器的名称、修饰符等。

动态创建对象执行方法

到目前为止,我们见识了利用反射方法获取Class对象的方式。那么,获取到的这些Class对象能做什么呢?

依然用代码示例来介绍:

//获得Class对象
Class c1 = Class.forName("main.java.reflection.User");

//构造一个对象
User user = (User)c1.newInstance();//本质上是调用了类的无参构造器
System.out.println(user);

//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
User user1 = (User)constructor.newInstance("庆庆",000,22);
System.out.println(user1);

//通过反射调用普通方法
User user2 = (User) c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user2,"庆庆");
System.out.println(user2);

//通过反射操作属性
User user3 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");

//不能直接操作私有属性,需要关闭程序的安全检测,属性或方法的setAccessible(true)
name.setAccessible(true);
name.set(user3,"庆庆1");
System.out.println(user3);

获取泛型信息

待更新ing

获取注解信息

待更新ing

最后修改:2020 年 10 月 24 日 05 : 43 PM