Java反射
- 反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性以及方法。反射在设计模式和框架底层都会用到。
- 加载完类之后,在堆中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象包含了类的完整结构信息。可以通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。
在反射中,可以把方法视为对象
Java 程序在计算机执行有三个阶段
1. 反射相关的主要类
- java.lang.Class 代表一个类,Class 对象表示某个类加载后在堆中的对象
- java.lang.reflect.Method 代表类的方法,Method 对象表示某个类的方法
- java.lang.reflect.Field 代表类的成员变量,Field 对象表示每个类的成员变量
- java.lang.reflect.Constructor 代表类的构造方法,Constructor 对象表示构造器
这些类在 java.lang.reflection
2. Class 类
2.1 基本介绍
- Class 也是类,因此也继承 Object 类
- Class 类对象不是 new 出来的,而是系统创建的
- 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过 Class 可以完整地得到一个类的完整结构,通过一系列API
- Class 对象是存放在堆中的
- 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等)
2.2 Class 类的常用方法
如下表:
方法名 | 功能说明 |
---|---|
static Class forName(String name) | 返回指定类名 name 的 Class 对象 |
Object newInstance() | 调用缺省构造函数,返回该 Class 对象的一个实例 |
getName() | 返回此 Class 对象所表示的实体(类、接口、数组类、基本类型等)名称 |
Class getSuperClass() | 返回当前 Class 对象的父类的 Class 对象 |
Class[] getInterfaces() | 返回当前 Class 对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Class getSuperclass() | 返回表示 此 Class 所表示的实体 的超类的 Class |
Constructor[] getConstructors() | 返回一个包含某些 Constructor 对象的数组 |
Field[] getDeclaredFields() | 返回 Field 对象的一个数组 |
Method getMethod(String name, Class … paramTypes) | 返回一个 Method 对象,此对象的形参类型为 paramType |
实例:
1 | public static void main(String[] args) throws Exception { |
2.3 获取 Class 类对象的常用方法
- 前提:已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException,示例:Class cls1 = Class.forName(“com.pushihao.Cat”);
应用场景:多用于配置文件,读取类全路径,加载类
- 前提:若已知具体的类,通过类的 class 获取,该方式最为安全可靠,程序性能最高,示例:Class cls2 = Cat.class;
应用场景:多用于参数传递,比如通过反射得到对应构造器对象
- 前提:若已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象,实例:Class cls3 = 对象.getClass();
应用场景:通过创建好的对象,获取 Class 对象
- 其他方式,示例:
1 | ClassLoader cl = 对象.getClass().getClassLoader(); |
- 基本数据(int, char, boolean, float, double, byte, long, short)可通过.class得到 Class 类对象:Class cls5 = 基本数据类型.class;
- 基本数据类型对应的包装类,可以通过.TYPE得到 Class 类对象:Class cls6 = 包装类.TYPE;
2.4 类加载
基本说明:
反射机制是 java 实现动态语言的关键,也就是通过反射实现类动态加载。
- 静态加载:编译时就加载相关的类,如果没有此类就报错(不管用到没用到),依赖性太强
- 动态加载:运行时才加载需要的类,如果运行时没有用到该类,即使不存在也不报错,降低了依赖性
类加载时机:
前三个都是静态加载,只有4是动态加载
- 当创建对象时(new)
- 当子类被加载时
- 调用类中的静态成员时
- 通过反射
1 | //假设没有Dog类,也没有Person类 |
类加载阶段:
加载 -> 准备(验证 -> 准备 -> 解析)-> 初始化
3. 通过反射获取类的结构信息
3.1 java.lang.Class 类
- getName 获取全类名
- getSimpleName 获取简单类名
- getFields 获取所有public修饰的属性,包含本类以及父类的
- getDeclaredFields 获取本类中所有属性
- getMethods 获取所有public修饰的方法,包含本类以及父类的
- getDeclaredMethods 获取本类中所有方法
- getConstructors 获取所有public修饰的构造器,只包含本类
- getDeclaredConstructors 获取本类中所有构造器
- getPackage 以Package形式放回包信息
- getSuperClass 以Class形式返回父类信息
- getInterfaces 以Class[]形式返回接口信息
- getAnnotations 以Annotation[]形式返回所有注解信息
3.2 java.lang.reflect.Field 类
getModifiers 以int形式返回修饰符
说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。多个修饰符时就是相加的结果
getType 以Class形式返回类型
getName 返回属性名
3.3 java.lang.reflect.Method 类
getModifiers 以int形式返回修饰符
说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。多个修饰符时就是相加的结果
getReturnType 以Class形式获取返回类型
getName 返回方法名
getParameterTypes 以Class[]形式返回参数类型的数组
3.4 java.lang.reflect.Constructor 类
- getModifiers 以int形式返回修饰符
- getName 返回构造器名(全类名)
- getParameterTypes 以Class[]形式返回参数类型数组
4. 反射暴破
假设有这么一个类:
1 | package com.pushihao.Reflection_.Explode; |
4.1 通过反射创建对象
- 方式一:调用类中的 public 修饰的无参构造器 class 对象
- 方式二:调用类中的指定构造器
- Class 类相关方法
- newInstance:调用类中的无参构造器,获取对应类的对象
- getConstructor:根据参数列表,获取对应的构造器对象
- getDecalaredConstructor:根据参数列表,获取对应的构造器对象
- Constructor 类相关方法
- setAccessible:爆破(暴力破解:反射可以使用类中的私有构造器/方法)
- newInstance:调用构造器
1 | public static void main(String[] args) throws Exception { |
4.2 通过反射访问类中的属性
- 根据属性名获取 Field 对象
1 | Field f = class对象.getDeclaredField(属性名); |
- 暴破
1 | f.setAccessible(true); |
- 访问
1 | f.set(对象实例, 新值); |
如果是静态属性,则 set 和 get 中的参数可以写成 null
1 | public static void main(String[] args) throws Exception { |
4.3 通过反射访问类中的方法
- 根据方法名和参数列表获取 Method 方法对象
1 | Method method = class对象.getMethod(方法名, class对象); |
- 获取对象实例
1 | Object o = class对象.getConstructor().newInstance(); |
- 暴破
1 | method.setAccessible(true); |
- 访问
1 | Object returnValue = method.invoke(o, 实参列表); |
如果是静态方法,则 invoke 的参数 o 可以写成 null
1 | public static void main(String[] args) throws Exception { |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 葡萄的个人博客!
评论
GitalkWaline