Java中反射函数的使用方法和注意事项
一、反射函数简介及相关概念
反射,指程序可以访问、检测和修改它本身状态或行为的一种能力。在Java中,反射即通过分析和操作类的运行时属性来获取或使用类对象的编程技术。反射功能通过JAVA语言提供的类类型(Class)可以实现,反射使得我们可以在编译期不了解类的情况下,可以通过运行期对对象进行操作。
Java中类信息在使用时,一般是通过类名获取(Class.forName())及new实例化对象来使用其方法或属性的。也就是说,普通方法和属性都是通过对象调用,而反射氏针对类信息的操作,允许我们在运行期动态的获取某个特定名称的对象所代表的类的全部信息,包括类的属性、方法及构造方法等,具体的使用方法:
1、通过Class.forName()方式获取一个类;
2、通过newInstance()方法创建一个实例;
3、获取类的属性、方法、构造器等等;
二、反射函数使用方法
1、获取Class对象
在Java中,每个对象都有一个class对象(类类型),通过.obj.getClass()或Class.forName(“类名”)或者类.class语法实现获取。可以通过Class对象获取类的基本信息,如,类名、父类、实现的接口、构造函数、属性、方法等。
2、Class.forName()方法
Class.forName()方法可以动态的获取一个类,该方法中的参数为类的完整限定名。
首先介绍一下参数的三种写法,User.class、Class.forName("com.java.entity.User")、new User().getClass()三种方式中, 种方式最为常用,是大部分用户首选的方法。第二种方式是最早的方式,虽然写法比较蹩脚,但是别忘了,作为程序猿,自己的生命在于创新,略微有些不优雅的方式为我们节省启动时间和内存资源,速度是其最大的优势。第三种方式需要实例化之后再获取,对于某些需要再全局获取之前校验的场景比较有用。
Class.forName()加载的类会进行类的初始化,也就是会执行静态代码块中的内容,通常我们会将类的初始化放在静态代码块中,所以绝大部分情况下我们获取Class对象时需要使用 Class.forName()。
3、newInstance()方法
newInstance()方法可以完美的解决模块化问题,它将一个字符串作为参数,这个字符串的内容就是你要动态生成的对象的类元素。这个方法能完全使脚本化编程成为可能,并且使得软件能够极短期内实现软件的模块化以及可插拔式的体系结构设计。
在JDK9及以后的版本中,newInstance()方法已经被标记为过时方法,官方推荐使用getDeclaredConstructor().newInstance()代替。
4、Method类
在反射机制中,Method类是一个被封装的对象类。其包含基本的类方法,如getName()、getModifiers()、getExceptionTypes()、invoke()等方法,执行方法时需要传入对象和方法参数,该类提供了invoke()方法实现调用;需注意的是:该方法每次都需要处理不同的类型,调用时不是很方便。
public class ReflectClassDemo {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class clazz=Class.forName("com.ysg.Reflection.Person");
//2.获取Person中的setName方法
Method method=clazz.getMethod("setName", String.class);
//3.反射调用setName方法
Person person=(Person)clazz.newInstance();
method.invoke(person,"ysg");
//4.验证
System.out.println(person.getName());
}
}
输出结果:ysg
5、Constructors类
在反射机制中,Constructor类是一个被封装的类对象,包含类的构造方法的基本方法,如getName()、getModifiers()等方法,构造时无需返回值,需注意的是:它也需要处理不同的类型,不是很方便,一般使用newInstance()方法。
public class ReflectClassDemo1 {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class clazz=Class.forName("com.ysg.Reflection.Person");
//2.获取Person中的setName方法
Constructor constructor=clazz.getConstructor();
//3.反射调用setName方法
Person person=(Person)constructor.newInstance();
//4.验证
System.out.println(person.getName());
}
}
输出结果:null
6、Field类
在反射机制中,Field类是一个被封装的类对象,包含类的属性的基本方法,如getName()、set()、get()等;
注意:其类型必须和实际类型一致,否则将会抛出异常。
public class ReflectClassDemo2 {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class clazz=Class.forName("com.ysg.Reflection.Person");
//2.获取Person对象中的name的属性
Field field=clazz.getDeclaredField("name");
//3.设置可访问权限
field.setAccessible(true);
//4.反射调用setName方法
Person person=(Person)clazz.newInstance();
field.set(person,"ysg");
//5.验证
System.out.println(field.get(person));
}
}
输出结果:ysg
三、反射函数注意事项
反射机制虽然可以使得程序更加灵活,但是在应用过程中也有一些需要注意的地方。
1、反射机制破坏封装性和安全性
反射机制使工程师跨过了Java的许多保护区域,例如,工程师可以:
获取一个方法的私有成员,然后调用;(setAccessible(true))
将数据存放在一个私有变量中,而该变量原本是不能访问的;
动态加载一个类,而不是在代码中固定地显式加载。
2、运行效率较低
相对于普通的调用(直接new一个对象和调用其方法),使用反射机制是一个间接的操作,因为它需要动态的进行类构建、方法和属性的寻找和调用,因此其性能也相对较低,应尽量避免使用反射操作频繁的执行同一类的操作,反射机制的使用如果不得当很容易引起性能问题。
使用反射需要注意权衡!在某些情况下,可能更加适合使用普通对象调用技术。
3、获得的类也需要在应用中有所限制
通过反射获得一个类之后,我们仍然需要保证该类是被授权访问的。例如,从整个设计层次来说,它可能不是一个被公开的类。如果代码仅仅是要对JavaBean进行设置,而拥有一个被授权的JavaBean类,但这并不被看成是一个问题。
一个典型的方式是,如果类被定义为null或抛出一个异常,那么特别当心此类的访问。
