2021-06-06

JavaSE中反射理解

最近更新:2021/06/06

Reflcetion

1 反射的概念

反射可以是程序访问装载到JVM中的类的内部信息

2 反射的作用

  • 获取已装载类的属性信息
  • 获取已装载类的方法
  • 获取已装载的构造方法信息

3 反射的优点

反射提高了Java程序的灵活性和扩展性,降低偶合性,提高自适应能力。

允许程序创建和控制任何类的对象,无需提前硬编码目标类

4 反射的缺点

性能问题——使用反射基本上是一种解释操作,用于自断和方法介入时要远慢于直接代码

用于灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。

反射会模糊程序内部逻辑:反射绕过了源码的的技术,带来维护问题。

5 反射的使用:

获取类的Class对象

在Java中,每个类都有自己的Class对象,我们编写的.java文件通过javac编译后生成.class字节码文件时,字节码文件中包含类的所有信息,再被装载到虚拟机执行时,会在内存中生成Class对象,包含了内部的所有信息,在程序运行时可以获得这些信息。

获取类的Class对象的3种方式:

  • 类名.class:这种获取方式只有在编译前已经声明了该类的类型才能获取Class对象
  • 实例.getClass:通过实例化对象获取该实例的Class对象
  • Class.forName(classname):通过类的全限定名获取类的Class对象
构造 类的实例化对象
  • Class对象 调用newInstance()方法(默认会执行无参构造方法)
  • Constructor构造器 调用newInstance()方法(执行显式构造方法)
获取 类中的变量(Field)
  • Field[] getFields():获取类中所有被public修饰的所有变量
  • Field getField(String name):根据变量名获取类中的一个变量,该变量必须被public修饰
  • Field[] getDeclaredFields():获取类中所有的变量,但无法获取继承下来的变量
  • Field getDeclaredField(String name):根据姓名获取类中的某个变量,无法获取继承下来的变量
获取类中的方法(Method)
  • Method[] getMethods():获取类中被public修饰的所有方法
  • Method getMethod(String name, Class…<?> paramTypes):根据名字和参数类型获取对应方法,该方法必须被public修饰
  • Method[] getDeclaredMethods():获取所有方法,但无法获取继承下来的方法
  • Method getDeclaredMethod(String name, Class…<?> paramTypes):根据名字和参数类型获取对应方法,无法获取继承下来的方法
获取类的构造器(Constructor)
  • Constuctor[] getConstructors():获取类中所有被public修饰的构造器
  • Constructor getConstructor(Class…<?> paramTypes):根据参数类型获取类中某个构造器,该构造器必须被public修饰
  • Constructor[] getDeclaredConstructors():获取类中所有构造器
  • Constructor getDeclaredConstructor(class…<?> paramTypes):根据参数类型获取对应的构造器
每种功能内部以 Declared 细分为2类:

有Declared修饰的方法:可以获取该类内部包含的所有变量、方法和构造器,但是无法获取继承下来的信息

无Declared修饰的方法:可以获取该类中public修饰的变量、方法和构造器,可获取继承下来的信息

如果想获取类中所有的(包括继承)变量、方法和构造器,则需要同时调用getXXXs()和getDeclaredXXXs()两个方法,用Set集合存储它们获得的变量、构造器和方法,以防两个方法获取到相同的东西。

6 通过反射调用方法

通过反射获取到某个 Method 类对象后,可以通过调用invoke方法执行。

7 反射的应用场景

Spring实例化对象(applicationContext.xml并解析出里面所有的标签实例化到IOC容器中)

7.1 定义xml文件

通过ClassPathXmlApplicationContext加载配置文件

程序启动

解析并实例化bean,并放入IOC容器中(IOC就是一个工厂,通过工厂传入标签的id属性获取到对应的实例)

7.2 反射实例化对象的步骤

获取Class对象构造器

通过构造器调用newinstance()实例化对象

Spring 在实例化对象时,做了非常多额外的操作,才能够让现在的开发足够的便捷且稳定

8 反射 + 工厂模式

利用反射和工厂模式相结合,在产生新的子类时,工厂类不用修改任何东西,可以专注于子类的实现,当子类确定下来时,工厂也就可以生产该子类了。

8.1 核心思想

在运行时通过参数传入不同子类的全限定名获取到不同的 Class 对象,调用 newInstance() 方法返回不同的子类。

一般会用于有继承或者接口实现关系。
8.2 模式事例
JDBC链接数据库(注册驱动会用到反射加载驱动类)

在导入第三方库时,JVM不会主动去加载外部导入的类,而是等到真正使用时,才去加载需要的类,正是如此,我们可以在获取数据库连接时传入驱动类的全限定名,交给 JVM 加载该类。

9 反射的优势及缺陷

9.1 反射的优点

增加程序的灵活性:面对需求变更时,可以灵活地实例化不同对象

利用反射连接数据库,涉及到数据库的数据源。 SpringBoot 中一切约定大于配置,想要定制配置时,使用application.properties配置文件指定数据源

e.g. 三种角色

角色1 - Java的设计者

角色2 - 数据库厂商

角色3 - 开发者

需求变更:某天,老板来跟我们说,Druid 数据源不太符合我们现在的项目了,我们使用MysqlDataSource 吧,然后程序猿就会修改配置文件,重新加载配置文件,并重启项目,完成数据源的切换。(只需更改配置文件,不用改变代码——Spring Boot 底层封装好了连接数据库的数据源配置,利用反射,适配各个数据源。)

9.2 反射两个比较隐晦的缺点

有得必有失,一项技术不可能只有优点没有缺点,

破坏类的封装性:可以强制访问 private 修饰的信息

setAccessable(true)可以无视访问修饰符的限制,外界可以强制访问。

单例模式用了枚举避免被反射KO

性能损耗:反射相比直接实例化对象、调用方法、访问变量,中间需要非常多的检查步骤和解析步骤,JVM无法对它们优化。

在直接 new 对象并调用对象方法和访问属性时,编译器会在编译期提前检查可访问性,如果尝试进行不正确的访问,IDE会提前提示错误,例如参数传递类型不匹配,访问 private 属性和方法。

“而在利用反射操作对象时,编译器无法提前得知对象的类型,访问是否合法,参数传递类型是否匹配。只有在程序运行时调用反射的代码时才会从头开始检查、调用、返回结果,JVM也无法对反射的代码进行优化。

9.3 不一概而论

单次调用反射的过程中,性能损耗可以忽略不计。如果程序的性能要求很高,那么尽量不要使用反射

10 反射基础篇文末总结

反射的思想:反射就像是一面镜子一样,在运行时才看到自己是谁,可获取到自己的信息,甚至实例化对象。

反射的作用:在运行时才确定实例化对象,使程序更加健壮,面对需求变更时,可以最大程度地做到不修改程序源码应对不同的场景,实例化不同类型的对象。

反射的应用场景常见的有3个:Spring的 IOC 容器,反射+工厂模式 使工厂类更稳定,JDBC连接数据库时加载驱动类

反射的3个特点:增加程序的灵活性、破坏类的封装性以及性能损耗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值