Java反射机制是什么?
好久没更新自己的blog 这一个月确实太多事情了 放下了自己ctf的学习进度 话说还是边做笔记变学习有效率
Java程序运行原理
Java程序运行原理其实本质上就是Java虚拟机与跨平台原理 |
Java程序的运行过程就像“翻译官带着菜谱去不同国家的厨房做饭”,核心是跨平台执行和自动管理内存。
1.写代码→翻译成通用菜谱 (编译)
·你写的代码:用Java语法写好的java文件,比如一个“西红柿炒蛋”的菜谱(代码)
·编译成字节码:用javac命令把代码翻译成.class文件(字节码)。这相当于把中文菜谱转成“国际通用菜
谱”(字节码),任何国家的厨房(操作系统)都能看懂。
关键点:
·字节码不是机器码,而是一种中间格式,类似“通用菜谱”,需要二次翻译。
·跨平台的关键:每个国家(操作系统)配一个翻译官(JVM)就能用同一份菜谱
2.厨房开工→JVM加载并执行
·启动JVM:运行java命令时,相当于请来翻译官(JVM)进厨房。
·类加载器搬食材:JVM的“搬运工”(类加载器)把菜谱(字节码)和食材(类信息)搬到厨房的不同区域:
。方法区:存放菜谱步骤(类结构、静态变量)。
。堆内存:放炒菜用的锅碗瓢盆(对象实例),比如“西红柿”和“鸡蛋”对象。
。栈内存:记录当前炒菜的步骤(方法调用栈),比如“先热锅再倒油”。
关键点:
·JVM自动管理内存,不用手动清理(垃圾回收)
·每个线程(厨师)有自己的工作台(栈)。
3.边炒菜边优化→解释执行&即时编译(JIT)
·解释执行:翻译官(JVM)逐行读菜谱(字节码),转成机器能执行的指令。比如“切西红柿”翻译成机器码。
·即时编译(JIT):如果某步骤重复多次(比如“翻炒”),JVM会把这部分编译成机器码缓存起来,下次直接执
行,加快速度
关键点:
·解释执行适合启动快,即时编译适合长期运行的高效。
·类似厨师记住常用步骤,不用每次都看菜谱。
4.收尾→垃圾回收
·自动清理:炒完菜后,JVM的“清洁工”(垃圾回收器)会自动扔掉用过的锅(无引用的对象),腾出堆内存
·分代回收:新锅(新生代)和旧锅(老年代)分开清理,提高效率69。
Java反射机制
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
通过 Java 的反射机制,程序员可以更深入地控制程序的运行过程。例如,在程序运行时由用户输入一个类名,然后动态获取该类拥有的构造、属性和方法,甚至调用任意类的任意方法。 |
在Java开发中,我们使用new关键字来创建一个对象,然后在程序运行的时候JVM就会相应的生成这个对象。也就是说我们一旦确定了这个类以及对象,那么就无法进行修改。
那么可不可以在我们程序运行的时候动态的生成一个对象呢?
当然可以。我们把Java程序在运行的时候根据需要动态生成的对象称为Java的反射机制。
在程序运行的时候动态创建一个类的实例
周用实例的方法和访问它的属性
Java代码运行原理:
源码
编译器(javac)纟编译为字节码.class文件
各平台JVM解释器把字节码文件转换成操作系统
指令(跨平台)
了解下什么是反射
Java反射机制是什么?
Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,大家应该先了解两个概念,编译期和运行期。
编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
运行期是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制**。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。
这里的反射方式
实例化对象:
- 在反射机制中,我们已经有了一个对象(或者不知道对象类型时),它可以是任何类的实例。
调用
getClass()
方法:- 通过调用该对象的
getClass()
方法,我们可以获取该对象的类类型。这种方式不需要在编写代码时提前知道对象的具体类型。
- 通过调用该对象的
得到完整的包类名称:
使用
getClass()
方法,我们可以获取该对象的完整类名,这包括其所在的包名。这对于动态处理和调试非常有用。- **正常方式**适合静态类型的语言或不需要动态处理的场景,显式类的引入和实例创建。 - **反射方式**适合需要动态类处理的场景,可以在运行时根据对象的类型做出不同的处理,提供更大的灵活性。
Java 反射机制主要提供了以下功能,这些功能都位于`java.lang.reflect`包。
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 生成动态代理。
众所周知,所有 Java 类均继承了 Object 类,在 Object 类中定义了一个 getClass() 方法,该方法返回同一个类型为 Class 的对象。例如,下面的示例代码:
```java
Class labelCls = label1.getClass(); // label1为 JLabel 类的对象
利用 Class 类的对象 labelCls 可以访问 labelCls 对象的描述信息、JLabel 类的信息以及基类 Object 的信息。表 1 列出了通过反射可以访问的信息。
- 获取类信息:通过
Class.forName()
方法获取某个类的Class
对象,然后使用Class
对象的各种方法获取类的信息,如getName()
获取类名,getDeclaredMethods()
获取所有方法等。 - 创建对象:通过
Class
对象的getConstructor()
或getDeclaredConstructor()
方法获取指定构造函数的引用,然后使用newInstance()
或newInstance()
方法创建对象。 - 访问属性:通过
Class
对象的getDeclaredField()
方法获取指定属性的引用,然后使用set()
或get()
方法设置或获取属性的值。 - 调用方法:通过
Class
对象的getDeclaredMethod()
方法获取指定方法的引用,然后使用invoke()
方法调用方法并传递参数。 - 动态代理:通过 Java 反射实现动态代理,可以在运行时创建一个实现了指定接口的代理类,并实现指定的方法逻辑。
更多的可以去看https://y4er.com/posts/java-deserialization-1/
我觉得写的还行的
什么是反射?反射之中包含了一个「反」字,所以想要解释反射就必须先从「正」开始解释。
我们先来看一段代码
1 |
fanshe testObj = new fanshe(); |
---|---|
2 |
testObj.setPrice(5); |
很简单,就是通过new创建了一个fanshe
类的对象testObj
,这是[正射]。在这个实例化的过程中,我们需要知道类名,那么实际开发中如果我们不确定类名的话就没办法new
一个实例了,为此java搞了一个反射出来。
所以反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法
一、通过访问权限区分
反射可通过不同方法获取类成员的访问权限:
公有成员
使用getFields()
或getMethods()
,获取所有公有字段/方法(包括继承的):Field[] publicFields = MyClass.class.getFields();
Method[] publicMethods = MyClass.class.getMethods();所有成员(包括私有)
使用getDeclaredFields()
或getDeclaredMethods()
,获取本类所有字段/方法(不包含继承的):Field[] allFields = MyClass.class.getDeclaredFields();
Method[] allMethods = MyClass.class.getDeclaredMethods();判断具体修饰符
通过Modifier
类分析访问权限:Field field = MyClass.class.getDeclaredField("privateField");
boolean isPrivate = Modifier.isPrivate(field.getModifiers());
二、通过继承关系区分
反射可区分成员是否来自父类或接口:
获取父类信息
Class<?> superClass = MyClass.class.getSuperclass();
System.out.println("父类:" + superClass.getName());获取接口信息
Class<?>[] interfaces = MyClass.class.getInterfaces();
for (Class<?> interfaceClass : interfaces) {
System.out.println("接口:" + interfaceClass.getName());
}判断是否为继承成员
通过对比父类与本类成员:Method[] parentMethods = superClass.getDeclaredMethods();
// 遍历本类方法,检查是否在父类中存在同名方法
三、通过成员类型区分
反射可区分字段、方法、构造函数的类型:
字段类型
Field field = MyClass.class.getDeclaredField("name");
Class<?> fieldType = field.getType();
System.out.println("字段类型:" + fieldType.getName()); // 如 java.lang.String方法参数与返回值
Method method = MyClass.class.getMethod("setName", String.class);
Class<?> returnType = method.getReturnType(); // void
Class<?>[] paramTypes = method.getParameterTypes(); // [String.class]构造函数参数
Constructor<?> constructor = MyClass.class.getConstructor(String.class, int.class);
Class<?>[] constructorParams = constructor.getParameterTypes();
四、通过动态调用区分
反射允许运行时动态操作对象:
调用公有方法
Object obj = MyClass.class.newInstance();
Method publicMethod = MyClass.class.getMethod("publicMethod");
publicMethod.invoke(obj);强制调用私有方法
Method privateMethod = MyClass.class.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true); // 突破私有权限
privateMethod.invoke(obj);修改字段值
Field privateField = MyClass.class.getDeclaredField("secret");
privateField.setAccessible(true);
privateField.set(obj, "newValue");
五、通过元数据特征区分
判断是否为合成成员
Java编译器生成的合成成员(如内部类访问外部类的字段):Field field = MyClass.class.getDeclaredField("this$0");
boolean isSynthetic = field.isSynthetic(); // true获取注解信息
Annotation[] annotations = MyClass.class.getAnnotations();
if (annotations.length > 0) {
System.out.println("类上存在注解:" + annotations[0].annotationType());
}
总结
通过反射API可精准区分类成员的以下特性:
维度 | 反射方法 | 示例场景 |
---|---|---|
访问权限 | getFields() vs getDeclaredFields() |
公有/私有字段分离 |
继承关系 | getSuperclass() |
判断方法是否来自父类 |
成员类型 | getParameterTypes() |
动态适配方法参数 |
动态操作 | Method.invoke() |
运行时调用未知方法 |
元数据特征 | isSynthetic() |
过滤编译器生成代码 |