A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 陈郊 中级黑马   /  2012-9-28 18:38  /  3098 人查看  /  11 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文

父子类静态代码块、非静态代码块、构造函数 它们的执行顺序是怎么样的?为什么?

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1

查看全部评分

11 个回复

倒序浏览
求高手指教!
急!
急!!
急!!!
谢谢
回复 使用道具 举报

回帖奖励 +2

静态代码块
随着类的加载而执行,只执行一次。用于给类进行初始化

构造代码块
对象一建立就运行,给对象进行初始化,优先于构造函数执行

构造函数
对象一建立就会调用与之对应的构造函数,可以用于给对象进行初始化



没有原因

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1

查看全部评分

回复 使用道具 举报
本帖最后由 李建强 于 2012-9-28 18:55 编辑

下面的例子说明了加载规则,代码如下:
  1. public class MyClassFather {
  2. public MyClassFather() {
  3. System.out.println("执行要创建对象类父类的构造器");
  4. }
  5. }
  6. public class MyClass extends MyClassFather {
  7. public MyClass() {
  8. System.out.println("执行要创建对象类的构造器");
  9. }
  10. Unstatic u = new Unstatic();
  11. final UnstaticFinal uf = new UnstaticFinal();
  12. {
  13. System.out.println("执行非静态语句块");
  14. }
  15. static {
  16. System.out.println("执行静态语句块");
  17. }
  18. static final StaticFinal sf = new StaticFinal();
  19. static Static s = new Static();
  20. }
  21. public class StaticFinal {
  22. public StaticFinal() {
  23. System.out.println("静态final的成员变量初始化");
  24. }
  25. }
  26. public class Static {
  27. public Static() {
  28. System.out.println("静态非final的成员变量初始化");
  29. }
  30. }
  31. public class UnstaticFinal {
  32. public UnstaticFinal() {
  33. System.out.println("非静态final的成员变量初始化");
  34. }
  35. }
  36. public class Unstatic {
  37. public Unstatic() {
  38. System.out.println("非静态非final的成员变量初始化");
  39. }
  40. }
  41. public class Sample10_16 {
  42. public static void main(String[] args) {
  43. new MyClass();
  44. }
  45. }
复制代码
运行结果如下:
执行静态语句块!!!
静态final的成员变量初始化!!!
静态非final的成员变量初始化!!!
执行要创建对象类父类的构造器!!!
非静态非final的成员变量初始化!!!
非静态final的成员变量初始化!!!
执行非静态语句块!!!
执行要创建对象类的构造器!!!

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1 恭喜25分

查看全部评分

回复 使用道具 举报
运行顺序首先是静态代码块,静态随着类的加载而加载,随着类的结束而结束
其次运行构造函数,
最后是非静态代码块.

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1

查看全部评分

回复 使用道具 举报
class A
{
        private int age;
        private String name;
        {
                System.out.println("china !");//这就是构造代码块,只有{};
        }
        A()
        {
                System.out.println("china VS Japanese");
        }
        void show()
        {
                System.out.println("china VS America!");
        }
}
举个简单的例子:
他们的执行顺序是:1.构造代码块 2.构造函数 3.实例方法。
原因为:构造代码块和构造函数都是给对象初始化,但是构造代码块优先于构造函数执行,对象一建立就运行。构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象进行初始化。

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1

查看全部评分

回复 使用道具 举报
类加载时静态代码块就运行了,但是只运行一次,而后非静态代码块运行,没new个对象出来就运行一次先于构造函数,最后是构造函数

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1

查看全部评分

回复 使用道具 举报
柳彬 中级黑马 2012-9-28 20:58:31
8#
构造函数是初始化就调用,关于父子类静态代码块、非静态代码块,静态码用类引用,非静态用对象应用,现在知道了吧?你用这个原理自己推理。要学会逻辑分析

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1

查看全部评分

回复 使用道具 举报
这个问题比较难以理解。。 很多人都搞不清楚。。


看下面的代码:

class B
{
static int a = 0;
{
System.out.println("B.scope is running");
a = 10 ;
}
static
{
System.out.println("B.static scope is running");
a = 20;
}
public B()
{
System.out.println("B.Constructor is running");
}

public static void main(String arg[])
{
System.out.println(B.a);
System.out.println(B.a);
B b1 = new B();
B b2 = new B();
System.out.println(b1.a);
System.out.println(b2.a);

System.out.println(B.a);


}

}

我们在类B中定义了一个静态变量a,并分别用静态初始化和f非静态初始化的方法将其初始化为10 和 20

在main方法中我们先没有将实例化,两次打印出类成员a来,然后将a实例化两次,再两次打印出对象成员a来,最后再打印出一次类成员a,想想结果会是什么了?

B.static scope is running
20
20
B.scope is running
B.Constructor is running
B.scope is running
B.Constructor is running
10
10
10

这是我所得到的实验结果。

可以看到:当我们在使用B类时,JVM将B的定义装载,这是它调用了B的静态定义初始化 和 静态代码块 由于是静态的,尽管我们打印了两次,静态定义初始化 和 静态代码块都只执行了一次。然后打印出两次20,接着非静态代码块被执行了,而且是两次,这是在我们实例化的时候被执行的,接着是构造函数由于我们在非静态代码块中改变了a的值所以打出来的是10,最后一个打印也很有意思,我们看到同样的一句代码System.out.println(B.a);在前面打印的结果是20,而在后面却是10,怎么回事?这是由于a是static类型的,所类有实例以及类成员本身都只是共享一份a,不管在哪里被改变,其他地方的值都睡随之改变,这也是为什么我们要在不希望值被改变静态成员前加上final的原因,当然如果我们允许程序该变某个变量,我们在变量前加上static 的直接好处是可以让我们不管在程序的任何地方都可以得到与其他地方一致的值,而且是最新的。

我们也可以将这个类稍微修改一下看看

class B
{
static int a = 0;
{
System.out.println("B.scope is running");
a = 10 ;
}
static
{
System.out.println("B.static scope is running");
a = 20;
}
public B()
{
a = 30;
System.out.println("B.Constructor is running");
}

public static void main(String arg[])
{
System.out.println(B.a);
System.out.println(B.a);
B b1 = new B();
B b2 = new B();
System.out.println(b1.a);
System.out.println(b2.a);
System.out.println(B.a);

}

}

运行结果为:

B.static scope is running
20
20
B.scope is running
B.Constructor is running
B.scope is running
B.Constructor is running
30
30
30

可以看到由于构造函数是后于非静态代码块执行的,所以a的值被修改成30,同时打印类成员是也可得到30.

再来看看继承的情况:

class B
{
static int a = 0;
{
System.out.println("B.scope is running");
a = 10 ;
}
static
{
System.out.println("B.static scope is running");
a = 20;
}
public B()
{
a = 30;
System.out.println("B.Constructor is running");
}
/*
public static void main(String arg[])
{
System.out.println(B.a);
System.out.println(B.a);
B b1 = new B();
B b2 = new B();
System.out.println(b1.a);
System.out.println(b2.a);
System.out.println(B.a);

}
*/
}

class C extends B
{
static int c = 0;
static
{
c = 10;
System.out.println("C.static code is runnning and now c is " + c);

}
{
c = 20;
System.out.println("C.non-static code is runnning and now c is " + c);

}

public C()
{
c = 30;
System.out.println("C.constructor is running and now c is " + c);
}
public static void main(String args[])
{
System.out.println(C.c);
System.out.println(C.c);
C c1 = new C();
C c2 = new C();
System.out.println(c1.c);
System.out.println(c2.c);
System.out.println(C.c);

}
}

我们将B中的启动函数main注释掉了,然后让C来继承B,C和B有着类似的结构,在次不多费口舌,看看打印结果:

B.static scope is running
C.static code is runnning and now c is 10
10
10
B.scope is running
B.Constructor is running
C.non-static code is runnning and now c is 20
C.constructor is running and now c is 30
B.scope is running
B.Constructor is running
C.non-static code is runnning and now c is 20
C.constructor is running and now c is 30
30
30
30

在此描述一下c变化过程,虽然定义了两个对象c1,c2但其实只有一份共有的c,

c被装载时先被初始化为0,后来执行静态代码块时被初始化为10,接着执行c1的非静态代码快时被初始化为20,然后是c1的构造函数被出化为30,然后轮到c2的非静态代码块,这是又重新被初始化回20,然后c2的构造函数,变为30.

还可以看到载装载类时先要装载该类的基类,一直往前指导Object类。在构造函数书中也是要调用积累的构造函数的。

总结:深入理解一个类从被JVM装载开始,各种代码的执行顺序。

被JVM装载->先以后面描述的方式之后执行父类的相关代码->如果有静态初始化,先执行静态初始化,且只执行一次,以后即使有该类实例化,也不会再执行->如果有静态代码块(以与静态初始化一样的方式执行)->如果有new语句带来的实例化,先为成员变量分配空间,并绑定参数列表,隐式或显式执行super(),即父类的构造方法,->执行非静态代码块-〉执行本类的构造函数-〉其他代码

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1

查看全部评分

回复 使用道具 举报
本帖最后由 宫明星 于 2012-9-28 22:41 编辑

先是静态代码块
因为静态代码块是随着类的加载而执行的,而且只执行一次,所以是类存在的时候就有了,一般是用来给类进行初始化。
然后是构造代码块
构造代码块是对象一建立就运行,给对象进行初始化用的,它在执行时优先于构造函数。
最后才是构造函数
这个是对象建立才会调用与之对应的构造函数,一般用于给对象进行初始化的。

所以顺序是 静态代码块 —— 构造代码块 —— 构造函数

上个例子给你看吧:

  1.         StaticCode()        //构造函数
  2.         {
  3.                 System.out.println("b");
  4.         }

  5.         static        //静态代码块,给类进行初始化的。特点,只执行一次
  6.         {
  7.                 System.out.println("a");
  8.         }

  9.         {                //构造代码块,给对象进行初始化的
  10.                 System.out.println("c"+this.num);
  11.         }

  12.         StaticCode(int x)        //构造函数,给对应对象进行初始化的
  13.         {
  14.                 System.out.println("d");
  15.         }


  16.         /*如果执行
  17.         打印的结果应该是a,c,d       
  18.         b没有执行到。*/
复制代码

评分

参与人数 1技术分 +1 收起 理由
滔哥 + 1

查看全部评分

回复 使用道具 举报
执行顺序:静态代码块--构造代码块--构造方法
静态代码块:它只执行一次,它比main还先执行。
构造代码块:在类的成员位置上。每个构造方法都会去默认调用构造代码块。优先于构造方法
构造方法:建立对象时调用,用于对象初始化。

评分

参与人数 1技术分 +1 收起 理由
王海宇 + 1

查看全部评分

回复 使用道具 举报
执行顺序是父类静态块,子类静态块,父类非静态块,父类构造函数,,子类非静态块,子类构造函数。父类先于子类存在,子类继承父类属性,所以得先构造父类对象,在构造子类对象。但静态块属于类所用,在类加载就被创建。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马