一、什么是反射
Java的反射(reflection)机制:(动态语言的关键)程序运行时,动态地获取一个类的所有属性和方法,还可以操作这些方法和属性。
- 类加载器将.class文件中的二进制字节流读入到内存中,将字节流转化为方法区的运行时数据结构,然后在堆区创建一个java.lang.Class 对象(类相关的信息),作为对方法区中这些数据的访问入口
- 然后再通过类的实例来操作类的方法和属性。如果使用反射的话,需要拿到该类的Class对象,再通过Class对象来操作类的方法和属性或者创建类的实例
- Class类的构造方法是private,只有JVM能创建Class实例,开发人员无法创建Class实例的,JVM在构造Class对象时,需要传入一个类加载器。 类也是可以用来存储数据的,Class类用来保存“类所有相关信息”的类。
二、反射的优缺点
1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
2、缺点:
(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题
三、哪里会用到反射机制?
- JDBC:Class.forName('com.mysql.jdbc.Driver.class');//加载MySQL的驱动类
- 像 Spring/Spring Boot、MyBatis 等等框架中都大量使用反射机制,反射被称为框架的灵魂
- 类上加上@Component注解,Spring就帮我们创建对象
- 在Spring我们只需 @Value注解就读取到配置文件中的值
四、获得Class:主要有四种方法
(1)Object-->getClass,用于传过来的是Object类型的对象,不知道具体是什么类
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性,需要提前知道导入类的包,程序性能更高,比较常用,通过此方式获取 Class 对象,Person类不会进行初始化
(3)通过class类的静态方法:forName(String className)(最常用),只需传入类的全路径,Class.forName会进行初始化initialization步骤,即静态初始化(会初始化类变量,静态代码块)。
(4)通过类加载器获得class,loadClass 传入的第二个参数是"false",因此它不会对类进行连接这一步骤,根据类的生命周期我们知道,如果一个类没有进行验证和准备的话,是无法进行初始化过程的,即不会进行类初始化,静态代码块和静态对象也不会得到执行
//类加载器
ClassLoader classLoader = TestReflection.class.getClassLoader();
Class c4 = classLoader.loadClass("com.zj.demotest.domain.Person");
五、Class类常用的API
日常开发的时候,我们一般使用反射是为了创建类实例(对象)、反射获取类的属性和调用类的方法
| getName() | 获得类的完整名字 |
| getFields() | 获得类的public类型的属性 |
| getDeclaredFields() | 获得类的所有属性。包括private 声明的和继承类 |
| getMethods() | 获得类的public类型的方法 |
| getDeclaredMethods() | 获得类的所有方法。包括private 声明的和继承类 |
| getMethod(String name, Class[] parameterTypes) | 获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。 |
| getConstructors() | 获得类的public类型的构造方法 |
| getConstructor(Class[] parameterTypes) | 获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型 |
| newInstance() | 通过类的不带参数的构造方法创建这个类的一个对象 |
| getSuperClass() | 用于返回表示该 Class 表示的任何类、接口、原始类型或任何 void 类型的超类的Class(即父类)。 |
六、无参构造私有时
Class c1 = Class.forName("com.zj.demotest.domain.Person");
Constructor con = c1.getDeclaredConstructor();
con.setAccessible(true);//允许访问
Person p1 = con.newInstance();
注意:setAccessible()方法能在运行时压制Java语言访问控制检查(Java language access control checks),从而能任意调用被私有化保护的方法、域和构造方法。 由此我们可以发现** 单例模式不再安全,反射可破之!**
2.发现获取方法getMethod()时,需要传参 方法名和参数 这是因为.class文件中通常有不止一个方法,获取方法getMethod()时,会去调用searchMethods方法循环遍历所有Method,然后根据 方法名和参数类型 找到唯一符合的Method返回。
3.类的方法是在JVM的方法区中 ,当我们new 多个对象时,属性会另外开辟堆空间存放,而方法只有一份,不会额外消耗内存,方法就像一套指令模板,谁都可以传入数据交给它执行,然后得到对应执行结果。 method.invoke(obj, args)时传入目标对象,即可调用对应对象的方法
如果获取到的Method表示一个静态方法,调用静态方法时,无需指定实例对象,所以invoke方法传入的第一个参数永远为null, method.invoke(null, args)
那如果 方法重写了呢,反射依旧遵循 多态 的原则。
阅读参考:
https://zhuanlan.zhihu.com/p/601343285

1490

被折叠的 条评论
为什么被折叠?



