1.什么是反射
反射是在运行期间动态的获取这个类的基本信息,一个类分为运行期和编译器,编译器主要干什么事情呢?主要是进行把一个类生成.class文件,把什么是class文件呢,就是二进制文件,能够让机器进行识别的,也就是0和1的组成的。运行期主要是把类加载到jvm内存里面,用java虚拟机进行加载。主要分为几个过程,加载,连接(验证,准备,解析),初始化。
jvm里面的加载
在java里面规定四种类加载器
1)Bootstrap ClassLoader启动类加载器
负责加载$JAVA_HOME中jre/lib/里所有的 class(JDK 代表 JDK 的安装目录,下同),或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如 rt.jar,所有的java.*开头的类均被 Bootstrap ClassLoader 加载)。启动类加载器由 C++ 实现,不是 ClassLoader 子类。无法被 Java 程序直接引用的。
2)Extension ClassLoader扩展类加载器
该加载器由sun.misc.LauncherExtClassLoader实现,负责加载Java平台中扩展功能的一些jar包,包括ExtClassLoader实现,负责加载Java平台中扩展功能的一些jar包,包括JAVA_HOME中jre/lib/.jar或-Djava.ext.dirs指定目录下的 jar 包。即JDK\jre\lib\ext目录中,或者由 java.ext.dirs 系统变量指定的路径中的所有类库(如javax.开头的类),开发者可以直接使用扩展类加载器
3)App ClassLoader应用程序类加载器
。该类加载器由 sun.misc.Launcher$AppClassLoader 来实现,负责记载 classpath 中指定的 jar 包及目录中 class,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
启动类加载器:它使用 C++ 实现(这里仅限于 Hotspot,也就是 JDK1.5 之后默认的虚拟机,有很多其他的虚拟机是用 Java 语言实现的),是虚拟机自身的一部分。
所有其他的类加载器:这些类加载器都由 Java 语言实现,独立于虚拟机之外,并且全部继承自抽象类 java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类。
应用程序都是由这三种类加载器互相配合进行加载的,我
4)自定义的类加载器。
加载过程:加载过程中会先检查类是否被已加载,检查顺序是自底向上,从 Custom ClassLoader 到 BootStrap ClassLoader 逐层检查,只要某个 Classloader 已加载就视为已加载此类,保证此类只所有 ClassLoade r加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
扫描二维码关注公众号,回复:
4388739 查看本文章
验证
验证的目的是为了确保 Class 文件中的字节流包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。不同的虚拟机对类验证的实现可能会有所不同,但大致都会完成以下四个阶段的验证:文件格式的验证、元数据的验证、字节码验证和符号引用验证
准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。
解析
解析阶段是虚拟机将常量池中的符号引用转化为直接引用的过程。
初始化
类初始化是类加载过程的最后一个阶段,到初始化阶段,才真正开始执行类中的 Java 程序代码。虚拟机规范严格规定了有且只有四种情况必须立即对类进行初始化
2. 反射的应用
Person接口: public interface Person {
//爱好
void hobby(String play,String sport);
//吃
void eat();
//性别
void sex();
} |
Student类: public class Student implements Person{
private int age;
private String name;
public Student(){
System.out.println("年龄 "+age +" ,"+"姓名 "+name);
}
public Student(int age,String name){
this.age = age;
this.name = name;
System.out.println("年龄 "+age +" ,"+"姓名 "+name);
}
@Override
public void hobby(String play, String sport) {
System.out.println("爱好是: "+play+sport);
eat();
}
@Override
public void eat() {
System.out.println("吃屎");
}
@Override
public void sex() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString(){
return "性别: "+ age +", 名称: "+name;
}
private void study(){
System.out.println("爱学习");
}
} |
运行类:
public static void main(String[] args) {
try {
Class<?> classStudent = Class.forName("com.mec.test.Student");
Object object = null;
try {
object = classStudent.newInstance(); //弱类型 为什么说弱类型,难道它不用申请内存空间吗?
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
/**
* 1. 动态获取这些类的方法
* 2. 对student里面的有关方法进行反射
*/
Method[] methods = classStudent.getDeclaredMethods();
for(Method method : methods){
System.out.println(method.getName());
if(method.getName().equals("hobby")){
try {
Method hobby = classStudent.getMethod("hobby",String.class,String.class);
hobby.invoke(object,"打","篮球");
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
if(method.getName().equals("study")){
try {
Method study = classStudent.getDeclaredMethod("study"); //对Student类里面的私有方法进行访问
study.setAccessible(true);
study.invoke(object);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
/**
* 1. 动态获取这些类的构造方法
* 2. 对student里面的构造方法进行反射
*/
Constructor[] constructors = classStudent.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor);
}
try {
Class<?>[] classes = new Class[]{int.class,String.class};
Constructor<?> constructor = classStudent.getConstructor(classes);
constructor.newInstance(10,"小明");
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
/**
* 1. 动态获取这些类的成员变量
* 2. 对student里面的成员变量进行反射
*/
Field[] fields = classStudent.getDeclaredFields();
for(Field field : fields){
System.out.println(field.getName());
if(field.getName().equals("age")){
try {
Field age = classStudent.getDeclaredField("age");
age.setAccessible(true);
age.set(object,12);
Student student = (Student)object;
System.out.println(student.getAge());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} |
运行结果: 年龄 0 ,姓名 null toString getName setName hobby 爱好是: 打篮球 吃屎 study 爱学习 getAge eat sex setAge public com.mec.test.Student() public com.mec.test.Student(int,java.lang.String) 年龄 10 ,姓名 小明 age 12 name |
分析:通过上述的代码我们可以发现,使用反射机制对类的方法,成员变量进行操作,可以看出里面最主要的方法就是newInstance()方法,它和new()操作有什么区别呢,从jvm来看,newInstance必须保证类已经加载,并且已经连接完成(验证,准备,解析),然而new操作我们不需要保证类已经加载,这就是为什么newInstance是低效率。从编程思想看newInstance可以实现工厂模式。
1. Class A = Class.forName("A");
2. Factory factory = (Factory)A.newInstance();(Factory是接口)
3. A可以配置在xml或者property文件里面
4. 以后只要其他类实现Factory接口,我们就不用改代码,只需要改正配置文件信息就可以了,
3. 反射机制的优点:
1. 解耦(工厂模式)
2. 访问私有变量
3. 为以后的spring框架做铺垫
4. 反射机制的缺点:
1. 安全性能降低
2. 效率变低
3. 反射代码破坏了抽象性