Java类加载

刚经历了一段面试,发现自己在java这一块还是入门了(其他方面就一般般,特别是数据结构和算法),接下来分享一些我觉得很重要的知识点。

Java类加载

常识:我们都知道自己写的.java文件会被静态编译器编译成.class文件(二进制文件或者字节码文件),你在自己工程的目录下面下会找到一个classes文件夹,里面放的就是编译好的class文件。
好了,我们编译好的class文件有什么作用呢?有人会说了,class文件之后会被编译成机器码(不太严谨),然后运行在jvm上。来我们举个例子:

Person p1 = new Person();
Person p2 = new Person();

每次new一个实例都需要将包含Person类的java文件重新编译一次吗?其实这是不需要的。同时我们类加载也只需要一次。

类加载定义:

类的加载是指将类的.class文件中的二进制数据读取到内存,并将其放在运行时方法区内(有人说这是一个叫做klass的东西),然后再在堆区创建一个java.lang.Class对象,用来封装在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。那么有几点你需要明确:(JVM内存模型去网上搜一些图看看,主要看三块,堆,方法区(听说已经被移除了),栈)
重点:
1.每个类都有一个Class对象,且是唯一一份。
2.Jvm会在该类对象初次被调用的时候将Class对象加载到内存。Class对象没有公共的构造方法,由java虚拟机通过类加载器中的defineClass方法自动构建的。
3.我们每次新建对象的时候都需要用这个Class对象,当我们第二次新建对象时不需要再去加载class文件了。
4.这个Class对象可以通过Java反射机制来获取。(下次来写写Java反射)
OK,现在我们大体知道类加载做了什么事情,下面我们将深入jvm的内存模型里面去细致的看一看在类加载的过程中做了哪些事。
类的加载包括以下几个步骤:(自己画的有点丑)

注意初始化可不是实例化,这还只是类的加载阶段。好,我们按顺序依次来看一下:
加载:这里专指把class文件的类信息调到内存中来,这部分由类加载器来实现的,包括一下三件事:
1.通过类的完全限定名去定位二进制字节流。
2.将这个字节流所代表的静态数据结构转化为方法区内的运行时数据结构。
3.创建一个Class对象作为方法区内的类信息的入口。

验证:确保类加载的正确性。(具体验证哪些细节可以自己去搜一下,大体是要符合jvm的规范)

准备:为类的静态变量在方法区中分配内存,并将其初始化默认值。注意只包含类变量(静态变量)而不包含实例变量(建议不清楚这些名词的赶紧去熟悉一下)。这里的默认值指的是该静态变量类型的默认值,如0,null,false。
例如:对于一个public static count = 3;那么在准备阶段该count值还为0,而不是3。
那么这里有一点,对于同时被static和final修饰的变量,除了我们需要显示地在声明时就为它赋值以外,在准备阶段它就会被初始化指定的值,而不是默认值。可以理解为现在已经将其放入了常量池中。

解析:把类中的符号引用转化为直接引用。(这个自己查查吧,我也说不清楚)
验证,准备和解析也被统称为连接。

初始化:为类的静态变量赋予正确的初值而不是那个默认值。可以通过两种方式赋值:
1.public static count = 3
2.public static count;
static{
count = 3;
}
JVM初始化步骤:
1.加入类还没有被加载和连接,那么会先执行之前的步骤。
2.加入该类的直接父类还没有被初始化,则先初始化其直接父类。
3.加入类中有初始化语句,会先执行初始化语句。
导致类初始化的原因:
1.创建类实例.
2.访问静态变量。
3.调用类的静态方法。
4.反射。
5.初始化子类,父类也会被初始化。
本章介绍了很多东西,对于小白来说还是有很多东西需要横向扩展的。下一章我们看一下类加载器以及类的一些加载策略吧。

你可能感兴趣的:(Java基础知识)