昨天看到群里面有人分享了一道题目,我答错了,于是趁机了解了下Java的类/对象初始化过程:

程序的输出见文章最后

程序A主要考察的是 类实例初始化 。简单验证了下,类实例初始化过程如下:父类实例初始化

构造块/变量初始化(按照文本顺序执行)

构造函数

程序B考察的则是 类初始化 。类初始化的过程如下:父类初始化

static变量初始化/static块(按照文本顺序执行)

但是我们必须做到 面向接口编程,而不是面向实现编程(Program to an ‘interface’, not an ‘implementation’) 。

于是就得看看 Java Language Specification 了。其中类初始化过程 如下:每个类都有一个初始化锁LC,进程获取LC(如果没有获取到,就一直等待)

如果C正在被其他线程初始化,释放LC并等待C初始化完成

如果C正在被本线程初始化,即 *递归初始化 *,释放LC

如果C已经被初始化了,释放LC

如果C处于erroneous状态,释放LC并抛出异常NoClassDefFoundError

否则,将C标记为正在被本线程初始化,释放LC;然后, 初始化那些final且为基础类型的类成员变量

初始化C的父类SC和各个接口SI_n (按照implements子句中的顺序来) ;如果SC或SIn初始化过程中抛出异常,则获取LC,将C标记为erroneous,并通知所有线程,然后释放LC,然后再抛出同样的异常。

从classloader处获取assertion是否被打开

接下来, 按照文本顺序执行类变量初始化和静态代码块,或接口的字段初始化,把它们当作是一个个单独的代码块。

如果执行正常,获取LC,标记C为已初始化,并通知所有线程,然后释放LC

否则,如果抛出了异常E。若E不是Error,则以E为参数创建新的异常ExceptionInInitializerError作为E。如果因为OutOfMemoryError导致无法创建ExceptionInInitializerError,则将OutOfMemoryError作为E。

获取LC,将C标记为erroneous,通知所有等待的线程,释放LC,并抛出异常E。

可以看到JLS确实规定了父类先初始化(7)、static块和类变量赋值按照文本顺序来(9)。

然后看看类实例的初始化:开始调用构造函数(给参数赋值)

如果这个构造函数在开始就调用了其他构造函数,那么调用新的构造函数,并按照本规则处理。如果执行过程中抛出异常,则整个过程也抛出同样的异常。如果正常,继续。

如果构造函数没有在开始就调用其他构造函数。如果本类不是Object,那么构造函数会隐式或者显式的 调用父类的构造方法 。父类构造方法也依本规则处理。如果执行过程中抛出异常,则整个过程也抛出同样的异常。如果正常,继续。

执行实例初始化和实例变量初始化。顺序 按照文本顺序 来处理——从左到右、从上到下。如果执行过程中抛出异常,则整个过程也抛出同样的异常。如果正常,继续。

执行剩下的构造函数。如果执行过程中抛出异常,则整个过程也抛出同样的异常。

JLS特意提到,如果子类覆盖了父类的方法,则在构造函数中, 调用的方法也是子类的 。

接下来一个一个看代码:

// 程序A

// 父类

class Parent {

int i = 1;

Parent() {

System.out.println(i);

int x = getValue();

System.out.println(x);

}

{i = 2;}

protected int getValue() {return i;}

}

// 子类

class Son extends Parent {

int j = 1;

Son() {j = 2;}

protected int getValue() {return j;}

}

class Test {

public static void main(String[] args) {

Son son = new Son();

System.out.println(son.getValue());

}

}21行,开始调用Son的构造函数

16行,Son的构造函数开始之前,初始化Parent

4行,开始执行i的变量初始化

10行,开始执行构造代码块

6-8行,开始执行Parent的构造函数。注意,调用的getValue方法是子类的,而此时Son.j还没有被构造函数、变量赋值语句初始化,此时Son.j是0。(输出2,0)

回到16行,继续执行Son的构造函数

22行,打印此时Son.j的值。(输出2)

所以,程序A的输出是:

接下来看程序B:

// 程序B

public class MagimaTest {

public static void main(String[] args) {

magimaFunction();

}

static MagimaTest st = new MagimaTest();

static {

System.out.println("1");

}

{

System.out.println("2");

}

MagimaTest() {

System.out.println("3");

System.out.println("a=" + a + ",b=" + b);

}

public static void magimaFunction() {

System.out.println("4");

}

int a = 110;

static int b = 112;

}3行,在执行main之前,需要初始化MagimaTest类。

6行,初始化st成员变量,开始初始化st实例。

13开始调用构造函数,但是开始前,需要处理成员变量初始化

10行,执行构造代码块(输出2)

20行,初始化a变量

14行,继续执行构造函数。此时a为110,b尚未初始化,所以是0(输出3,a=110,b=0)

7行,st成员变量初始化结束,执行下一个static代码块(输出1)

21行,继续初始化下一个static成员变量b

4行,调用magimaFunction(输出4)

所以输出是:

2

3

a=110,b=0

1

4

好了,看完了解析。那么我再出一个题目吧: 如果将程序B中的MagimaTest.b改为final的,输出会变化吗?

java类初始化_Java的类/实例初始化过程相关推荐

  1. java 延迟初始化_Java并发编程——延迟初始化占位类模式

    --仅作笔记使用,内容多摘自<java并发编程实战> 在并发编程中,如果状态变量仅在单个线程中初始化和使用,自然是线程安全的,但一旦涉及到线程间的数据交互,如何声明一个用于多线程的单例状态 ...

  2. java 虚拟机 初始化_Java虚拟机 类初始化 阶段

    Java虚拟机使用某个类的过程,可分为七个阶段: 加载 - 验证 - 准备 - 解析 - 初始化 - 使用 - 卸载 本文只介绍在什么情况下对类进行初始化. Java虚拟机规范对何时进行类的初始化做了 ...

  3. java 实例变量初始化_java学习之实例变量初始化

    实例变量的初始化方法 第一种:通过构造函数进行初始化. 第二种:通过声明实例字段初始化. 第三种:通过对象代码块初始化. 通过构造函数进行初始化方法 通过构造函数进行对象初始化,必须在类中声明一个带参 ...

  4. java+character类使用_Java Character类应用实例

    之前给大家讲了一下什么是Java Character类以及它的常用方法,下面的话要给大家分享的就是Java Character类的应用实例. 在注册会员的时候,要验证用户输入的用户名.密码.性别.年龄 ...

  5. java 枚举 注解_Java枚举类和注解梳理

    1. 枚举类 1. 枚举类的使用 枚举类的理解:类的对象只有有限个,确定的.我们称此类为枚举类. 当需要定义一组常量时,强烈建议使用枚举类. 如果枚举类中只有一个对象,则可以作为单例模式的实现方式. ...

  6. java异常判断_Java异常类

    异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的异常. ...

  7. java static变量何时初始化_Java 静态变量何时初始化?

    小编典典 从请参阅Java静态变量方法: 它是一个属于类而不属于对象(实例)的变量 静态变量在执行开始时仅初始化一次.在初始化任何实例变量之前,将首先初始化这些变量 该类的所有实例共享一个副本 静态变 ...

  8. java file 初始化_java类加载和对象初始化

    对象初始化过程: 1.首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化: 2.然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化: 3.其次,初始化父 ...

  9. java 匿名初始化_Java:双括号初始化 /匿名内部类初始化法

    偶然见到一种初始化方式,感到十分新奇: //新建一个列表并赋初值A.B.C ArrayList list = new ArrayList() {{ add("A"); add(&q ...

最新文章

  1. 获取json的节点名称
  2. System.Transactions事务超时设置
  3. ubuntu 环境下调试mysql源码_Linux中eclipse调试mysql源代码
  4. COND SWITCH 操作符
  5. 通渭县义岗川镇之行(2020年11月19日)
  6. 一件登录facebook_我从Facebook的R教学中学到的6件事
  7. 第一百零五期:5年前,跳槽涨薪,你笑了,5年后,跳槽降薪,你慌了!
  8. java 初始化参数_我们如何在Java中的对象参数中初始化数组?
  9. java工程师面试经典题目整理
  10. 背景图片的位置(HTML、CSS)
  11. setTimeout(), nextTick(),setImmediate()区别 ZT~
  12. USB-CAN-TOOL CAN分析仪上位机使用说明
  13. 迅为4418/6818开发板 Yocto 系统烧写
  14. 设计师常用网站,建议收藏
  15. 面试官:什么是BFC?BFC有什么特性?如何创建BFC?BFC有什么作用?
  16. 作业二:词云制作 使用软件wordart
  17. String 类 TRYTRY
  18. 香港网络新危机:黑客入侵网上银行账户买卖股票
  19. P4766 [CERC2014]Outer space invaders(区间dp)
  20. qq群淘客怎么引流?淘客新手如何利用qq群引流?

热门文章

  1. 笔记 编写可读性代码的艺术
  2. json字符串使用注意问题
  3. Linux中的rz和sz命令
  4. 【Python】range和xrange区别
  5. IOS中JSON数据的解析
  6. Java 9终于要包含Jigsaw项目了
  7. Node.js新手教程——怎样实现文件上传功能
  8. 如何在ant里import
  9. 在ASP.NET中实现AJAX
  10. C# Reflection