什么是反射
在运行时通过Class对象动态获取类信息,并且可以操作类或对象的内部属性。反射可以动态创建对象并调用其属性。而这种动态获取信息以及动态调用对象方法的功能就是JAVA的反射。
反射可以做什么
- 开发框架,例如Spring中动态创建bean
- IDE的代码提示
- 调试器,例如IDEA里的
Watches
一段简单的反射代码
//根据类的全限定名通过反射获取实例
Class> c = Class.forName("com.it.boot_demo.entity.UserEntity");
System.out.println(c);
System.out.println(c.getName());
System.out.println(c.getSimpleName());
Class类详解
在反射中的作用
- 提供方法用于访问运行期间类的元数据;
- 提供方法用于检查和修改类的运行时行为;
获取Class的三种方式
//UserEntity为演示用实体类,包含用户名等属性
//通过forName方式获取Class
Class> c1 = Class.forName("com.it.boot_demo.entity.UserEntity");
//类名.class
Class c2 = UserEntity.class;
//Object类的getClass()获取
UserEntity userEntity = new UserEntity();
Class extends UserEntity> c3 = userEntity.getClass();
演示对象:
public class UserEntity {
private Integer id;
private String username;
private String email;
private String address;
//....省略构造方法和getter setter
}
Class类概览
通过反射可以访问主要的描述信息,贴一下网上的图
判断Class对应的类型
通过反射创建实例对象
- 通过Class对象的newInstance()方法创建,这种方式只能调用无参构造方法;
- 通过Constructor对象的newInstance()方法创建,这种方式适用于有参构造方法,并且还可以破坏单例模式,调用私有构造方法;
这里给出两种方式调用无参构造创建实例的代码:
Class.newInstance()
Class> c = Class.forName("com.it.boot_demo.entity.UserEntity");
UserEntity userEntity = (UserEntity) c.newInstance();
Constructor对象的newInstance()
Class> c = Class.forName("com.it.boot_demo.entity.UserEntity");
UserEntity userEntity = (UserEntity) c.getDeclaredConstructor().newInstance();
由于 Class.newInstance()
这种方式在JDK1.9中已经被废弃,不建议再使用,所以推荐构造器创建实例的方式。
Field类
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。提供了获取 public 、private字段,获取包括和不包括继承字段的API。具体API和用法可以参考官方文档,这里贴一篇相当不错的博客。需要特别注意的是被final关键字修饰的Field字段是安全的,在运行时可以接收任何修改,但最终其实际值是不会发生改变的。
https://blog.csdn.net/javazejian/article/details/70768369#commentBox
Type类
Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
类型 | 常见表示 |
---|---|
原始类型 | 类,枚举,数组,注解等 |
参数化类型 | 常用的泛型如 List、Map等 |
数组类型 | 不是指基本类型的 String[] ,int[] ,而是指带有泛型的数组 T[] |
基本类型 | java的基本类型 int,float,byte等 |
Type类型展开讲就又是比较多的内容了,这里就不展开讲了。对于几种类型的默认实现,大家可以去网上看一看博客。
Method类
method提供关于类或接口上单独的某个方法的信息,提供了获取方法参数类型,获取返回参数类型,获取参数长度等API,使用起来也比较简单,具体使用可以参考API,这里略过。
Array类
java.lang.reflect.Array
这个类被声明为final,不可被继承。提供了动态创建和访问数组的API,Array 允许在执行 get 或 set 操作进行取值和赋值。
简单总结
JAVA反射相关的很多API都是native的,也就是说并不是通过java代码来实现的,属于底层实现。所以这里不去讲这些源码,API贴太多也没太大意义,其实实际开发中反射用到的并不是很多,而写框架就用的比较多了。网上很多大佬都讲了反射,这篇文章也参考了不少。这里给出我对反射的理解。反射是要在运行时创建和操作一个对象,对于JVM来说要通过反射操作的这个类就是一个黑盒子。但是JAVA语言对于一个对象是有一些通用的定义的,比如会有属性,会有方法,属性又会有各种类型,方法又会有无参和有参,有返回和无返回的区别。知道了这些信息之后还需要通过构造方法来创建对象的实例,而反射提供给我们的就是这些API。
反射的注意事项
- 反射会额外的消耗系统性能,如果不需要动态创建对象,尽量不要使用反射
- 反射调用方法可以忽略权限检查,可能会造成一定的安全问题