深入浅出!全面剖析Java反射-Reflection

Class clazz = Class.forName(“com.guo.Person”)

反射用这种方式是因为是在不清楚普通类的信息的前提下,根据类名创建Class实例,然后进行创建普通类实例等操作。注意此时只是知道普通类的路径名信息,该普通类的Class文件可能尚未加载到内存中,即在编译阶段尚未确定是否调用该类。而前两种方式都是先调用普通类,然后获取Class实例,这要求提前获取普通类的信息,与反射中事前不清楚类信息的场景不符合。

Class类常用方法

Class类提供许多方法,能够全面控制Class实例对应的普通类。本文从实现功能的角度,介绍一些Class类常用的方法。

以下方法都是由Class实例调用,操作结果对应Class实例对应的普通类。

获取普通类的构造方法

根据Class类实例访问其对应的普通类的构造方法,涉及以下方法

Constructor getConstructor(Class<?>… parameterTypes) 获取指定的public的构造器

  • Class<?>… parameterType表示构造器的参数类型
  • 参数的三个点表示可变参数列表,即该方法可以传入零到多个Class类型参数
  • 譬如当构造器的参数是String类型时,此处应该传入 String.class

Constructor[] getConstructors() 获取全部public的构造器

Constructor getDeclaredConstrnctor(Class<?>… parameterTypes) 获取某个构造器,不分公有、私有

  • (Class<?>… parameterType表示构造器的参数类型
  • 譬如当构造器的参数是String类型时,此处应该传入 String.class

Constructor[] getDeclaredConstructors() 获取全部构造器,不分公有、私有

tips:

  • 构造器总是作用在当前类,和父类无关,因此不存在多态问题。
  • 加”Declared“表示获取当前类的全部构造器,不分公私。而不加“Declared”表示当前类的公有构造器。
  • 加“s”表示获取全部,返回值是个数组。

以上方法的返回值类型是Constructor类,位于java.lang.reflect中。该类的构造器为私有,只能通过Class实例获取。用于描述普通类的构造器全部信息,包括修饰符、构造器名称、构造器参数。通过Constructor类提供的getName()、getModifiers()等方法,我们可以获取一个类的构造器的名称、参数等信息。

Constructor类提供了一个public T newInstance(Object … initargs)方法,利用该方法可以根据指定的构造方法创建对应的普通类的一个实例,但创建实例时newInstance方法中的参数列表必须与获取Constructor实例的getConstructor方法中的参数列表一致。与Constructor类不同,Class类也提供一个newInstance()方法用于创建对应普通类的实例,但这个方法要求普通类必须有无参构造方法,实质上Class类的newInstance()方法底层也是调用Constructor类的newInstance()方法。类似如下代码

Class clazz = Class.forName(“com.guo.Person”)
//使用Class实例创建实例,只能调用无参构造方法,因此要求普通类必须有无参构造方法。
Person p1 = (Person) clazz.newInstance();
//获取指定构造函数的类实例
Constructor con = clazz.getConstructor(String.class, int.class);
Person p2 = (Person) con.newInstance(“lisi”, 30);

获取普通类的字段

根据Class类实例访问其对应的普通类的字段信息,涉及以下方法

  • Field getField(name):根据字段名获取某个public的Field实例(包括父类)
  • Field[] getFields():获取全部public的Field实例(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的的某个Field实例(不包括父类)
  • Field[] getDeclaredFields():获取当前类的全部Field实例

tips:

  • 加”Declared“表示获取当前类的属性,包括公有、私有。
  • 不加“Declared”表示获取当前类和父类的公有属性。
  • 加“s”表示获取全部,返回值是个数组。

以上方法的返回值类型是Field类,位于java.lang.reflect中。该类的构造器为私有,只能通过Class实例获取。用于描述类或接口的字段的全部信息,包括修饰符、字段类型、字段名称。通过Field类提供的getName()、getType()、set(Object,Object)、setAccessible(Boolean)等方法,我们可以获取、修改一个类的字段的名称、值或访问权限。

获取普通类的方法

根据Class类实例访问其对应的普通类的方法信息,涉及以下方法

Method getMethod(String name,Class<?>… parameterTypes) 获取指定public的 Method实例(包括父类)。

  • 参数 name 为要获取的方法名
  • parameterTypes 为指定方法的参数的 Class,由于可能存在多个同名的重载方法,所以只有提供正确的 parameterTypes 才能准确的获取到指定的 Method
  • 参数的三个点表示可变参数列表,即该方法可以传入零到多个Class类型参数

Method[] getMehods() 获取全部public的Method实例(包括父类)

Method getDeclaredMethod(String name,Class<?>…parameterTypes) 获取当前类指定的Method实例(不包括父类)

  • 参数 name 为要获取的方法名
  • 参数的三个点表示可变参数列表,即该方法可以传入零到多个Class类型参数
  • parameterTypes 为指定方法的参数的 Class,由于可能存在多个同名的重载方法,所以只有提供正确的 parameterTypes 才能准确的获取到指定的 Method

Method[] getDeclaredMethods() 获取当前类全部的Method实例(不包括父类)

tips:

  • 加”Declared“表示获取当前类指定方法,包括公有、私有。
  • 不加“Declared”表示获取当前类和父类的公有属性。
  • 加“s”表示获取全部,返回值是个数组。

以上方法的返回值类型是Method类,位于java.lang.reflect中。该类的构造器为私有,只能通过Class实例获取。用于描述一个方法签名的全部信息,包括方法修饰符、方法返回值类型、方法名称、方法参数。通过Method类提供的getName()、getReturnType()、getParameterTypes()、setAccessible(Boolean)等方法,我们可以获取、调用、修改一个类的方法的名称、返回值或访问权限。

特点

反射的优点

  • 可扩展性:应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
  • 类浏览器和可视化开发环境 :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
  • 调试器和测试工具 : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。

反射的缺点

尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。

  • 性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
  • 安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
  • 内部暴露 :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

作用

反射的定义已经给出了反射的作用,它其实代表一种机制,或者说能力。准确的说,我们可以用反射这种机制完成什么样的功能需求,上一小节中获取普通类的构造方法、获取普通类的字段、获取普通类的方法就是反射所描述能力的体现。那么常用的场景有

  • 根据普通类的全限定名创建实例
    由于普通类的全限定名是一个字符串,因此这个字符串可以在运行时动态传输给程序,字符串来源可以是网络也可以是配置文件。然后使用Class.forName(“普通类的全限定路径”)获取普通类对应的Class实例(如果内存中没有Class实例,会触发动态加载Class文件到内存),再使用Class类或Constructor类的newInstance()方法创建一个普通类实例。这个创建实例的过程不使用new方式,即不必我们在编辑代码阶段硬编码写进去,而是根据程序实时运行状况的需要确定创建实例,达到了解耦。
  • 用Method.(Object obj, Object… args)执行方法
    反射调用方法是动态代理中的核心,具体参看动态代理
  • IDE的“.”智能提示

最后

面试题文档来啦,内容很多,485页!

由于笔记的内容太多,没办法全部展示出来,下面只截取部分内容展示。

1111道Java工程师必问面试题

MyBatis 27题 + ZooKeeper 25题 + Dubbo 30题:

Elasticsearch 24 题 +Memcached + Redis 40题:

Spring 26 题+ 微服务 27题+ Linux 45题:

Java面试题合集:

ng 26 题+ 微服务 27题+ Linux 45题:**

[外链图片转存中…(img-I31SgZjx-1714739886944)]

Java面试题合集:

[外链图片转存中…(img-JVXhYPyT-1714739886944)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值