Java中反射的应用【奔跑吧!JAVA】

举报
多米诺的古牌 发表于 2021/05/17 16:32:14 2021/05/17
【摘要】 1.反射的定义反射是使用 JDK 提供的反射 API 进行反射调用 ,可以在运行时检查类、接口、方法和变量等信息,无需知道类的名字,方法名等。还可以在运行时实例化新对象,调用方法以及设置和获取变量值。举几个例子:在最熟悉的JDBC中,通过反射机制建立的数据库字段和实体类的映射关系(即映射到实体类各个属性中的getter/setter方法)开发中必不可少的Spring中最经典依赖注入(控制反转...

1.反射的定义

反射是使用 JDK 提供的反射 API 进行反射调用 ,可以在运行时检查类、接口、方法和变量等信息,无需知道类的名字,方法名等。还可以在运行时实例化新对象,调用方法以及设置和获取变量值。

举几个例子:

在最熟悉的JDBC中,通过反射机制建立的数据库字段和实体类的映射关系(即映射到实体类各个属性中的getter/setter方法

开发中必不可少的Spring中最经典依赖注入(控制反转)也是利用反射机制

还有SpringBoot中大量使用的注解,大幅度减少配置的操作(约定大于配置),也是通过大量反射机制完成的。

2.使用反射时可能存在的问题

2.1 性能较差 

尽管反射解决了动态类型的问题,但是也引入了在classpath 扫描类进行加载的过程,可能会影响性能。

2.2 安全限制 

反射需要在运行时获得访问权限,但是在某些应用或组件中可能是不允许的,这样可能会导致运行失败。

2.3安全问题 

通过反射我们可以访问那些不建议我们访问的类,例如我们可以访问private的属性并修改其值, 可能会引起安全问题从而导致应用异常。

2.4较高的维护代价 

反射相关的代码难以理解和调试,代码出现的错误也不能在编译期展现出来,使用反射的代码灵活性不高并难以维护。

3.反射的使用

Class clzz = Class.forName("com.test.Test");
Method method = clzz.getMethod("setName", int.class);
Constructor constructor = clzz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, "张小明");

上面的例子即为反射的简单应用,等同于下面的new对象设置属性操作:

Test test = new Test(); 
test.setName("张小明");

第一个例子中是通过通过Class类方法的forName()方法动态获取完整类名称com.test.Test,并且可以在运行时获取Test类的完整构造,包括属性、方法、构造器等,并调用对应的方法,进行实例化、设置属性等操作。

第二个例子为我们正常创建对象,设置属性。

3.1 获取对象的Class实例:

  • 通过静态变量class

public class Test{
    public static void  main(String arts[]){
        Class clazz = Test.class;
        System.out.println(clazz.getName());
        System.out.println(clazz.getSimpleName());
    }
}

上面方法中getName()可以获取完整类名称(包含包名),getSimpleName()只能获得类名

  • 使用示例的getClass()方法

String str ="我是一个字符串,我的想知道我的是什么类的";
Class clazz = str.getClass();
  • java.lang.Class.forName(String 完整类名称)

3.2 获取类的构造函数

Class clazz = Test.class;
Constructor[] constructors = clazz .getConstructors();

上面的例子中可以获得Test类的所有构造函数,但是如果已经知道构造器的参数怎么获得对应的构造器呢?

Class clazz = Test.class;
try{
     Constructor constructors = clazz .getConstructor(new Class[]{String.class});
}catch(NoSuchMethodException  e){
     ... ...
}

注意:获取构造函数时只能获取到public修饰的构造函数,并且需要捕获NoSuchMethodException异常。

通过getDeclaredConstructor()方法传参获取特定参数类型的构造方法

 Class clazz = test.getClass();
 Constructor[] constructors = clazz .getDeclaredConstructors();

其中.getModifiers()方法可以获得构造方法的类型, .getParameterTypes方法可以获得构造方法的所有参数 

3.3 获取类的包名

getPackage() .getName()方法获取包的class实例后获取对应名称,即为包名

3.4 获取类的实现接口

getGenericInterfaces() 可以获取class已经实现的接口的数组,包含泛型接口。

getInterfaces()方法会返回所有实现的接口,但是不包含泛型接口。

3.5 获取类的方法

getMethods()方法可以获取所有的public方法,包含父类、接口中继承来的public方法。

getDeclaredMethods()可以获得上述所有方法,并且包含私有方法,即获得所有成员方法。

getMethod(String 方法名称 ,参数类型)获得指定方法,如果要获取的方法没有参数,则用null替代。

getDeclaredMethod(String 方法名称 ,参数类型)获得指定方法,可以是私有方法,如果要获取的方法没有参数,则用null替代。

Method[] metchods = clazz.getMethods();
Method[] metchods1 = clazz.getDeclaredMethods();
Method metchods2 = clazz.getMethod("doSomething", new Class[]{int.class});
Method metchods3 = clazz.getDeclaredMethod("doSomething", new Class[]{int.class});

通过getDeclaredMethod方法获取到这个私有方法的例子:

其中method.setAccessible(true);为可以使用私有方法的标识,设置为true即为可以通过反射获取私有变量的值,在访问时会忽略访问修饰符的检查。

     Class[] strClazz = {String.class};
     Method method = clazz.getDeclaredMethod("doSomething",strClazz );
     method.setAccessible(true);
     Object arg1s[] = {"设置属性的值"};
     method.invoke(test,arg1s);

invoke()方法,在运行时根据业务需要调用相应的方法时,只要通过反射获取到方法名就可以调用对应的方法。该方法有两个参数,第一个参数是调用方法的对象,第二个参数是调用方法要传入的参数。如果有多个参数,则可以使用数组传参,如果调用的是static方法,invoke()方法第一个参数可以使用null进行代替。

3.6 获取类的成员的getter和setter方法

使用反射可以在运行时检查和调用类声明的成员方法,可以用来检测某个类是否有getter和setter方法。 getter和setter方法有下面的一些规律: getter方法以get为前缀并且没有参数,但必须有返回值; setter方法则是以set为前缀并且有一个参数,参数类型必须和对应成员属性的类型保持一致,返回值可有可无。

一般使用.startsWith("get")和.startsWith("set")来进行判断是否有 getter和setter方法,或者判断后进行调用等操作。

3.7 获取成员变量
通过反射可以在运行时获取到类的所有成员变量,还可以给成员变量赋值和获取成员变量的值。

getFields()方法获取所有public修饰的成员变量。

getField()方法需要传入变量名,并且变量必须是public修饰符修饰。

getDeclaredFields方法获取所有成员变量,不管是修饰符是什么,即包含私有成员。

Class clazz = Test.class;
Field[] fields1 = clazz .getFields();
Field[] fields2 = clazz .getDeclaredFields();
Field fields3 = clazz .getField("name");
Field fields4 = clazz .getDeclaredField("sex");

获取成员变量的变量类型可以通过getType()方法进行获取,如下:

Object fieldType = fields4.getType();

获取到成员变量的Field引用后,就可以获取通过get()方法获取变量值,或者可以通过set()方法给变量进行赋值操作。

当需要获取私有成员变量时,可以使用 Class.getDeclaredField(String str)或者Class.getDeclaredFields()才能获取到私有变量,其中必须调用setAccessible(true)方法进行允许获取私有成员变量的标识操作,public和protected等都不需要。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。