反射简介
反射是Java的高级特性之一,但是在实际的开发中,使用Java反射的案例却非常的少,但是反射确实在底层框架中被频繁的使用。
比如:JDBC中的加载数据库驱动程序,Spring框架中加载bean对象,以及态代理,这些都使用到反射,因为我们要想理解一些框架的底层原理,反射是我们必须要掌握的。
理解反射我们先从他的概念入手,那么什么是反射呢?
反射就是在运行状态能够动态的获取该类的属性和方法,并且能够任意的使用该类的属性和方法,这种动态获取类信息以及动态的调用对象的方法的功能就是反射。
实现上面操作的前提是能够获取到该类的字节码对象,也就是.class文件,在反射中获取class文件的方式有三种:
- 类名.class 如:Person.class
- 对象.class 如:person.class
- Class.forName(全类名)获取 如:Class.forName("ldc.org. demo.person")
这里有点区别的就是使用1,2(.class)方式获取Class对象,并不会初始化Class对象,而使用3(forName("全类名"))的方式会自动初始化Class对象。
反射
反射对应到Java中的类库就是在java.lang.reflect下,在该包下包含着Field、Method和Constructor类。
Field: 表示一个类的属性信息Method: 表示类的方法信息Constructor: 表示的是类的构造方法的信息
在反射中常用的方法,我这里做了一个列举,当然更加详细的可以查官方的API文档进行学习。
方法名 | 作用 |
---|---|
getConstructors() | 获取公共构造器 |
getDeclaredConstructors() | 获取所有构造器 |
newInstance() | 获取该类对象 |
getName() | 获取类名包含包路径 |
getSimpleName() | 获取类名不包含包路径 |
getFields() | 获取类公共类型的所有属性 |
getDeclaredFields() | 获取类的所有属性 |
getField(String name) | 获取类公共类型的指定属性 |
getDeclaredField(String name) | 获取类全部类型的指定属性 |
getMethods() | 获取类公共类型的方法 |
getDeclaredMethods() | 获取类的所有方法 |
getMethod(String name, Class[] parameterTypes) | 获得类的特定公共类型方法 |
getDeclaredClasses() | 获取内部类 |
getDeclaringClass() | 获取外部类 |
getPackage() | 获取所在包 |
User 类:
点击查看代码
package com.example.zhangchonghu.demo.controller.reflect;/*** @Description:* @author: 张重虎* @Date: 2022/2/17 11:20* @Version 1.0*/public class User {private String name;public Integer age;public User() {}public User(String name, Integer age) {this.name = name;this.age = age;}private void privateMethod() {System.err.println("私有方法执行了");}public void publicMethod(String param){System.err.println("公有方法执行了,参数为:"+param);}@Overridepublic String toString() {return "User{" +"name=\'" + name + \'\\\'\' +", age=" + age +\'}\';}}
在User的实体类中,有两个属性age和name,并且除了有两个测试方法privateMethod和publicMethod用于测试私有方法和公共方法的获取。接着执行如下代码:点击查看代码
package com.example.zhangchonghu.demo;import org.junit.jupiter.api.Test;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/*** @Description:* @author: 张重虎* @Date: 2022/2/17 11:54* @Copyright: Xi\'an Dian Tong Software Co., Ltd. All Rights Reserved.* @Version 1.0*/public class TestReflect {@Testpublic void test() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException, ClassNotFoundException {//1、类名.class/*** Class clazz = User.class;*///2、对象.class/*** User user = new User();* Class clazz = user.getClass();*///3、Class.forName(全类名)获取Class clazz = Class.forName("com.example.zhangchonghu.demo.controller.reflect.User");Constructor constructor = clazz.getConstructor(String.class, Integer.class);//获取该类对象并设置属性的值Object obj = constructor.newInstance("张三", 18);//获得类全类名,既包含包路径String fullClassName = clazz.getName();//获得类名String className = clazz.getSimpleName();//获取类中公共类型(public)属性Field[] fields = clazz.getFields();String fieldName = "";for (Field field : fields) {//获取属性名fieldName = field.getName();System.out.println("public 属性名为:" + fieldName);}//获取类中所有的属性,包括 private 属性Field[] fieldsAll = clazz.getDeclaredFields();fieldName = "";for (Field field : fieldsAll) {fieldName = field.getName();System.out.println("private 属性名为:" + fieldName);}//获取指定公共属性值Field age = clazz.getField("age");Object value = age.get(obj);System.err.println("公共指定属性:" + value);//获得指定的私有属性值Field name = clazz.getDeclaredField("name");//设置为true才能获取私有属性name.setAccessible(true);Object value2 = name.get(obj);System.err.println("私有指定属性值:" + value2);//获取所有公共类型方法 这里包括 Object 类的一些方法Method[] methods = clazz.getMethods();String methodsName = "";for (Method method : methods) {methodsName = method.getName();System.out.println("公开的方法:" + methodsName);}//获取该类中的所有方法(包括private)Method[] methodsAll = clazz.getDeclaredMethods();methodsName = "";for (Method method : methodsAll) {methodsName = method.getName();System.out.println("私有的方法:" + methodsName);}//获取并使用指定方法Method privateMethod = clazz.getDeclaredMethod("privateMethod");//获取无参私有方法privateMethod.setAccessible(true);privateMethod.invoke(obj);//调用方法Method publicMethod = clazz.getMethod("publicMethod", String.class);//获取有参数方法publicMethod.invoke(obj, "张三");//调用有参方法}}
运行结果(多次运行结果顺序不相同,盲猜是和多线程有关。埋个雷,没有深入挖掘):
反射在jdk 1.5的时候允许对Class对象能够支持泛型,也称为泛化Class,具体的使用如下:
Class<User> user= User.class;//泛化class可以直接得到具体的对象,而不再是ObjectUseruser= user.newInstance();
泛化实现了在获取实例的时候直接就可以获取到具体的对象,因为在编译器的时候就会做类型检查。当然也可以使用通配符的方式,例如:Class<?>
反射优点和缺点
优点:反射可以动态的获取对象,调用对象的方法和属性,并不是写死的,比较灵活,比如你要实例化一个bean对象,你可能会使用new User()写死在代码中。
但是使用反射就可以使用class.forName(user).newInstance(),而变量名user可以写在xml配置文件中,这样就不用修改源代码,灵活、可配置。
缺点:反射的性能问题一直是被吐槽的地方,反射是一种解释操作,用于属性字段和方法的接入时要远远慢于直接使用代码,因此普通程序也很少使用反射。