反射和类加载器

反射和类加载器

1. 程序运行阶段

  1. 代码阶段(硬盘阶段):比如编写一个Person.java,编译成字节码Person.class,这都是存在硬盘的,而程序运行是要在内存中的
  2. 类加载器ClassLoader阶段:在第一阶段和第二阶段中间,也就是本文要讲的内容,先卖个关子,知道有这个阶段
  3. Runtime运行时阶段:也就是new Person(),那么此时已经在内存中

2. “第二阶段”:Class类对象阶段

通过第一第三阶段,可以推测出第二阶段其实是把硬盘上的class文件加载进内存变成java可以识别的内容(术语叫做Class类对象)的过程,随后第三阶段便可读取。那么究竟是如何做的呢?

在这个阶段会把Person.class(泛指所有.class文件)文件分解成三部分加载进内存(当然不是加载成Person这个类,程序没有new,java是不知道你要什么类的,所以他是加载成上面说的统一的“Class类对象”),这个对象包括了三个东西:

  • Filed[] fields:里面存放.class分解出来的成员变量
  • Constructor[] cons:存放构造方法
  • Method[] methods:存放成员方法

这个从.class文件分解为各个部分并封装为Class类对象的过程其实就叫反射

这不是很抽象的东西,来个具体应用举例:idea对着一个String对象敲下.,会提示chatAt等一堆可以用的方法,这方法从哪来?idea内部判断了String然后提示?怎么可能,其实这就是运用了反射,读取了Method[]列表,显示出来。可以把idea看作是一个运行中的java程序,因此反射可以在运行的时候获取类的信息


3. api

既然知道了反射就是为了获取”Class类对象“(再次重申,不是Person类对象,他就叫Class类对象!!),那么我们可以看如何获取了,刚刚说了三个阶段,那么每个阶段都有对应的api用于获取Class类对象。

  • Class.forName(“全类名”):第一阶段,将字节码文件加载进内存,返回Class类对象(全限定类名:包名+类名,这个阶段没有类名,因此必须手动写全限定类名,)

  • 类名.class:第二阶段,通过类名的class属性获取(这一阶段jvm已经加载好了,知道了类名,因此需要的话只要直接用类名.属性名就可以了)

  • 对象名.getClass():第三阶段,这时候你已经new Person()了,已经有对象了,getClass()是Object类提供的方法,因此你的Person类必有这个方法

1
2
3
4
5
//1.自己写一个Person类,随便给个name和age属性,略
Class cls1 = Class.forName("com.wjw.domain.Person");
Class cls2 = Person.class;
Person person = new Person();
Class cls3 = person.getClass();

同一个字节码文件只会被加载一次Class类对象,三种方式获得的地址都是同一个


4. 双亲委派机制

如果自己建立一个名为java.lang.String的包和类,能不能替换掉真正的String类呢?答案是不行的。

ClassLoader有三种

BootstrapClassLoader ==> $JAVA_HOME/jre/lib

ExtClassLoader ==> $JAVA_HOME/jre/lib/ext

AppClassLoader ==> 第三方和自己项目下的类

所谓双亲委派机制,就是当程序读取String的时候,先从BootstrapClassLoader开始,层层往下读取,如果能够加载String,那么交由该类加载器加载,如果到AppClassLoader都没有发现该类,会报ClassNotFoundException

String在$JAVA_HOME/jre/lib/rt.jar下,所以被BootstrapClassLoader加载。

需要查看是什么类加载器,Class类提供了getClassLoader()方法,如下

1
2
Person p = new Person();
System.out.println(p.getClass().getClassLoader());

如果要看上一级,就通过类加载器的getParent()

1
System.out.println(p.getClass().getClassLoader().getParent());

要注意的是,由BootstrapClassLoader加载的类getClassLoader()会为null,不是没有该类(没有会报错),而是显示不出来,已经去了c写的native里面

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×