关于静态代码块的执行顺序,很简单的一道题,应该所有人都会吧?

 2023-09-05 阅读 45 评论 0

摘要:之前面试的时候做过代码块和构造方法的执行顺序,当时虽然半蒙半猜作对了,但是对这个还不是特别的了解,所以就想看看今天能不能彻底搞懂,即帮助大家、也帮助自己。 简单题 9 ++ 在 Java 中有静态代码块、非静态代码块(构造代码块&#

之前面试的时候做过代码块和构造方法的执行顺序,当时虽然半蒙半猜作对了,但是对这个还不是特别的了解,所以就想看看今天能不能彻底搞懂,即帮助大家、也帮助自己。

简单题

9

++

在 Java 中有静态代码块、非静态代码块(构造代码块)、普通代码块,还有静态变量、成员变量,来做道题看看你对这些代码块和变量赋值的执行顺序是否真的了解了?

上代码

public class Address {private String province;public Address(String province) {this.province=province;System.out.println("-- Address 的构造方法:province="+this.province+"");}}
public class User {private static final Address address1=new Address("guangdong");private Address address2=new Address("guangxi");static {System.out.println("-- User 的静态代码块1--");}{System.out.println("-- User 的非静态代码块1--");}public User() {{System.out.println("-- User 的普通代码块1--");}System.out.println("-- User 的构造方法--");{System.out.println("-- User 的普通代码块2--");}}private static final Address address3=new Address("hubei");private Address address4=new Address("hunan");{System.out.println("-- User 的非静态代码块2--");}static {System.out.println("-- User 的静态代码块2--");}}
public class StaticCodeTest {/*** 静态代码块、非静态代码块、构造方法、静态变量、成员变量的运执行顺序。*/public static void main(String[] args) {new User();}
}

可以先写下自己的答案哦。

来对答案吧!

-- Address 的构造方法:province=guangdong
-- User 的静态代码块1--
-- Address 的构造方法:province=hubei
-- User 的静态代码块2--
-- Address 的构造方法:province=guangxi
-- User 的非静态代码块1--
-- Address 的构造方法:province=hunan
-- User 的非静态代码块2--
-- User 的普通代码块1--
-- User 的构造方法--
-- User 的普通代码块2--普通代码块2--

可以看到,静态代码块和静态变量的赋值是最快执行的,执行的顺序是按照在类中写的顺序来执行的,然后就是 User 的成员变量的赋值、User 的非静态代码块、最后再是构造方法,普通代码块实际上跟普通的代码差不多了哈哈。

好了,今天就到这里了,下课。

复杂一丢丢的题

啊喂,就这么简单?我都不好意思发出来了,上面的题目几乎学过 Java 的都能做出来,来来来,加点难度,不是还有一个继承嘛,我给你们加上。

public class SonUser extends User {private static final Address address1=new Address("beijing");private  Address address2=new Address("nanjing");static {System.out.println("-- SonUser 的静态代码块1--");}{System.out.println("-- SonUser 的非静态代码块1--");}public SonUser(){{System.out.println("-- SonUser 的普通代码块1--");}System.out.println("-- SonUser 的构造方法--");{System.out.println("-- SonUser 的普通代码块2--");}}{System.out.println("-- SonUser 的非静态代码块2--");}static {System.out.println("-- SonUser 的静态代码块2--");}
}
public class StaticCodeTest {public static void main(String[] args) {new SonUser();}
}

这个答案是有点长了。不先写好答案也行吧,来一起看看。

-- Address 的构造方法:province=guangdong
-- User 的静态代码块1--
-- Address 的构造方法:province=hubei
-- User 的静态代码块2--
-- Address 的构造方法:province=beijing
-- SonUser 的静态代码块1--
-- SonUser 的静态代码块2--
-- Address 的构造方法:province=guangxi
-- User 的非静态代码块1--
-- Address 的构造方法:province=hunan
-- User 的非静态代码块2--
-- User 的普通代码块1--
-- User 的构造方法--
-- User 的普通代码块2--
-- Address 的构造方法:province=nanjing
-- SonUser 的非静态代码块1--
-- SonUser 的非静态代码块2--
-- SonUser 的普通代码块1--
-- SonUser 的构造方法--
-- SonUser 的普通代码块2--SonUser 的普通代码块2--

可以看到有关于静态的(包含父类和子类)基本上都是在一开始就执行了,静态变量以及静态代码块,只不过父类的会比子类的更快执行,在执行完静态代码块后,就会去执行父类的变量

静态变量赋值和静态代码块,可以看作是一段静态代码的打包,按照代码顺序打包到一起,而成员变量的赋值非静态代码块也打一个包,前者我们可以称作静态代码包,后者我们可以称作非静态代码包。

在类加载时就执行静态代码包,这个只执行一次;在每次声明(new)一个对象的时候就会执行非静态代码包,然后再执行构造方法,注意这里的非静态代码包会每次都执行,构造方法就看每次调用的是那个,就执行那个构造方法。

一起来看看 Byte Code

这里的静态代码包和非静态代码包虽名称是我编的,但是确实存在这个东西。

让我们看看 User 类的字节码。

public class test/other/entity/User {// compiled from: User.java// access flags 0x1Aprivate final static Ltest/other/entity/Address; address1// access flags 0x2private Ltest/other/entity/Address; address2// access flags 0x1Aprivate final static Ltest/other/entity/Address; address3// access flags 0x2private Ltest/other/entity/Address; address4// access flags 0x1public <init>()VL0LINENUMBER 25 L0ALOAD 0INVOKESPECIAL java/lang/Object.<init> ()VL1LINENUMBER 10 L1ALOAD 0NEW test/other/entity/AddressDUPLDC "guangxi"INVOKESPECIAL test/other/entity/Address.<init> (Ljava/lang/String;)VPUTFIELD test/other/entity/User.address2 : Ltest/other/entity/Address;L2LINENUMBER 22 L2GETSTATIC java/lang/System.out : Ljava/io/PrintStream;LDC "-- User 的非静态代码块1--"INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)VL3LINENUMBER 36 L3ALOAD 0NEW test/other/entity/AddressDUPLDC "hunan"INVOKESPECIAL test/other/entity/Address.<init> (Ljava/lang/String;)VPUTFIELD test/other/entity/User.address4 : Ltest/other/entity/Address;L4LINENUMBER 39 L4GETSTATIC java/lang/System.out : Ljava/io/PrintStream;LDC "-- User 的非静态代码块2--"INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)VL5LINENUMBER 27 L5GETSTATIC java/lang/System.out : Ljava/io/PrintStream;LDC "-- User 的普通代码块1--"INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)VL6LINENUMBER 29 L6GETSTATIC java/lang/System.out : Ljava/io/PrintStream;LDC "-- User 的构造方法--"INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)VL7LINENUMBER 31 L7GETSTATIC java/lang/System.out : Ljava/io/PrintStream;LDC "-- User 的普通代码块2--"INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)VL8LINENUMBER 33 L8RETURNL9LOCALVARIABLE this Ltest/other/entity/User; L0 L9 0MAXSTACK = 4MAXLOCALS = 1// access flags 0x8static <clinit>()VL0LINENUMBER 9 L0NEW test/other/entity/AddressDUPLDC "guangdong"INVOKESPECIAL test/other/entity/Address.<init> (Ljava/lang/String;)VPUTSTATIC test/other/entity/User.address1 : Ltest/other/entity/Address;L1LINENUMBER 18 L1GETSTATIC java/lang/System.out : Ljava/io/PrintStream;LDC "-- User 的静态代码块1--"INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)VL2LINENUMBER 35 L2NEW test/other/entity/AddressDUPLDC "hubei"INVOKESPECIAL test/other/entity/Address.<init> (Ljava/lang/String;)VPUTSTATIC test/other/entity/User.address3 : Ltest/other/entity/Address;L3LINENUMBER 43 L3GETSTATIC java/lang/System.out : Ljava/io/PrintStream;LDC "-- User 的静态代码块2--"INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)VL4LINENUMBER 44 L4RETURNMAXSTACK = 3MAXLOCALS = 0
}

unicode被我转成了中文。

确实是吧,静态代码包就是 static ()V,而非静态代码块就是 public ()V。而 clinit 方法就是在这个类被加载进内存就会执行的方法,init 方法就是每次调用构造方法会调用的方法。

我看到了这一行代码INVOKESPECIAL java/lang/Object. ()V,嗯哼,就是这个方法会去触发父类的构造方法啊,索嘎,所以子类会调用父类的构造方法,然后就会触发父类的加载,然后就触发了父类的静态代码包,父类的构造方法又会在第一行去触发爷类的构造方法,然后又触发爷类的加载,然后就触发了爷类的静态代码包,然后爷类。。。。。无限循环到 Object 类,就开始执行爷类的非静态代码包、父类的非静态代码包、子类的。。。

可以和递归一起联想。

我也想把父类的构造方法放在最下面,但是却只看到了 IDEA 的无情爆红,当然也是执行不了的。如果不加 super() 那就会默认加一个空参数的父类构造方法,也就是super()。

作者:程序员徐小白链接:https://juejin.cn/post/7000735799692132389

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/1165.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息