一 定义
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
二 实现方式
- Object ——> getClass();
- 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
- 通过Class类的静态方法:forName(String className)(常用)
public class Fanshe {
public static void main(String[] args) {
//第一种方式获取Class对象
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
System.out.println(stuClass.getName());
//第二种方式获取Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
//第三种方式获取Class对象
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
注意:在运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
三 内部实现
上图是jvm如何执行Class cls = Class.forName("Foo"),也就是jvm如何将Foo.class文件加载到内存中的——类加载
step1:javac(java编译器)将java文件编译生成*.class文件
step2:jvm在运行过程中,根据class.forName("Foo")中的Foo来找到硬盘中的Foo.class文件(这个过程叫做类加载——classLoad)。加载并放到方法区中,这就是
为何我们在创建类的时候,类名要和文件名相同,不然jvm无法找到*.class文件。
step3:jvm根据Foo.class文件创建一个以cls命名的Class对象。这个对象可以通向方法区,我们可以操作cls来获得Foo类的所有信息。从而动态调用方法,创建对象,访问属性(甚至通过setAccessible来打开属性访问权限)。
首先我们知道在类的加载过程中仅有两种方式,一种是通过new来创建,第二是通过forname来获取到对象(其中第一种方式包含了第二种方式,只是new的时候,除了forname外JVM还做了实例化等一系列工作),而这些对象的来源都是通过ClassLoader读取进入到内存中的。而这些class最基本的信息,也就是字节码中所存储的所有的接口、方法、属性之类的信息都会存储在方法区中,一直不会变化。
那么反射其实就是通过从方法区中读取出了class所有的相关信息后,再显示的调用了实例化的方法(也就是构造器来实例化类)后,模拟了整个类的创建、引用和调用的过程,只是这整个过程更为底层一些(相比直接new多了好几个步骤)。但是总的来说,其实我们正常的使用对象上并没有很大的区别(当然关于private这个权限的处理上,反射做了特殊的处理来绕过了权限的认证)
四 为什么要用反射
- 在实际开发中,我们需要把一个包中的class new出来,但是这个包中的类总是需要变动,那么怎么办,难道总是修改main方法中xxx=new xxx()吗。这样无疑是麻烦的。而运用反射。我们可以相应的增加一个配置文件,在里面记录包中所有的类名,包中类增加时就加一个类名,删除时就删除一个 类名。让main方法去读取这个配置文件中的类名,通过反射获得实例,完全不用我们去修改main方法中的代码。
- 编译时显式的创建对象,运行会加载对象的所有属性,如果对象有很多的引用,这些引用又继续往下加载其所含有的引用,造成启动慢的现象。如果使用反射,运行时再加载,可以给人启动速度很快的感觉。
注意:
由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。
另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
原文:https://blog.csdn.net/sinat_38259539/article/details/71799078
https://blog.csdn.net/whathellll/article/details/80904244
https://www.jianshu.com/p/e5a65fd193d0