通过面试题解析 Java 类加载机制

论坛 期权论坛     
niminba   2021-5-22 14:57   226   0
<p>在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题:</p>
<div class="blockcode">
<pre class="brush:java;">
class Grandpa
{
static
{
System.out.println("爷爷在静态代码块");
}
}
class Father extends Grandpa
{
static
{
System.out.println("爸爸在静态代码块");
}
public static int factor = 25;
public Father()
{
System.out.println("我是爸爸~");
}
}
class Son extends Father
{
static
{
System.out.println("儿子在静态代码块");
}
public Son()
{
System.out.println("我是儿子~");
}
}
public class InitializationDemo
{
public static void main(String[] args)
{
System.out.println("爸爸的岁数:" + Son.factor); //入口
}
}
</pre>
</div>
<p>请写出最后的输出字符串。</p>
<p>正确答案是:</p>
<div class="blockcode">
<pre class="brush:java;">
爷爷在静态代码块
爸爸在静态代码块
爸爸的岁数:25
</pre>
</div>
<p>我相信很多同学看到这个题目之后,表情是崩溃的,完全不知道从何入手。有的甚至遇到了几次,仍然无法找到正确的解答思路。</p>
<p><strong>其实这种面试题考察的就是你对Java类加载机制的理解。</strong></p>
<p>如果你对Java加载机制不理解,那么你是无法解答这道题目的。</p>
<p>所以这篇文章,我先带大家学习Java类加载的基础知识,然后再实战分析几道题目让大家掌握思路。</p>
<p>下面我们先来学习下Java类加载机制的七个阶段。</p>
<p><strong>Java类加载机制的七个阶段</strong><br>
</p>
<p>当我们的Java代码编译完成后,会生成对应的 class 文件。接着我们运行java Demo命令的时候,我们其实是启动了JVM 虚拟机执行 class 字节码文件的内容。而 JVM 虚拟机执行 class 字节码的过程可以分为七个阶段:<strong>加载、验证、准备、解析、初始化、使用、卸载。</strong></p>
<p><strong>加载</strong><br>
</p>
<p>下面是对于加载过程最为官方的描述。</p>
<blockquote>
<p>加载阶段是类加载过程的第一个阶段。在这个阶段,JVM 的主要目的是将字节码从各个位置(网络、磁盘等)转化为二进制字节流加载到内存中,接着会为这个类在 JVM 的方法区创建一个对应的 Class 对象,这个 Class 对象就是这个类各种数据的访问入口。</p>
</blockquote>
<p>其实加载阶段用一句话来说就是:把代码数据加载到内存中。这个过程对于我们解答这道问题没有直接的关系,但这是类加载机制的一个过程,所以必须要提一下。</p>
<p><strong>验证<br>
</strong></p>
<p>当 JVM 加载完 Class 字节码文件并在方法区创建对应的 Class 对象之后,JVM 便会启动对该字节码流的校验,只有符合 JVM 字节码规范的文件才能被 JVM 正确执行。这个校验过程大致可以分为下面几个类型:</p>
<ul>
  <li>JVM规范校验。JVM 会对字节流进行文件格式校验,判断其是否符合 JVM 规范,是否能被当前版本的虚拟机处理。例如:文件是否是以 0x cafe bene开头,主次版本号是否在当前虚拟机处理范围之内等。</li>
  <li>代码逻辑校验。JVM 会对代码组成的数据流和控制流进行校验,确保 JVM 运行该字节码文件后不会出现致命错误。例如一个方法要求传入 int 类型的参数,但是使用它的时候却传入了一个 String 类型的参数。一个方法要求返回 String 类型的结果,但是最后却没有返回结果。代码中引用了一个名为 Apple 的类,但是你实际上却没有定义 Apple 类。</li>
</ul>
<p>当代码数据被加载到内存中后,虚拟机就会对代码数据进行校验,看看这份代码是不是真的按照JVM规范去写的。这个过程对于我们解答问题也没有直接的关系,但是了解类加载机制必须要知道有这个过程。</p>
<p><strong>准备(重点)</strong><br>
</p>
<p>当完成字节码文件的校验之后,JVM 便会开始为类变量分配内存并初始化。这里需要注意两个关键点,即内存分配的对象以及初始化的类型。</p>
<p>内存分配的对象。Java 中的变量有「类变量」和「类成员变量」两种类型,「类变量」指的是被 static 修饰的变量,而其他所有类型的变量都属于「类成员变量」。在准备阶段,JVM 只会为「类变量」分配内存,而不会为「类成员变量」分配内存。「类成员变量」的内存分配需要等到初始化阶段才开始。<br>
</p>
<p>例如下面的代码在准备阶段,只会为 factor 属性分配内存,而不会为 website 属性分配内存。</p>
<div class="blockcode">
<pre class="brush:java;">
public static int factor = 3;
public String website = www.cnblogs.com/chanshuyi;</pre>
</div>
<p>初始化的类型。在准备阶段,JVM 会为类变量分配内存,并为其初始化。但是这里的初始化指的是为变量赋予 Java 语言中该数据类型的零值,而不是用户代码里初始化的值。<br>
</p>
<p>例如下面的代码在准备阶段之后,sector 的值将是 0,而不是 3。</p>
<div class="blockcode">
<pre class="brush:java;">
public st
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1060120
帖子:212021
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP