Java工程师工作中会需要用到反射,本文从原理和使用场景出发,讲解下什么是反射。
在《Java编程思想》中,反射只有极小的篇幅介绍,在334页有“反射:运行时的类信息”这样一个小章节。书中这样描述:
在使用IDE构建项目时,可以通过代表不同组件的图标拖拽到表单中创建程序,然后在编程时通过设置构建的属性值来配置它们。这种设计时要求构件都是可实例化的,而且要暴露其部分信息,以允许程序员读取好修改构件的属性。
反射提供了一种机制-用来检查可用的方法,并返回方法名。
人们想要在运行时获取类的新的另一个动机,是希望提供跨网络的远程平台上创建对象的能力,这被称为RMI。
Class类和java.lang.reflect类库对反射的概念进行了支持,该类库包含了Field,Method,Constructor类,这样可以用各种方便的API获取类的变量,调用方法。这样匿名对象的类信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。
重要的是,要认识到反射机制并没有什么神奇之处,当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类,用它做其他事情之前必须先加载.class对象,要么在本地机器上,要么通过网络获取。
简单摘取了部分内容,我认为书中强调的特性是反射提供了获取运行时类信息的能力,无论是本地加载的类,还是网络传输过来的外部类。在程序运行时,可能还不知道真实操作的类是什么样子,这些都是在运行时才知道的,而非编译时就已经确定的。
在工作中常用的场景也是各异的,可能已经知道了类是什么样子的结构,用反射可能是为了更优雅的获取类的方法,参数,不需要硬编(但是可能损失了性能,反射消耗CPU资源较多),也可能不知道具体是类或者类的实例传了过来,想获得注解或者某个参数。
在Baeldung的反射教程中,也举了一个例子,例如我们需要持久化一个对象Student,其对应在数据库的表名是tbl_student_data,因此我们需要在一个传入对象的方法中进行转换,达到传入任何Class的实例,能转换成对应数据库的表名的功能。
通过上面的叙述,可以看出反射其实没那么复杂的,接下来会通过例子系统地演示反射的API使用。
1 准备
接口Eating
public interface Eating {
String eats();
}
抽象类Animal
public abstract class Animal implements Eating {
public static String CATEGORY = "domestic";
private String name;
protected abstract String getSound();
//隐藏构造方法和getter/setter方法
}
另一个接口Locomotion
public interface Locomotion {
String getLocomotion();
}
创建一个子类Goat
public class Goat extend