本篇主要讲解类的初始化,也就是上图红框的部分。
类的初始化都做了哪些事呢?
一、初始化:
1.初始化阶段就是执行类构造器方法<clinit>() 的过程。
2.此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。
发现只要有类变量的赋值和静态代码块,系统默认生成<clinit>() 方法。
看如下代码:
它的字节码文件:
就会发现没有<clinit>()方法。
3.构造器方法中指令按语句在源文件中出现的顺序进行执行。
4.<clinit>() 不同于类的构造器,一旦一个类进行声明后,系统默认会生成该类的无参构造器,也就对应的是字节码文件中的<init>() 方法。
5.若该类具有父类,JVM 会保证子类的<clinit>() 执行前,父类的<clinit>() 已经执行完毕。
如上图,执行ClinitTest1的main() 方法,首先会进行ClinitTest1 加载到内存中,然后进行链接,初始化完成后,调用它的main() 方法执行打印输出Son.B,又
由于Son 继承Father类,会先执行Father类的<Clinit>() 方法,将A=1,在执行静态代码块,A=2,再去执行Son的<clinit>() 方法,将A=2的值赋值给B,最后输出
B的值为2.
6. 虚拟机必须保证一个类的<clinit>() 方法在多线程下被同步加锁,也就是一个类只会被加载一次。存放到方法区中,并进行缓存。
二、初始化时机:(也就是何时才会进行初始化):
三、虚拟机自带的加载器:
1、启动类加载器(引导类加载器,Bootstrap ClassLoader):
a.这个类加载使用c/c++ 语言实现的,嵌套在JVM 内部。
b.它是来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar、resources.jar 或sun.boot.class.path 路径下的内容),用于提供JVM自身需要的类
如下图代码:
运行结果:
c.并不继承java.lang.ClassLoader,没有父类加载器。
d.加载扩展类和应用程序类加载器,并指定为他们的父类加载器。
e.出去安全考虑,Bootstrap 启动类加载器只加载包名为java、javax、sun 等开头的类。
2、扩展类加载器:
a、Java语言编写,由sun.misc.Launcher$ExtClassLoader实现。
b、派生于ClassLoader
c、父类加载器为启动类加载器。
d、从java.ext.dirs 系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库,如果用户创建的jar放在此目录下,也会自动由扩展类加载器加载。
如下图代码:
运行结果:
3、应用程序类加载器(系统类加载器,AppClassLoader)
a、java语言编写,由sun.misc.Launcher$AppClassLoader实现
b、派生于ClassLoader类
c、父类加载器为扩展类加载器。
d、它负责加载环境变量classpath 或系统属性 java.class.path 指定路径下的类库。
e、该类加载时程序 默认的类加载器,一般来说,java 应用的类都是由他来加载完成的
f、通过ClassLoader#getSystemClassLoader() 方法可以获取到该类加载器