反射机制原理
反射机制原理:
首先,明确几点:
1.运行eclipse的时候,生成.class文件并将.class文件的字节码加载到内存(这里也可以看出.class文件和字节码是一对包含和被包含的关系)
2.反射reflect将字节码中的字段,方法和构造函数等提取出来,这对于我们来说是透明的底层的,夸张点也就是说它默默奉献不为人知。
3.javap反编译.class生成的类似汇编语言的文件里是看不到类的私有变量的,但仍然能够通过反射获得字节码中的private修饰的成员。
在我们写出一个类并编译运行的时候,我们会发现由源文件.java编译生成了.class文件,.class文件存在于硬盘中,而.class文件对应的字节码则存在于内存中,这就不得不让我们想起了当初第一天学java的时候在命令行javac一个源文件(编译)生成.class文件,然后java生成的.class文件这一步就存在了将.class文件的字节码从硬盘提取到内存这个操作,当然java这条指令不仅仅包含这一个操作。
首先我们引自
https://baike.baidu.com/item/JAVA%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6/6015990
对反射的概述:
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
分析一下这句话:显而易见,Java的反射机制动态获取程序信息以及动态调用对象的功能非人为的,那么就产生这样的疑惑:为什么要反射?怎么反射的?
得出答案过程:我们平时访问一个类的成员变量和方法等类信息,一般要new一个对象,然后再对象.属性或方法的方式,但你是否曾想过,当我们的系统很大时,大到有好几十个类,那你在源代码中是不是要通过new很多个对象以访问这些类的成员变量和方法,首先,这很繁琐,其次,不便于管理。于是,基于在不改变源代码的基础上spring框架的ioc(控制反转)的底层就是通过反射机制实现的,它通过.xml配置文件或者注解的方式把对象的生成和属性的赋值交给spring这个容器去生成和存储。这样的话我们每当要对对象之间的关系的管理就直接通过修改.xml配置文件即可。还有我们JaveWeb中的MVC框架,对于servlet,我并没有去new一个servlet的对象,但它却能调用servlet类中的方法,这也是由反射机制来完成的。
实践出真知,下面我们来敲段代来证明以上说法:
Student类:
package tests11;
public class Student {
public String name = "zhangsan";
private int age = 10;
public Student() {
System.out.println("执行无参构造方法");
}
public Student(String name , int age) {
System.out.println("执行有参构造方法: " + name+" "+age);
}
public void Hello(String name) {
System.out.println("执行公开方法: " + "我的名字是 :"+ name+", "+"我今年"+age+"岁");
}
}
反射机制测试类:
package tests11;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest {
public static void main(String[] args) throws Exception{
// 1、通过指定类的完全限定名:包名+类名,加载类
Class c1 = Class.forName("tests11.Student");
System.out.println(c1);//验证输出的类名称与字节码文件是否一致,答案是一致的
// 2.提取/反射类c1的公开无参构造函数,即默认的构造函数
Constructor ctor1 = c1.getConstructor();
// 3、构造函数的用途,就是创建类的对象(实例)的
// 除了私有构造函数外(单列模式,禁止通过构造函数创建类的实例,保证一个类只有一个实例)
// ctor1.newInstance()默认生成一个Object对象,我们需要强制转化成Student类对象
// Object s1 = ctor1.newInstance();
Student s1 = (Student) ctor1.newInstance();
// 4、通过访问类中的name成员证明是Student实例对象
System.out.println(s1.name);
// 2.b、 提取/反射类c1的公开有参构造函数,参数为string和int
Constructor ctor2 = c1.getConstructor(String.class, int.class);
Student s2 = (Student) ctor2.newInstance("lisi", 21);
System.out.println("获得本类中的所有的字段 : ");
// 5、获得类中的所有的字段 包括public、private和protected,不包括父类中申明的字段
Field[] fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("获得本类中的所有公有的字段,并获得指定对象的字段值 : ");
// 6、获得类中的所有的公有字段
fields = c1.getFields();
for (Field field : fields) {
System.out.println(field + ", 字段值 = " + field.get(s1));
if (field.getName() == "name" && field.getType().equals(String.class)) {
String newName = (String) field.get(s1);
newName = "ZHANGSAN";
field.set(s1, newName);
}
}
System.out.println("利用反射出的字段,修改字段值,修改后的name = " + s1.name);
System.out.println("获取本类中的所有的方法 : ");
// 7、获取本类中所有的方法 包括public、private和protected,注意不包括父类中申明的方法
Method[] methods = c1.getDeclaredMethods();
for (Method M : methods) {
System.out.println(M);
}
System.out.println("获取本类中的所有的公有方法 :");
// 8、获取类中所有公有方法
methods = c1.getMethods();
for (Method M1 : methods) {
System.out.println(M1);
}
System.out.println("根据方法名称和参数类型获取指定方法,并唤起方法:指定所属对象s1,并给对应参数赋值 : ");
// 9、唤起Method方法(执行) getMethod:第一个参数是方法名,后面跟方法参数的类
Method hello = c1.getMethod("Hello", String.class);
System.out.println(hello.invoke(s1, "longlong"));
}
}
Student.class文件反编译:
Student类中的Hello方法的符号引用:
总结:
我们从上面的例子中不难看出反射机制动态获取程序信息以及动态调用对象的结果跟.class文件的描述如出一辙,同时它是通过类对象java.lang.class这个入口去获得的,Class c1 = Class.forName(“tests11.Student”);得到的c1在获得类的构造函数,字段,方法等中可以明确看出(都是c1.xxx)。
本文地址:https://blog.csdn.net/m0_48333563/article/details/107440721
上一篇: Java 字符串判空