Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java代码块执行顺序初探

Java代码块执行顺序初探

作者头像
WindCoder
发布于 2018-09-19 09:44:04
发布于 2018-09-19 09:44:04
2.7K00
代码可运行
举报
文章被收录于专栏:WindCoderWindCoder
运行总次数:0
代码可运行

1、仅构造方法

每个类都有构造方法。

如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。

若定义了有参构造方法,编译器将不会提供默认的无参构造,如需要,需要自己构造。

Java继承中对构造函数是不继承的,只是显式或者隐式调用。

实例

父类A

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class A {

    public A(){
        System.out.println("A constructor");
    }


    public A(int a){
        System.out.println("A constructor ,num:"+a);
    }
}

子类B

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class B extends A {

    public B (){
        System.out.println("B constructor");
    }

    public B(int b){
        System.out.println("B constructor ,num:"+b);
    }
}

测试类E

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class E {
    public static void main(String[] args) {
        System.out.println("E main");
        A a = new B();
        System.out.println("-------------AB可爱分割线-------------");
        B b = new B();

        System.out.println("\n---------小可爱分割线-----------\n");

        A a1 = new B(1);
        System.out.println("-------------AB可爱分割线-------------");
        B b1 = new B(2);
    }
}

测试结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
E main
A constructor
B constructor
-------------AB可爱分割线-------------
A constructor
B constructor

---------小可爱分割线-----------

A constructor
B constructor ,num:1
-------------AB可爱分割线-------------
A constructor
B constructor ,num:2

解析

Java继承中对构造函数是不继承的,只是显式或者隐式调用,并且必须是在构造函数第一行。这里是隐式调用了super()。

子类不能继承父类的构造器(构造方法或者构造函数),但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。

如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。

所以先执行父类构造函数,再执行子类构造函数。

2、静态块、构造方法

静态代码块:在java中使用static关键字声明的代码块。

实例1:

父类A

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class A {

    static {
        System.out.println("A constructor before static");
    }

    public A(){
        System.out.println("A constructor");
    }


    public A(int a){
        System.out.println("A constructor ,num:"+a);
    }

    static {
        System.out.println("A constructor after static");
    }
}

子类B

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class B extends A {
    static {
        System.out.println("B constructor before static");
    }
    public B (){
        System.out.println("B constructor");
    }

    public B(int b){
        System.out.println("B constructor ,num:"+b);
    }

    static {
        System.out.println("B constructor after static");
    }
}

测试类E

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class E {
    public static void main(String[] args) {
        System.out.println("E main");
        A a = new B();
        System.out.println("-------------AB可爱分割线-------------");
        B b = new B();

        System.out.println("\n---------小可爱分割线-----------\n");

        A a1 = new B(1);
        System.out.println("-------------AB可爱分割线-------------");
        B b1 = new B(2);
    }
}

测试结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
E main
A constructor before static
A constructor after static
B constructor before static
B constructor after static
A constructor
B constructor
-------------AB可爱分割线-------------
A constructor
B constructor

---------小可爱分割线-----------

A constructor
B constructor ,num:1
-------------AB可爱分割线-------------
A constructor
B constructor ,num:2

解析

静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。

由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。

如果类中包含多个静态代码块,那么将按照"先定义的代码先执行,后定义的代码后执行"。

静态代码块不能存在于任何方法体内。

静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。

当父类与子类都有静态代码块和构造函数的时候,执行顺序如下:

  • 父类静态代码块 > 子类静态代码块(Java虚拟机加载类时,就会执行该块代码)。
  • 父类构造函数 > 子类构造函数 (先有父亲,后有孩子)
  • 如果是多级继承关系的话,高层的父类首先执行,然后依次递减。

总结:静态优先执行,父类优先于子类执行。 静态代码块是在JVM加载类的时候执行的,而且静态代码块执行且仅执行一次

实例2:

子类包含静态属性---子类的实例化。

父类A:同实例1 子类B:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class B extends A {

    public static A b = new B(3);
    public static A a = new A(2);
    public static B c = new B(1);
    static {
        System.out.println("B constructor before static");
    }
    public B (){
        System.out.println("B constructor");
    }

    public B(int b){
        System.out.println("B constructor ,num:"+b);
    }

    static {
        System.out.println("B constructor after static");
    }
}

测试类F

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class F {
    public static void main(String[] args) {
        System.out.println("F main");
        A a = new B();
    }
}

测试结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
F main
A constructor before static
A constructor after static
A constructor
B constructor ,num:3
A constructor ,num:2
A constructor
B constructor ,num:1
B constructor before static
B constructor after static
A constructor
B constructor

解析: 进入main函数,先执行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 System.out.println("F main");

得到

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
F main

执行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
A a = new B();

后,先执行父类的静态块得到

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
A constructor before static
A constructor after static

执行子类静态成员变量(方法块),由于子类包含静态属性和方法,按照“先定义的代码先执行,后定义的代码后执行”的规则,先执行静态属性部分,即:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   public static A b = new B(3);
   public static A a = new A(2);
   public static B c = new B(1);

此时由于父类静态部分已经执行,故执行相应的构造函数得到(感觉说是中断类加载过程,执行实例初始化过程更贴切些,这部分可以考虑参考看最后关于“暂停类加载”部分):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
A constructor
B constructor ,num:3
A constructor ,num:2
A constructor
B constructor ,num:1

由于之后子类含有静态块,故继续执行静态块中的打印:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
B constructor before static
B constructor after static

最后执行最终的无参构造函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
A constructor
B constructor

若父类A,改成如下会更直观:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class A {

   public static A a = new B(12);

   static {
       System.out.println("A constructor before static");
   }

   public A(){
       System.out.println("A constructor");
   }


   public A(int a){
       System.out.println("A constructor ,num:"+a);
   }

   static {
       System.out.println("A constructor after static");
   }
}

此时结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
F main
A constructor
B constructor ,num:12
A constructor before static
A constructor after static
A constructor
B constructor ,num:3
A constructor ,num:2
A constructor
B constructor ,num:1
B constructor before static
B constructor after static
A constructor
B constructor

实例3:

子类和父类存在相同的静态方法和非静态方法时 父类A

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class A {

   static {
       System.out.println("A constructor before static");
   }

   public static void getClassStaticFunc(){
       System.out.println("get A static Function");
   }

   public  void getClassFunc(){
       System.out.println("get A  Function");
   }

   public A(){
       System.out.println("A constructor");
   }


   public A(int a){
       System.out.println("A constructor ,num:"+a);
   }

   static {
       System.out.println("A constructor after static");
   }

}

子类B

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class B extends A {

   static {
       System.out.println("B constructor before static");
   }

   public static void getClassStaticFunc(){
       System.out.println("get B static Function");
   }

   public  void getClassFunc(){
       System.out.println("get B  Function");
   }

   public B (){
       System.out.println("B constructor");
   }

   public B(int b){
       System.out.println("B constructor ,num:"+b);
   }

   static {
       System.out.println("B constructor after static");
   }

}

测试类F

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class F {
   public static void main(String[] args) {
       System.out.println("F main");
       A a = new B();
       a.getClassStaticFunc();
       a.getClassFunc();
   }
}

测试结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
F main
A constructor before static
A constructor after static
B constructor before static
B constructor after static
A constructor
B constructor
get A static Function
get B  Function

解析java中静态属性和静态方法可以被继承,但是没有被重写(overwrite)而是被隐藏。

原因:

1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。 2). 多态之所以能够实现依赖于继承、接口和重写、重载(继承和重写最为关键)。有了继承和重写就可以实现父类的引用指向不同子类的对象。重写的功能是:"重写"后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。 3). 静态属性、静态方法和非静态的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。

3、构造代码块、静态块、构造方法

构造块:直接在类中定义且没有加static关键字的代码块称为{}构造代码块。

构造代码块在创建对象时被调用,每次创建对象都会被调用,并且构造代码块的执行次序优先于类构造函数。

实例1

父类A

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class A {

   {
       System.out.println("A constructor before static before 构造块");
   }
   static {
       System.out.println("A constructor before static");
   }

   public A(){
       System.out.println("A constructor");
   }


   public A(int a){
       System.out.println("A constructor ,num:"+a);
   }

   static {
       System.out.println("A constructor after static");
   }

   {
       System.out.println("A constructor after static after 构造块");
   }
}

子类B

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class B extends A {

   {
       System.out.println("B constructor before static before 构造块");
   }

   static {
       System.out.println("B constructor before static");
   }
   public B (){
       System.out.println("B constructor");
   }

   public B(int b){
       System.out.println("B constructor ,num:"+b);
   }

   static {
       System.out.println("B constructor after static");
   }

   {
       System.out.println("B constructor after static after 构造块");
   }
}

测试类E

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class E {
    public static void main(String[] args) {
        System.out.println("E main");
        A a = new B();
        System.out.println("-------------AB可爱分割线-------------");
        B b = new B();

        System.out.println("\n---------小可爱分割线-----------\n");

        A a1 = new B(1);
        System.out.println("-------------AB可爱分割线-------------");
        B b1 = new B(2);
    }
}

测试结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
E main
A constructor before static
A constructor after static
B constructor before static
B constructor after static
A constructor before static before 构造块
A constructor after static after 构造块
A constructor
B constructor before static before 构造块
B constructor after static after 构造块
B constructor
-------------AB可爱分割线-------------
A constructor before static before 构造块
A constructor after static after 构造块
A constructor
B constructor before static before 构造块
B constructor after static after 构造块
B constructor

---------小可爱分割线-----------

A constructor before static before 构造块
A constructor after static after 构造块
A constructor
B constructor before static before 构造块
B constructor after static after 构造块
B constructor ,num:1
-------------AB可爱分割线-------------
A constructor before static before 构造块
A constructor after static after 构造块
A constructor
B constructor before static before 构造块
B constructor after static after 构造块
B constructor ,num:2

解析: 先静态后构造块,先父类后子类。

实例2:

对于非继承类J

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class J {
    public static int k = 0;
    static {
        print("静态块2");
    }
    public static J t1 = new J("t1");
    public static J t2 = new J("t2");
    public static int i = print("i");
    public static int n = 99;
    public int j = print("j");

    {
        print("构造块");
    }
    static {
        print("静态块");
    }

    public J(String str) {
        System.out.println((++k) + ":" + str + "   i=" + i + "    n=" + n);
        ++i;
        ++n;
    }

    public static int print(String str) {
        System.out.println((++k) + ":" + str + "   i=" + i + "    n=" + n);
        ++n;
        return ++i;
    }

    public static void main(String args[]) {
        J t = new J("init");
    }
}

测试结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1:静态块2   i=0    n=0
2:j   i=1    n=1
3:构造块   i=2    n=2
4:t1   i=3    n=3
5:j   i=4    n=4
6:构造块   i=5    n=5
7:t2   i=6    n=6
8:i   i=7    n=7
9:静态块   i=8    n=99
10:j   i=9    n=100
11:构造块   i=10    n=101
12:init   i=11    n=102

解析: 典型的暂停类加载。即:类加载过程中,可能调用了实例化过程(因为static可以修饰方法,属性,代码块,内部类),此时则会暂停类加载过程而先执行实例化过程(被打断),执行结束再进行类加载过程。

类加载过程,不涉及构造方法。 实例化过程,涉及构造方法。

先类加载,执行static修饰的部分,遇到属性实例化,中断去执行实例化,完成后继续类加载,如此循环直至结束。

小结

无继承的的初始化顺序

静态成员变量(静态代码块)→普通成员变量→构造器

有继承的初始化顺序

父类静态成员变量、静态代码块→子类静态成员变量、静态代码块→父类普通成员变量、普通代码块→父类构造器→子类普通成员变量、普通代码块→子类构造器。

其他

类加载过程,不涉及构造方法

实例化过程,涉及构造方法

1、类中所有属性的默认值(一举而成)

2、父类静态属性初始化,静态块,静态方法的声明(按出现顺序执行)

3、子类静态属性初始化,静态块,静态方法的声明 (按出现顺序执行)

4 、调用父类的构造方法,

首先父类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)

然后父类构造方法

5、 调用子类的构造方法,

首先子类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)

然后子类构造方法

参考资料

深入了解Java程序执行顺序

Java中普通代码块,构造代码块,静态代码块区别及代码示例

Java:构造器,构造代码块,静态代码块的执行顺序

Java 继承

在继承中的问题,关于静态代码块,子类和父类的静态代码块的执行情况

JAVA静态方法是否可以被继承?

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
举例说明Java中代码块的执行顺序
可以看到,静态代码块只在类加载时执行一次,在main方法之前执行。实例代码块在每次创建对象时执行,然后再执行构造方法。
用户10604450
2024/03/15
1260
代码块总结
这种形式的程序段我们将其称之为代码块,所谓代码块就是用大括号({})将多行代码封装在一起,形成一个独立的数据体,用于实现特定的算法。一般来说代码块是不能单独运行的,它必须要有运行主体。在Java中代码块主要分为四种:
栋先生
2018/09/29
8590
【小家java】类中静态代码块、构造代码块、静态变量、成员变量执行顺序和继承逻辑
诚如各位所知,java的三大特性:封装、继承、多态。其中继承,是java中最有学问的一点也是最相对来说最难理解的一些东西,本文针对于此,做一些实例分析,希望能够帮助大家理解java中的继承机制
YourBatman
2019/09/03
1.5K0
【小家java】类中静态代码块、构造代码块、静态变量、成员变量执行顺序和继承逻辑
Java基础-08(01)总结帮助文档,代码块,继承
1:如何制作帮助文档(了解) (1)写一个类 (2)加入文档注释 (3)通过javadoc工具生成即可 javadoc -d 目录 -author -version ArrayTool.jav
Java帮帮
2018/03/15
8340
Java基础-08(01)总结帮助文档,代码块,继承
静态代码块、静态变量,构造代码块、实例变量的执行顺序和继承逻辑
各位小伙伴大家好,我是A哥。如果问:Java的三大特性是什么?你顺口就能答出:封装、继承、多态。如果继续问:你真的了解Java中的继承吗?
YourBatman
2022/05/06
1K0
静态代码块、静态变量,构造代码块、实例变量的执行顺序和继承逻辑
充电篇:再也不怕面试官问你Java基础了
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。
小熊学Java
2023/07/12
1590
充电篇:再也不怕面试官问你Java基础了
一文整懂 Java 中静态代码块 / 初始块 / 构造方法的执行顺序
“ 相信,刷过面试题应该都碰到过很多题,关于类继承后 Java 中静态代码块 / 初始块 / 构造方法的执行顺序问题,每每记一下又忘了,那么,今天来用不多的时间复习一下”
拾荒者的笔记
2020/05/25
9060
静态代码块、非静态代码块、构造函数三者执行顺序
主要探讨一下关于静态代码块,非静态代码块,构造函数的执行顺序。 如有错误,欢迎指出。
HaC
2020/12/30
5190
Java代码块
在 Java 中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程)
闲花手札
2021/08/24
9030
【Java】父子类执行代码顺序
父类静态代码块–>子类静态代码块–>父类代码块–>父类构造方法–>子类代码块–>子类构造方法
瑞新
2020/12/07
4850
彻底搞懂java程序的初始化顺序
在java程序中,当实例化对象时,对象的所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建对象。
全菜工程师小辉
2019/08/16
2.5K0
夯实Java基础系列7:一文读懂Java 代码块和执行顺序
本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看
程序员黄小斜
2019/09/27
6390
java 语言【继承和多态】
Java中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是 现实世界错综复杂,事物之间可能会存在一些关联,那在设计程序是就需要考虑。 比如:狗和猫,它们都是一个动物。 使用Java语言来进行描述,就会设计出:
用户11319080
2024/10/17
1070
java 语言【继承和多态】
Java基础系列5:Java代码的执行顺序
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架。
说故事的五公子
2019/11/14
7880
【面试题】Java中子类和父类静态代码块、非静态代码块、构造函数的执行顺序总结一览表
在面试的时候,有时候我们会被问到这样的问题:子类A继承父类B,A a = new A();则父类B的构造函数、父类B静态代码块、父类B非静态代码块、子类A构造函数、子类A静态代码块、子类A非静态代码块执行的先后顺序是什么?
凯哥Java
2022/12/02
6930
【面试题】Java中子类和父类静态代码块、非静态代码块、构造函数的执行顺序总结一览表
java小心机(5)| 浅谈类成员初始化顺序
类成员什么时候会被初始化呢?一般来说:"类的代码在初次使用时才被加载",加载过程包括了初始化。 比如说new A()调用构造函数时,类中全部成员都会被初始化。 但对于static域(包括静态成员变量
KEN DO EVERTHING
2019/01/17
4600
Java关键字final、static总结与对比
Java关键字final有“不可改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。
chenchenchen
2022/03/09
9290
Java关键字final、static总结与对比
干货——详解Java中的关键字
在平时编码中,我们可能只注意了这些static,final,volatile等关键字的使用,忽略了他们的细节,更深层次的意义。
Janti
2018/08/01
4350
干货——详解Java中的关键字
全面理解Java中继承关系
继承是面向对象思想的三大特性之一,使类与类之间产生特殊 - 一般的关系,即is-a关系。继承是从已有类中派生出新的类,新的类能吸收已有类的属性和方法,并且能拓展新的属性和行为。在Java中使用extends关键字表示继承,语法表示为: class 子类 extends 父类{}子类被称为派生类,父类又被称为超类。子类继承父类,表名子类是一种特殊的父类,子类拥有父类的属性和方法,并且子类可以拓展具有父类所没有的一些属性和方法。子类即是不扩展父类,也能维持拥有父类的操作。
用户7886150
2021/01/30
1.6K0
详解java中的四种代码块
使用synchronized(){}包裹起来的代码块,在多线程环境下,对共享数据的读写操作是需要互斥进行的,否则会导致数据的不一致性。同步代码块需要写在方法中。
三哥
2019/05/10
3.3K0
详解java中的四种代码块
推荐阅读
相关推荐
举例说明Java中代码块的执行顺序
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验