系列文章目录
Java反射机制
文章目录
- 系列文章目录
- 前言
- 一、反射的定义:
- 二、反射的相关类:
- 反射的核心API
- 三、Java程序计算机三个阶段
- 1.new对象创建流程分析:
- 2.反射创建对象:
- 3.类比:
- 四、Class类的details:
- 五、获取Class类对象:
- 六、反射的典型操作和爆破:
- 总结
前言
前期我们如果想得到一个对象是通过new的方式来创建对象的,但实际上在Java底层会进行类加载生成一个唯一类对象,里面存放着该类的信息,我们也可以通过这个对象来创建出来实例,Java 是静态类型语言,编译时需明确知道所有类型信息。但某些场景(如框架、插件系统)需要在运行时动态加载和操作未知类。反射允许在运行时分析类结构、创建对象、调用方法,无需在编译时硬编码类型。因此我们引入了反射,下面我们给大家具体讲一下:
Java反射机制样例:
通过外部文件配置,在不修改源码的情况下,来控制程序,也符合设计模式的OCP原则(开闭原则:不修改原码,扩容功能)
一、反射的定义:
1.反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法,反射在设计模式和框架底层都会用到。
2.加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息,通过这个对象得到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所有形象的称之为:反射
核心能力:突破静态代码的限制,实现 动态加载类、修改行为、调用私有 API 等高级操作。
二、反射的相关类:
反射的核心 API
类/接口 | 作用 |
---|---|
Class<T> | 表示类的元信息(如 String.class 、obj.getClass() )。 |
Field | 获取或修改字段的值(包括私有字段)。 |
Method | 动态调用方法(method.invoke(obj, args) )。 |
Constructor<T> | 创建对象实例(支持有参/无参构造器)。 |
Modifier | 解析修饰符(如 public 、static )。 |
反射的相关类:
1. Java.lang.class 代表一个类,class对象表示某个类加载后在堆中的对象。
2. Java.lang.reflect.Method 代表类的方法,Method对象表示某个类的方法。
3.Java.lang.reflect.Field 代表类的成员变量,Filed对象表示某个类的成员变量。
4. Java.lang.reflect.Consructor 代表类的构造方法,Consructor对象表示构造器。
三、Java程序计算机三个阶段:
这里面我们还是要了解一下这三个阶段的,因为跟反射有关。
这里面我们需要注意的是new和反射机制在创建对象的时候是不一样的。
new对象创建流程分析:
以Person类为例子:
1.加载Person类信息(Person.class),只会加载一次。
2.在堆中分配空间(地址)
3. 完成对象初始化(1.默认初始化 age=0,name=null 2.显式初始化 3.构造器初始化)
4.对象在堆中的地址返回给P(对象名或对象的引用)
details:
-
内存分配:
- JVM 根据方法区中
MyClass
的元数据(如字段数量和类型),计算对象需要的内存大小。 - 在堆中分配内存,并初始化对象头(包括指向方法区中
MyClass
元数据的类型指针)。
- JVM 根据方法区中
-
字段初始化:
- 默认初始化:所有字段设为默认值(如
int
为0
,对象引用为null
)。 - 显式初始化:通过构造函数(
<init>
方法)为字段赋值。
- 默认初始化:所有字段设为默认值(如
-
关键点:
- 对象的创建完全依赖方法区中的元数据(字段布局、构造函数等)。
- 对象头中的类型指针:确保对象知道自己属于哪个类(通过它找到方法区中的元数据)。
反射创建对象:
Class
对象本身不包含成员变量、方法的实现或构造器的代码,它只是提供反射接口去访问方法区中的元数据。
类比:
- 方法区(元数据):像一本“蓝图手册”,记录了类的结构(字段、方法、构造器等信息)。
-
Class
对象:像这本手册的“目录索引”,告诉你如何快速找到需要的字段或方法。 - 对象实例:按照蓝图手册建造出来的“房子”,每个房子知道自己对应的蓝图(通过对象头中的类型指针)。
四、Class类的details:
1.Class也是类,因此是继承Object类。
2.Class类不是new出来的,而是系统创建的。
3.对于某个类的Class类对象,在内存中只有一份,因此类只加载一次。
4.每个类的实例都会记得自己是由哪个Class实例所生成。
5.通过Class对象可以完整地得到一个类的完整结构,通过一系列API。
6.Class对象是放在堆中的。
7.类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,变量名,方法名和访问权限等等。)
五、获取Class类对象:
1.已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法来调用:
Class cls1 = Class.forname("类的具体路径");
场景:多用于配置文件,读取类全路径,加载类。
2.已知具体的类,通过类的Class获取,该方式最为安全可靠,程序性能最高。
Class cls2 = 类名.class;
场景:多用于参数传递,比如通过反射来获得对应的构造器对象。
3.对象.getClass();
场景:有对象实例。
4.通过类加载器来获取到类的Class对象:
(1)先得到类加载器Car:
ClassLoader classLoader = car.getClass().getClassLoader();
(2) 通过类加载器得到class对象:
Class cls4 = classLoader.loaderClass("classAllPath");
5. 基本数据类型:
Class<Integer> integerClass = int.class;
6.包装类:
Class<Integer> type = Integer.TYPE;
这里面我们还需要知道的一点就是为什么会有反射机制呢,我们要了解静态加载和动态加载:
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载:
静态加载:编译时加载相关的类,如果没有则报错。
动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。
六、反射的典型操作和爆破:
Class<?> clazz = User.class;
User user = (User) clazz.getDeclaredConstructor().newInstance();// 获取构造器(参数类型需匹配)
Constructor<User> constructor = clazz.getDeclaredConstructor(String.class, int.class);
User user = constructor.newInstance("Alice", 30);// 获取所有 public 字段(包括父类)
Field[] publicFields = clazz.getFields();// 获取本类所有字段(包括私有)
Field[] allFields = clazz.getDeclaredFields();// 获取指定字段(需处理 NoSuchFieldException)
Field nameField = clazz.getDeclaredField("name");// 设置可访问性(针对私有字段)
nameField.setAccessible(true);// 读取字段值
String nameValue = (String) nameField.get(user);// 修改字段值
nameField.set(user, "Bob");// 获取所有 public 方法(包括父类)
Method[] publicMethods = clazz.getMethods();// 获取本类所有方法(包括私有)
Method[] allMethods = clazz.getDeclaredMethods();// 获取指定方法(需参数类型匹配)
Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);// 设置可访问性(针对私有方法)
setNameMethod.setAccessible(true);// 调用实例方法(需传入对象实例)
setNameMethod.invoke(user, "Charlie");// 调用静态方法(对象实例传 null)
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
staticMethod.invoke(null);
爆破其实没什么神秘的,就相当于针对的是私有的修饰的属性或方法,相当于有一扇门我们要把它炸开,需要用setAccessible(true)方法。
总结
以上就是今天要讲的内容,本文帮助大家简单温习了一遍Java的反射机制,以及它的使用和类加载的内容,它的优点就是可以动态的创建和使用对象,使用灵活,没有反射机制,框架技术就失去了底层支撑。但是缺点就是解释执行,对执行速度有影响。
好了谢谢大家,接下来的两个星期我会开始进行大量算法题的更新,因为蓝桥杯快到了,我们一起进步,加油。