关于Java基础的一道面试题目
指出下面代码的结果是什么:
- public class Test {
- private static Test test = new Test();
- public static int num1;
- public static int num2=0;
- private Test(){
- num1++;
- num2++;
- }
- public static Test getInstance(){
- return test;
- }
- public static void main(String[] args) {
- Test test = Test.getInstance();
- System.out.println(test.num1);
- System.out.println(test.num2);
- }
- }
这题目主要考察Java的初始化顺序的。结果是1,0
分解一下这段代码执行的过程:
- Test test = Test.getInstance();
java虚拟机运行到Test了,于是开始寻找Test,找到了Test.class,然后开始加载,于是开始初始化。
初始化顺序的总规则:首先静态初始化,然后定义初始化,然后是构造函数初始化;
静态分为两种:
1、静态的成员变量
2、静态代码块 static{}
一般来说首先初始化静态类变量,然后初始化静态代码块。
如果某个类有父类,那么初始化顺序是:
1、对父类进行静态初始化(初始化父类的静态成员变量或者静态代码块)
2、对子类进行静态初始化(初始化子类的静态成员变量或者静态代码块)
3、对父类进行定义初始化(初始化父类的成员变量)
4、对父类进行构造函数初始化
5、对子类进行定义初始化(初始化子类的成员变量)
6、对子类进行构造函数初始化
注意:jvm只在首次使用某个类的时候对其类变量进行一次初始化!
回到正题,jvm初始化Test的时候也是按照上面的顺序进行的:
1、ClassLoader加载Test.class,这个时候Test.class中的三个静态变量已经被装载进内存,并分别赋予了初始值null,0,0 (不知道这样理解对不对)
2、按照顺序对Test.class进行静态初始化,注意这里只对所有显式初始化的变量进行初始化!没有显式初始化的变量不再进行 初始化!比如num1没有被显式的初始化,不再参与这个初始化过程(不知道这个理解对不对)
所以首先初始化的是:
private static Test test = new Test();
3、初始化test变量的时候调用了Test类的构造函数,在Test构造函数中对两外两个静态变量num1、num2进行了++操作,这个 时候num1和num2的初始值应该都是默认值0!进行++操作后,这个时候num1和num2的值都是1
4、完成了对静态变量test的初始化后开始初始化num2,这个时候num1=1,但是num2=0(即为结果)。
题目的延伸:
如果把代码改为:
- public class Test {
- public static int num1;
- public static int num2 = 0;
- private static Test test = new Test();
- private Test() {
- num1++;
- num2++;
- }
- public static Test getInstance() {
- return test;
- }
- public static void main(String[] args) {
- Test test = Test.getInstance();
- System.out.println(test.num1);
- System.out.println(test.num2);
- }
- }
结果将会是:1,1
因为按照顺序,先初始化了num2,后初始化test!
1、装载Test.class
2、给类变量分配空间,赋予初始值
test = null (指向null)
num1 = 0
num2 = 0
3、开始初始化显式赋值的变量(test、num2)
1)初始化num2,将num2赋值为0
2)初始化test
(1)对类Test进行初始化,因为类变量只初始化一次,所以这里不再需要对test进行初始化了
(2)Test没有实例变量,所以不需要初始化实例变量
(3)对Test进行构造函数初始化,对num1和num2进行++操作
(4)初始化完毕,在堆区开辟内存空间存储Test的实例。
3)这个时候num1 = 1,num2 = 1
把代码改为:
- public class Test {
- private static Test test = new Test();
- public int num1;
- public int num2 = 0;
- private Test() {
- num1++;
- num2++;
- }
- public static Test getInstance() {
- return test;
- }
- public static void main(String[] args) {
- Test test = Test.getInstance();
- System.out.println(test.num1);
- System.out.println(test.num2);
- }
- }
结果将会是:1,1
1、装载Test.class
2、给类变量分配空间,赋予初始值
test = null (指向null)
num1 = 0
num2 = 0
3、开始初始化显式赋值的变量(这里是test,因为num1和num2都不是静态的)
1)初始化test
(1)按照初始化顺序,构造Test的时候首先初始化类变量,因为类变量只初始化一次,所以这里不再需要对test进行初始化了
(2)进行定义初始化,初始化num1 和 num2
(3)对Test进行构造函数初始化,对num1和num2进行++操作
(4)初始化完毕,在堆区开辟内存空间存储Test的实例。
3)这个时候num1 = 1,num2 = 1。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~