类的加载过程基础知识
前人栽树,后人乘凉。关于类加载的五个过程(加载、验证、解析、准备、初始化)前辈已总结的很好。
原文链接:blog.csdn.net/carson_ho/a…
原文链接:juejin.cn/post/684490…
自己小结的笔记
面试题一:类加载机制基础知识考察
1.简要谈一谈Java类加载过程
加载-验证-准备-解析-初始化-使用-卸载。
加载:通过类的全限定名获取二进制字节流并加载到方法区中,转换为虚拟机内存中一个实实在在的对象。class对象在堆中,作为访问方法区的入口。
验证:判断二进制字节流是否符合class文件规范,是否能在当前版本的JVM虚拟机上运行。主要验证class文件格式、元数据、字节码验证、符号引用验证。
准备:为类变量分配内存并设置类变量的值。
解析:将常量池中的符号引用解析成直接引用。
初始化:按顺序执行static代码块
2.谈一谈何时进行初始化
如上图
3.谈一谈ClassLoader有哪些?
如上图
4.谈一谈双亲委派机制及其好处?
口语化就是:我爸是李刚,有事找我爹。当加载类的时候,会一层一层网上捅,如果BootStrapClassLoader没有则在依次往下寻找。好处就是防止类被污染,导致出现多份名字相同的字节码文件。
面试题二:理解类的加载顺序
示例一:分析下列程序输出结果?
public class test_4 extends Father {
public static void main(String[] args) {
System.out.println(Son.age);
}
}
class Son extends Father {
static {
System.out.println("儿子的静态方法");
}
}
class Father extends GrandPa {
public static int age = 20;
static {
System.out.println("爸爸的静态方法");
}
}
class GrandPa {
static {
System.out.println("爷爷的静态方法");
}
}
输出结果为:
爷爷的静态方法
爸爸的静态方法
20
解析:直接调用Son.age,此时并没有初始化Son类,但调用了Father类的静态变量age,所以会初始化Father类以及GrandPa类.
示例二:分析下列程序运行结果
public class Book {
public static void main(String[] args)
{
staticFunction();
}
static Book book = new Book();
static
{
System.out.println("书的静态代码块");
}
{
System.out.println("书的普通代码块");
}
Book()
{
System.out.println("书的构造方法");
System.out.println("price=" + price +",amount=" + amount);
}
public static void staticFunction(){
System.out.println("书的静态方法");
}
int price = 110;
static int amount = 112;
}
输出结果为:
书的普通代码块
书的构造方法
price=110,amount=0
书的静态代码块
书的静态方法
分析:首先找main方法所在的类,初始化Book类,
加载-验证-准备-解析-初始化。到准备阶段为类变量在方法区中分配内存和初始化值,所以book = null, amount = 0;接着继续初始化,初始化过程中按顺序执行所有的静态代码块和类变量赋值语句,过程中遇到new Book()实例化了一个book对象,于是执行对象构造器,输出普通代码块中、Book()中的内容,实例化完成后继续类的初始化,即输出“书的静态块”,最后在为amount赋值为112,再去执行main方法中的内容。
示例三:分析下列程序运行结果
public class test_6 {
private static test_6 t = new test_6();
static int x;
static int y=0;
public test_6() {
x++;
y++;
}
public static void main(String[] args) {
System.out.println(test_6.x);
System.out.println(test_6.y);
}
}
输出结果:
1
0
分析:找main方法,首先初始化main方法对应的类test_6,在加载-验证-准备-解析-初始化的准备阶段为类变量在方法区中分配内存和赋值,此时t=null、x=0、y=0;接着解析后进行初始化,初始化过程中按顺序执行所有的静态代码块和类变量赋值语句,执行到new test_6(),所以执行对象构造器,执行test_6(){....}此时x=1、y=1。在为y重新赋值为0,最后执行main方法中的语句,输出1、0
分析类的加载顺序的方法:
- 先找main方法对应的类,去初始化该类
- 加载-验证-准备-解析-初始化,留意准备阶段,在准备阶段时为类变量(static修饰的部分)在方法区分配内存并进行赋值
- 初始化类构造器,按顺序执行静态代码块和类变量赋值语句。若在初始化过程中实例化了类,则转去先执行对象构造器,等执行完毕在继续执行类构造器。
- 最后执行main方法中的内容。