记一个有趣的代码片段

昨天看到一个十分有意思的问题,当时看到问题的我也是瞬间懵了。其实就是自己掌握的知识不扎实。现在我把它分开经行解析下,如有不对,请指正!主要还是自己对基础知识理解的不扎实,以后还会遇到许许多多的问题,就干脆起了个java学习有趣代码片段(一)。

第一个问题

代码如下

package com.zhb;

public class Test {
    static Test test = new Test("3");

    static{
        System.out.println("1");
    }
    {
        System.out.println("2");
    }
    Test(String s){
        System.out.println(s);
    }

    public static void staticFunction(){
        System.out.println("4");
    }

    public static void main(String[] args) {
        staticFunction();
    }

}

看到这段代码,我心想我刚刚总结了关于static代码块的总结,这个还不在话下!结果应该是 1 2 3 4 。
运行后,我发现,什么鬼!
* 正确的运行结果:2 3 1 4 *

拆解分析

1.首先我们先再复习下关于代码块的知识
package com.zhb;

public class Demo1 {
    /**
     * 静态代码块
     */
    static{
        System.out.println("静态代码块");

    }

    /**
     * 构造代码
     */
    {
        System.out.println("构造代码块");
    }
    /**
     * 无参构造函数
     */
    public Demo1(){
        System.out.println("无参构造函数");
    }
    /**
     * 有参数的构造函数
     */
    public Demo1(int num){
        System.out.println("有参数的构造函数");
    }


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

}

执行结果
静态代码块

==================
构造代码块
无参构造函数

==================
构造代码块
有参数的构造函数

从中我们可以看出

  • 静态代码块它是随着类的加载而被执行,只要类加载了就会执行,而且只加载一次,主要用于给类进行初始化;
  • new一个对象的时候总是先执行构造代码,在执行构造函数,但是有一点需要注意构造代码不是在构造函数之前运行,它是依托构造函数执行。
  • 构造代码块每创建一个对象时,就会执行一次。同时构造函数是给特定的对象进行初始化,而构造代码是给所有的对象进行初始化,作用域不同。
  • 执行顺序:静态代码块> 构造代码块 > 构造函数
2,重现原题中的结构代码
package com.zhb;

public class Demo2 {
    static Demo1 demo1 =new Demo1() ;
    /**
     * 静态构造代码
     */
    static{
        System.out.println("Demo2静态构造代码块");
    }
    /**
     * 构造代码
     */
    {
        System.out.println("Demo2构造代码块");
    }
    /**
     * 无参构造函数
     */
    public Demo2(){
        System.out.println("Demo2无参构造函数");
    }
    /**
     * 有参数的构造函数
     */
    public Demo2(int num){
        System.out.println("Demo2有参数的构造函数");
    }


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


}

执行结果:
静态代码块
构造代码块
无参构造函数
Demo2静态构造代码块

==================
Demo2构造代码块
Demo2无参构造函数

==================
Demo2构造代码块
Demo2有参数的构造函数

从中我们知道

  • 静态变量和静态构造代码块是同级别的;
  • static按照顺序执行
进一步演练
package com.zhb;

public class Demo2 {
    //static Demo1 demo1 =new Demo1() ;
    static Demo2 demo2 =new Demo2() ;
    /**
     * 静态构造代码
     */
    static{
        System.out.println("Demo2静态构造代码块");
    }
    /**
     * 构造代码
     */
    {
        System.out.println("Demo2构造代码块");
    }
    /**
     * 无参构造函数
     */
    public Demo2(){
        System.out.println("Demo2无参构造函数");
    }
    /**
     * 有参数的构造函数
     */
    public Demo2(int num){
        System.out.println("Demo2有参数的构造函数");
    }


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


}

此时的代码结构和原题是一样的。

  • 顺序执行静态部分,首先执行 static Demo2 demo2 =new Demo2() ;
  • 此时,new一个对象的时候总是先执行构造代码。
    执行System.out.println(“Demo2构造代码块”);
  • 执行构造函数 System.out.println(“Demo2无参构造函数”);此时demo2 结束
  • 顺序执行静态构造代码
  • 后面的正常执行(前面已解释)

执行结果:
Demo2构造代码块
Demo2无参构造函数
Demo2静态构造代码块

==================
Demo2构造代码块
Demo2无参构造函数

==================
Demo2构造代码块
Demo2有参数的构造函数

此时明白了原程序为什么是那样的结果了

第二个问题

我们把原题的程序修改成如下

package com.zhb;

public class Test {
    static Test test = new Test();
    Test test2 = new Test();

    public static void main(String[] args) {

    }

}

执行后报错

  • Exception in thread “main” java.lang.StackOverflowError *

这个问题是为什么呢?

静静的分析下

1.我们知道首先运行static.
2.static Test test = new Test(); 初始化Test类,会把类的成员变量实例化
3.实例化test2,但是Test test2 = new Test();此时又会实例化test2,一直进行循环
4.导致栈溢出

这里又引出一个问题:类的实例变量初始化的过程

class B {
  private int b = 10;
  public B(){
    b = 100;
  }
}

编译成class文件后,使用命令 javap -c B.class 反编译

趣味Java代码 java一些有趣的代码_class

// 第一部分:父类的<init>()方法
0: aload_0
1: invokespecial #1                  // Method java/lang/Object."<init>":()V
// 第二部分:实例变量初始化,也就是定义变量时的赋值
4: aload_0
5: bipush        10
7: putfield      #2                  // Field b:I
// 第三部分:构造函数方法体
10: aload_0
11: bipush        100
13: putfield      #2                  // Field b:I
16: return

学习到这里总结一下前面学习的内容就是:
* Java实例变量在初始化时的顺序是父类的初始化代码(xxx—>xxx—>xxx)—>定义变量时直接赋值—>构造函数代码块。*

由于我也是菜鸟一枚,很多也是一知半解,如有不对,请指正。