大部分教程都会告诉我们静态初始化块和静态字段总是在初始化块和普通类字段前运行,事实上也确实如此,直到我看到下面这样的代码:

public class Test {

	static Test test = new Test();

	{
System.out.println("normal");
} static{
System.out.println("static");
} public static void main(String [] args){
Test test = new Test();
} }

可以先猜一猜结果,然后我们编译运行:

$ javac Test.java
$ java Test normal
static
normal

难道不应该是先打印出static才对吗?这里我就不卖关子了,答案很简单。我们先来看一段话:

every constructor written in the Java programming language (JLS §8.8) appears as an instance initialization method that has the special name <init>. The initialization method of a class or interface has the special name <clinit>.

这段话来自jls,意思是普通的构造函数和初始化块会被放在一个叫<init>的方法里,在创建对象实例时调用;而类自身的初始化包括静态字段和静态方法会被放在叫<clinit>的方法里。而静态字段和静态块的初始化优先级相同,顺序遵从在代码中排列的顺序,也就是谁在前面谁先执行。

因此执行顺序已经明了了:

  1. 因为main函数中要创建类的实例,所以类会先被加载,这是调用了<clinit>
  2. 静态字段在静态初始化块之前,所以先执行初始化
  3. 静态字段调用了new Test()创建类的实例,这时调用了<init>
  4. 因为普通的初始化块在<init>里,所以被调用,首先打印出了normal
  5. 静态字段初始化完成后开始执行紧随其后的静态块,打印出static
  6. 随后回到main函数中,类的初始化只会运行一次,所以这次只有<init>运行,打印出normal

因此看起来像是普通初始化块在静态块之前运行了,实际上只是静态字段的初始化导致了普通初始化块的提前执行。

知道了原理后,想下面这样的代码会输出什么自然也不在话下了:

public class Init {
private int v = 0;
static Init obj2 = new Init(); // 只能是static字段,想一想为什么 static {
System.out.println("static 2");
} static Init obj1 = new Init(); {
System.out.println("from init");
} static {
System.out.println("static 1");
} public static void main(String[] args) {
var o1 = new Init();
var o2 = new Init();
}
}

当然,在实际的编码中静态块应该只负责静态字段的处理,普通的初始化块只负责对普通类字段的处理,上面的代码是不应该被模仿的。

参考

https://www.zhihu.com/question/50374553

java中类的普通初始化块一定在静态初始化块后运行吗的更多相关文章

  1. Java学习笔记11---静态成员变量、静态代码块、成员变量及构造方法的初始化或调用顺序

    当创建一个对象时,各种成员变量及构造方法的初始化或调用顺序是怎样的呢? (1).如果类尚未加载,则先初始化静态成员变量和静态代码块,再初始化成员变量,最后调用相应的构造方法: (2).如果类已经加载过 ...

  2. JAVA程序执行顺序(静态代码块》非静态代码块》静态方法》构造函数)

    总结:静态代码块总是最先执行. 非静态代码块跟非静态方法一样,跟对象有关.只不过非静态代码块在构造函数之前执行. 父类非静态代码块.构造函数执行完毕后(相当于父类对象初始化完成), 才开始执行子类的非 ...

  3. java中静态代码块,非静态代码块,构造函数

    关于静态代码块 静态代码块的写法: static { System.out.println("我是静态代码块"); } 静态代码块的特点: 1.执行优先级高于非静态的初始化块,它会 ...

  4. Java子父类间静态代码块、非静态代码块、构造方法的执行顺序

    子类A继承父类B,A a=new A(); 正确的执行顺序是:父类B静态代码块->子类A静态代码块->父类B非静态代码块->父类B构造函数->子类A非静态代码块->子类A ...

  5. java 对象的初始化流程(静态成员、静态代码块、普通代码块、构造方法)

    一.java对象初始化过程 第一步,加载该类,一个java对象在初始化前会进行类加载,在JVM中生成Class对象.加载一个类会进行如下操作,下面给出递归描述.(关于Class对象详见反射 点击这里) ...

  6. Java中静态变量、静态代码块、非静态代码块以及静态方法的加载顺序

    在研究单例设计模式的时候,用到了静态变量和静态方法的内容,出于兴趣,这里简单了解一下这四个模块在类初始化的时候的加载顺序. 经过研究发现,它们的加载顺序为: 1.非静态代码块 2.静态变量或者静态代码 ...

  7. 浅谈Java中静态代码块和非静态代码块

    静态代码块: static{} 执行优先级高于非静态的初始化块,它会在类初始化(类初始化这个问题改天再详细讨论)的时候执行一次,执行完成便销毁,它仅能初始化类变量,即static修饰的数据成员. 非静 ...

  8. Java静态代码块与非静态代码块

    静态代码块,格式是 static{ },随着类的加载而加载,且只执行一次. 在程序中,执行的优先级最高. 非静态代码块,格式是{ },在创建对象的时候运行(即new一个对象的时候),每创建一次对象就执 ...

  9. Java的初始化块、静态初始化块、构造函数的执行顺序及用途探究

    Java与C++有一个不同之处在于,Java不但有构造函数,还有一个”初始化块“(Initialization Block)的概念.下面探究一下它的执行顺序与可能的用途. 执行顺序 首先定义A, B, ...

  10. Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序

    静态代码块:用staitc声明,jvm加载类时执行,仅执行一次构造代码块:类中直接用{}定义,每一次创建对象时执行.执行顺序优先级:静态块,main(),构造块,构造方法. 构造函数 public H ...

随机推荐

  1. C语言一个单链表L=(a1 , a2 , … , an-1 , an),其逆单链表定义为L’=( an , an-1 , … , a2 , a1),要求逆单链表仍占用原单链表的空间。

    /* 开发者:慢蜗牛 开发时间:2020.6.11 程序功能:顺序输出和逆序输出 */ #include<stdio.h> #include<malloc.h> #define ...

  2. 微信小程序敏感内容检测

    获取access_token access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token.开发者需要进行妥善保存.access_token的存储至少要保 ...

  3. CSS+HTML初学跟踪项目记录笔记【防丢失】(文章发布系统)二【鸽了】

    贴上源代码 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEnc ...

  4. 深入理解HarmonyOS UIAbility:生命周期、WindowStage与启动模式探析

    本文分享自华为云社区<深入理解HarmonyOS UIAbility:生命周期.WindowStage与启动模式探析>,作者:柠檬味拥抱. UIAbility组件概述 UIAbility组 ...

  5. 【GIT-精讲】从零玩转Git-基础理论

    关于版本控制 一.什么是版本控制 版本控制(Version Control Systems)版本控制(Revision control)是一种软件工程技巧 在开发的过程中,确保由不同人所编辑的同一档案 ...

  6. Python——第五章:csv模块

    未来我们会使用爬虫获取到一些json文件,例如去英雄联盟官方爬取英雄的数据库 查看代码 {"hero":[{"heroId":"1",&qu ...

  7. CentOS系统中yum的基本用法

    最小化安装系统时,yum可能会因为网卡配置问题,随机启动配置,导致无法使用, 在shell里面输入:yum --help ,结果显示 yum 已经正常安装了!!到底是哪里出了问题呢?经过网友的提示,我 ...

  8. 良心国产工具,比Xshell好用还免费!

    使用或维护Linux系统的都知道,我们日常对服务器的操作,一般都会借助SSH工具远程登录到服务器之后进行操作.常用的SSH工具有不少,比如:Xshell.Putty.SSH Secure Shell ...

  9. 解决 git中用vim编写文件时,无法写进文字字母以及光标无法移到最右边的问题

    解决方法:可以切换到英文输入法,然后按'a' 或者 'i'  或者 'o' 或者's'  等.s好像会删掉一个字母.o会使光标移到文末.

  10. C++ 惯用法之 RAII

    RAII(Resource Acquisition Is Initialization)资源获取即初始化,是 C++ 中最基本.应用最广范的惯用法(idiom)之一. RAII 的基本思想是通过构造/ ...