Java 中的反射机制

反射是什么?

反射(reflection):指的是在运行时通过反射 API 获取类的内部信息,并能直接操作任何对象的属性和方法。

当一个类被加载之后,这个类的信息保存在 Java 虚拟机的方法区中,这个类的对象实例则保存在虚拟机的堆中,通过方法区获取并操作一个类,就像是透过一面镜子反射到堆中的对象。JDK 提供了 java.lang.Class 类来支持反射机制。

java.lang.Class 类的理解

在 Java 中万事万物皆对象,我们创建的类本身也是一个对象。Java 程序经过 javac 命令编译后生成一个或多个字节码文件(.class),一个字节码文件对应唯一的一个类或接口的定义信息;接着使用 java 命令对某个字节码文件进行解释运行(类加载),相当于将这个字节码文件加载到内存中,我们将它叫做运行时类,这个运行时类 实际上是一个 java.lang.Class 对象。

也就是说,一个 Class 实例就对应一个运行时类。加载到内存中的运行时类会被缓存一段时间,在这段时间内我们可以通过不同的方法获取这个运行时类,那么如何获取这个运行时类,即 Class 实例呢?

获取 Class 实例的方法
// 方式一:调用运行时类的 .class 属性
Class clazz1 = Student.class; // Student 是我们自定义的类

// 方式二:调用运行时类的 getClass 方法
Student s = new Student();
Class clazz2 = p1.getClass();

// 方式三:调用 Class 类的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.example.java.Student");    // 该种方式用得多
有哪些数据类型能获取 Class 实例?

对象,接口,数组,枚举类,注解,基本数据类型,void 类型,Class 类本身。

通过反射操作运行时类

  1. 创建运行时类的对象:
// 通过newInstance()方法(运行时类必须存在可见的空参构造函数)
Class clazz = Student.class;
Student s = clazz.newInstance();        // Class使用了泛型,所以newInstance()不用强转类型了
  1. 获取运行时类的属性(Fields):
Class clazz = Student.class;

// getFields():获取当前运行时类及其父类中声明为public的属性
Field[] fields = clazz.getFields();

// getDeclaredFields():获取当前运行时类的所有属性(不包括父类的属性)
Field[] declaredFields = clazz.getDeclaredFields();
  1. 获取运行时类的方法(Methods):
Class clazz = Student.class;
Method[] methods = clazz.getMethods();
Method[] declaredMethods = clazz.getDeclaredMethods();

什么时候使用反射?

反射提供了一种动态性,即在运行时才获取类信息,创建类对象,操作对应的方法。比如一个网站的前后端,后端服务器正在运行,此时前端用户传来一个登录请求,那么后端就创建对应的对象来完成登录相关操作,因为此时后端已经在运行了,所以不能通过 new 来创建对象,要通过反射机制来创建。

例如 Spring 中我们配置 bean:

当我们在 XML 中配置了 bean 后,Spring 便会在启动时用反射机制创建对应的对象。

反射和封装是不是矛盾的?

不矛盾,将类的某些方法设置为私有权限时,意思是告诉其他类不要调用这些方法,使用其他方法就能满足需求了,这体现了封装性;而反射只是提供了一种机制,使得我们能够访问类的私有方法,但是原则上不建议调用这些私有方法。

参考资料:

  1. 尚硅谷 Java 入门
  2. 大白话说Java反射:入门、使用、原理

你可能感兴趣的:(java)