Java 中的反射是什么

用户头像
Rayjun
关注
发布于: 2020 年 10 月 16 日
Java 中的反射是什么

反射是一个程序语言用来自检和修改自身结构和行为的过程。



这么说可能有些晦涩,反射可以理解为在程序运行过程中,获取程序的结构信息,从而根据不同的情况来调整程序的行为。



在使用汇编语言编程的时代,汇编语言可以直接获取任何信息,可以认为汇编语言天然就支持反射。但是到后来的一些高级语言,比如 Fortran 和 C 语言等等,由于不支持反射,也就无法获取信息。



再到后续的 Java,C# 等语言中都自带了反射框架,这些语言就可以通过反射来实现通用的框架,比如在 Java 的 Spring 中就大量使用了反射。



这篇文章重点介绍 Java 中的反射。



💡本文基于 OpenJDK11



1. 反射能干什么



在 Java 中,反射可以获取类的定义、属性和方法,并且可以调用这些方法,或者构造对象,甚至可以在运行时修改类的定义。



先来看一个简单的例子,通过这个例子可以知道反射能做什么,例子中的代码下面会详细讲解:



// 利用反射来执行方法
public Object methodInvoke(Object o, Method method, Object[] params) {
try {
return method.invoke(o, params);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}



使用反射来生成 ArrayList 对象并调用方法:

// 获取 ArrayList 的 Class 对象和构造函数,并生成对象
Class clazz = ArrayList.class;
Constructor<ArrayList> constructor = clazz.getConstructor();
ArrayList instance = constructor.newInstance();
// 获取 add 方法,并调用 add 方法添加元素
Method add = clazz.getDeclaredMethod("add", Object.class);
Object[] params = {"reflection"};
methodInvoke(instance, add, params);
// 获取 get 方法,并调用 get 方法获取刚刚添加的元素
Method get = clazz.getDeclaredMethod("get", int.class);
Object[] params1 = {0};
System.out.println(methodInvoke(instance, get, params1));



如果不使用反射,这个代码很简单:

ArrayList list = new ArrayList();
list.add("reflection");
System.out.println(list.get(0));



反射这么麻烦,为什么还要使用它?



观察一下上面利用反射来执行方法的代码可以发现,执行任何类的方法,那段代码都不需要修改,而且更重要的是,具体执行的方法还可以在程序运行的过程中改变



反射用起来虽然麻烦,但是反射的动态性提高了 Java 的灵活性,很多框架都依赖反射。



2. 相关概念



在 Java 中,有很多的类,每个类都有自己的属性,方法,构造函数等等信息,这些信息都需要通过一种方式表示,在 Java 中就是通过 Class 对象来表示。



这里不要把 Class 与 Object 混在一起,Object 是所有类的父类,而 Class 类则不一样,在每个类中,都有一个 Class 对象来保存类的信息。即使对于 Java 中的原生数据类型,也有自己的 class 对象。



Class clazz = int.class;



每个 Class 对象中都有主要有三个主要部分:Field、Method 和 Constructor。这些和反射相关的类都在 java.lang.reflect 包下。



Method 和 Constructor 在上面的代码中都看到了,分别表示类的方法和构造函数。Field 则表示类的属性。



除了上面这些信息外,还有其他的信息,比如判断属性或者方法是否能访问,获取类、方法、属性的注解等等:



Method add = clazz.getDeclaredMethod("add", Object.class);
add.isAccessible(); // 判断是否能访问



即使对于私有方法或者属性,也可以通过下面的方式来访问:

Method add = clazz.getDeclaredMethod("add", Object.class);
add.setAccessible(true); // 通过这个设置,即使私有方法也能访问



在 Java 中,注解用的很多,实际上,注解本身并没有包含额外的内容,只是起到标记的作用,所有的注解都可以通过反射来获取,然后再进行相应的逻辑处理。



Method add = clazz.getDeclaredMethod("add", Object.class);
add.getDeclaredAnnotations(); // 获取该方法所有的注解



还可以获取方法的参数,返回值的类型等等信息。



所以总的来说,反射是一个框架,可以帮助获取程序内部的信息,至于获取这些信息来做什么,就看开发人员的发挥。



反射虽然很强大,但是性能上相比于普通方式性能上难免会有一些损失,需要注意,在程序执行的过程中,需要尽量少用反射。而且反射连私有方法都可以访问,如果滥用就可能造成程序的不安全。



3. 反射的基本用法



使用行业的第一步,就是获取目标类型的 Class 对象,如果获取不到,那就说明目标类型无法完成类加载,也就不能进行后续的反射操作。

获取 Class 对象的方法



有 4 种方法可以获取 Class 对象。



通过类的 class 属性:

Class clazz1 = String.class;



通过 Class.forName() 方法:

Class clazz2 = Class.forName("java.lang.String");



通过对象的 getClass() 方法:

Class clazz3 = "string".getClass();



对于基本类型的包装类,还有一种方法可以获取 Class 对象:

Class clazz4 = Integer.TYPE;



创建对象



在获取到 Class 对象之后就可以创建对象了,创建对象有两种方式。



通过 Class 对象直接生成:

Class clazz = String.class;
String str = (String) clazz.newInstance();



通过构造函数生成:

Constructor<String> constructor = clazz.getConstructor();
String str1 = constructor.newInstance();



如果构造函数有参数:

Constructor<String> constructor2 = clazz.getConstructor(String.class);
String str2 = constructor2.newInstance("String");



调用方法

在生成了对象之后,自然就可以调用方法,调用的方式和上面的代码一样。



首先获取 Method,如果方法没有参数,直接获取执行就可以:

Method lengthMethod = clazz.getMethod("length");
lengthMethod.invoke(str2);



如果有参数,在获取方法的时候就需要声明方法的类型和个数,而且参数的顺序不能乱:

Method substringMethod = clazz.getMethod("substring", int.class, int.class);
substringMethod.invoke(str2, 1,2);



其他工具方法



上面是反射的一些基本用法,当然反射也提供了其他的一些工具方法。



获取所有已经声明的属性、方法和构造函数,包括 private 的也会被获取出来:

Field[] fields = clazz.getDeclaredFields();
Method[] methods = clazz.getDeclaredMethods();
Constructor[] constructors = clazz.getDeclaredConstructors();



判断是否是内部类:

clazz.isMemberClass();



还有其他判断是否接口、注解,获取类的泛型信息等等方法。



文 / Rayjun

本文首发于微信公众号



欢迎关注我的微信公众号



REF

[1] https://en.wikipedia.org/wiki/Reflection_(computer_programming)



发布于: 2020 年 10 月 16 日 阅读数: 15
用户头像

Rayjun

关注

程序员,王小波死忠粉 2017.10.17 加入

非著名程序员,还在学习如何写代码,公众号同名

评论

发布
暂无评论
Java 中的反射是什么