Java中反射的应用【奔跑吧!JAVA】
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等都不需要。
- 点赞
- 收藏
- 关注作者
评论(0)