知方号

知方号

类加载机制&面试题分析<类的加载机制面试题>

类加载机制&面试题分析

使用

jvm初始化完之后,就会从方法入口,执行用户的代码

卸载

当用户程序代码执行完毕后,JVM 便开始销毁创建的 Class 对象,最后负责运行的 JVM 也退出内存

总结

上面对jvm的类加载机制进行了一个大概的了解,知道类加载机制之后可能你会问有什么用,我们平时用不到呀,虽然我平时也用不到这些,但是既然是学习java开发的,我觉得有必要把这些东西了解一下,毕竟我们每天都在用java,怎么能不去了解一下他是怎么运行的呢,除此之外,面试的出场也是非常高的

例题1

请看下面的题目,下面代码输出的结果是什么?

public class Student { static int age = 22; public Student(){ System.out.println("无参构造方法 age = "+ age); } { System.out.println("普通代码块"); } static { System.out.println("静态代码块"); } public static void main(String[] args) { Student student = new Student(); System.out.println("main方法"); }} 1234567891011121314151617181920232223 例题1分析

输出结果如下

静态代码块普通代码块无参构造方法 age = 22main方法 1234

当我们的程序要执行main方法的时候,要先对该类进行初始化,在上面的类中有两个方法,一个构造方法和一个main方法,在java类编译成字节码文件的时候,字节码中只有类初始方法和对象初始化方法这两个概念,我们可以根据以下来区分

类初始化方法:顾名思义,类初始化方法会在类初始化的时候执行,IDE会按顺序手机变量的赋值语句,静态代码块,组合成类初始化方法

如上面的Student类,下面两个语句将组成类初始化方法

static int age = 22; static { System.out.println("静态代码块"); } 12345 对象初始化方法:在对象实例化的时候才会执行,IDE会按顺序手机成员变量的赋值语句,普通代码块,最后收集构造函数代码组成对象初始化方法 例题2 package jvmlearn.basetype;class A { static { System.out.println("A的静态块"); }}class B extends A { public static int bbb = 10; static { System.out.println("B的静态块"); } public B(){ System.out.println("B的构造方法"); }}class C extends B{ static { System.out.println("C的静态代码块"); } public C(){ System.out.println("C的构造方法"); }}public class LoadDemo { public static void main(String[] args) { System.out.println("B的参数bbb="+C.bbb); }} 12345678910111213141516171819202322232425262728293031323334353637 例题2分析

输出结果如下

A的静态块B的静态块B的参数bbb=10 123

你可能会疑惑为什么C的静态代码块这句话没输出,这里要注明一下,jvm在加载类的过程中,对于静态字段只有直接定义这个字段的类才会被初始化,而不会触发子类的初始化(执行类初始化方法)

具体过程可以描述如下: 1. main方法调用C.bbb 2. bbb在B类定义,初始化B类,不初始化C类 3. 初始化B类的时候发现A类没有被初始化,去初始化A类 4. 初始化A类,输出A的静态代码块 5. 初始化完A类后,继续初始化B类,输出B的静态代码块 6. 执行main方法,输出B的参数bbb=10

例题3 package jvmlearn.basetype;class A { static { System.out.println("A的静态块"); } public A(){ System.out.println("A的构造方法"); }}class B extends A { public static int bbb = 10; static { System.out.println("B的静态块"); } public B(){ System.out.println("B的构造方法"); }}class C extends B{ static { System.out.println("C的静态代码块"); } public C(){ System.out.println("C的构造方法"); }}public class LoadDemo { public static void main(String[] args) { new C(); }} 1234567891011121314151617181920232223242526272829303132333435363738 例题3分析

结果输出

A的静态块B的静态块C的静态代码块A的构造方法B的构造方法C的构造方法 1234567

加载过程可以描述为如下: 1. 执行main方法,构造C对象的实例 2. 初始化C,发现B没初始化,去初始化B 3. 初始化B,发现A没初始化,去初始化A 4. 输出A的静态块 5. A初始化完成,初始化B,输出B的静态块 6. B初始化完成,初始化C,输出C的静态代码块 7. 执行C的构造方法,调用父类B的构造方法 8. 执行B的构造方法,调用A的构造方法 9. 执行A的构造方法,调用object的构造方法,然后输出A的构造方法 10. 执行B的构造方法,输出B的构造方法 11. 执行C的构造方法,输出C的构造方法

例题4 class A { static A a = new A(); static { System.out.println("A的静态块"); } { System.out.println("A的普通代码块"); } public A(){ System.out.println("A的构造方法"); System.out.println("aaa = "+aaa +" ; bbb = "+bbb); } public static void staticMethod(){ System.out.println("A的静态方法"); } int aaa = 1; static int bbb = 2;}public class LoadDemo { public static void main(String[] args) { A.staticMethod(); }} 12345678910111213141516171819202322232425262728293031 例题4分析

输出结果

A的普通代码块A的构造方法aaa = 1 ; bbb = 0A的静态块A的静态方法 12345

过程分析

这一题我们可以这么来分析,上面我们说过,java虚拟机会把字节码解析成初始化方法和实例化方法,本例中的初始化方法和示例方法分别如下

类初始化方法 static A a = new A(); static { System.out.println("A的静态块"); } static int bbb = 2; 1234567 类实例化方法 { System.out.println("A的普通代码块"); } int aaa = 1; public A(){//构造方法在最后 System.out.println("A的构造方法"); System.out.println("aaa = "+aaa +" ; bbb = "+bbb); } 1234567891011

执行步骤

入口是main方法,对A类进行了实例化初始化A类,执行类初始化方法类初始化第一句为static A a = new A(),A被实例化,jvm会去执行A的实例化方法执行普通代码块,输出A的普通代码块初始化aaa = 1执行A的构造方法,输出A的构造方法,bbb的值在准备阶段初始化为0,aaa被初始化为1,输出aaa = 1 ; bbb = 0;实例化方法执行完毕,继续执行类初始化方法类初始化方法继续往下执行,下一行为执行静态代码块,输出A的静态代码块然后为bbb赋值2类的初始化完成,执行main中的方法,输出A的静态方法 例题5 class X{ Y y=new Y(); public X(){ System.out.print("X"); }}class Y{ public Y(){ System.out.print("Y"); }}public class Z extends X{ Y y=new Y(); public Z(){ System.out.print("Z"); } public static void main(String[] args) { new Z(); }} 123456789101112131415161718192023222324 例题5分析

输出结果

YXYZ 1

过程分析

执行步骤

入口是main方法,对Z类进行了实例化初始化Z类,执行类的初始化方法,发现X方法没有初始化,去执行X初始化方法,本例子中没有使用static修饰符,所以类初始化方法可以看成是空由于main用的是new方法,会对Z的父类进行构造执行X的实例化方法,先执行Y y=new Y();,输出Y执行X的构造函数,输出X执行Z的实例化方法,先执行Y y=new Y();,输出Y再执行Z的构造方法,输出Z 把输出结果合成,最后输出字符串为YXYZ 例题总结

从上面的例子可以看出类的执行顺序如下

确定类变量的初始值:在类初始化的时候,在准备阶段jvm会为类变量(static修饰的变量)初始化,如对象会被初始化为null,int被初始化为0找到入口执行:找到main方法,开始执行,在执行之前会对main方法所在类进行初始化执行类初始化方法:jvm 会按顺序收集类变量的赋值语句、静态代码块,组成类初始化方法去执行执行对象实例化方法:JVM 会按照收集成员变量的赋值语句、普通代码块,最后收集构造方法,组成对象实例化方法去执行

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。