java学习_有趣代码片段(一)

记一个有趣的代码片段

昨天看到一个十分有意思的问题,当时看到问题的我也是瞬间懵了。其实就是自己掌握的知识不扎实。现在我把它分开经行解析下,如有不对,请指正!主要还是自己对基础知识理解的不扎实,以后还会遇到许许多多的问题,就干脆起了个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 反编译

// 第一部分:父类的<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)—>定义变量时直接赋值—>构造函数代码块。*

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

参考:

1.http://blog.csdn.net/chenssy/article/details/14486833
2.http://blog.csdn.net/cauchyweierstrass/article/details/48943077

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值