java静态代码块 构造代码块 构造方法的执行顺序及注意问题

今天我总结了一下java中静态代码块 构造代码块 构造方法的执行顺序及其注意问题

首先要知道静态代码块是随着类的加载而加载,而构造代码块和构造方法都是随着对象的创建而加载

当时做了这么一个小案例(想必这个大多数学java的都做过,不知道是否都理解了)

复制代码
class Fu{
    static {
        System.out.println("Fu static code");
    }
    {
        System.out.println("Fu code");
    }
    public Fu(){
        System.out.println("Fu GouZao");
    }
}

class Zi extends Fu{
    static {
        System.out.println("Zi static code");
    }
    {
        System.out.println("Zi code");
    }
    public Zi(){
        System.out.println("Zi GouZao");
    }
}

public class Text{
    public static void main(String[] args) {
        Zi zi = new Zi();
    }
}
复制代码

1,在编译Text.java时,先加载了Fu类,因此Fu类的静态代码块首先执行,而后加载Zi类,Zi类的静态代码块执行,这没什么好说的

2,然后创建Zi的对象,大家都知道构造代码块优先于构造方法执行,这时候问题来了,这时应该先看Zi类的构造方法,Zi类里的构造方法里有一句隐式的super()首先被执行,所以找到Fu类的构造方法,而Fu类的构造方法中也有一句隐式的super()执行(调用Object类的构造方法),并没有什么返回结果,接下来才是在执行Fu类构造方法的方法体前先执行了Fu类的构造代码块(Fu code),再执行Fu类构造方法的方法体(也就是Fu GouZao),最后又回到Zi类的构造方法中,这时Zi类的super()已经执行完了,在执行Zi类构造方法的方法体前先执行Zi类的构造代码块(Zi code),再执行Zi类构造方法的方法体(Zi GouZao)

最后的结果是:

Fu static code
Zi static code
Fu code
Fu GouZao
Zi code
Zi GouZao

不知道大家绕晕了没有,我在这再总结一下:静态的是与类有关,肯定先加载,而构造代码块执行前要先看构造方法中是否有this()或super(),有的话在其之后执行,最后执行构造方法的方法体



题目如下,请写出程序运行结果

[html]  view plain  copy
  1. class Base{  
  2. {  
  3. System.out.println("base block");  
  4. }  
  5. static {  
  6. System.out.println("base static block");  
  7. }  
  8. Base(){  
  9. System.out.println("base constructor");  
  10. }  
  11. }  
  12. class Parent extends Base{  
  13. int i = 0;  
  14. int j3;  
  15. {  
  16. System.out.println("parent block");  
  17. }  
  18. static {  
  19. System.out.println("parent static block");  
  20. }  
  21.   
  22.   
  23. public Parent() {  
  24. System.out.println("parent constructor");  
  25. }  
  26. void out(){  
  27. System.out.println(i+"@@@@"+j);  
  28. }  
  29. }  
  30.   
  31.   
  32. class Child extends Parent {  
  33. int i = 3;  
  34. int j = 0;  
  35. {  
  36. System.out.println("child block");  
  37. }  
  38. static {  
  39. System.out.println("child static block");  
  40. }  
  41.   
  42.   
  43. public Child() {  
  44. System.out.println("child constructor");  
  45. }  
  46. void out(){  
  47. System.out.println(i+"####"+j);  
  48. }  
  49. }  
  50.   
  51.   
  52. public class Test {  
  53.   
  54.   
  55. public static void main(String[] args) {  
  56. Parent p = new Child();  
  57. p.out();  
  58. System.out.println(p.i+"
    $"+p.j);  
  59.   
  60. }  
  61. }  

程序的运行结果这里不说了,这道题考察两个知识点:代码块(静态、非静态)、继承。

首先讲一下代码块:

代码块定义很简单,就是用{}包裹的一块代码,如果是在大括号外加static 则表示的是静态代码块。这里要提一下类的装载步骤。

在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:

装载:查找和导入类或接口的二进制数据;
链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
校验:检查导入类或接口的二进制数据的正确性;
准备:给类的静态变量分配并初始化存储空间;
解析:将符号引用转成直接引用;
初始化:激活类的静态变量的初始化Java代码和静态Java代码块。
初始化类中属性是静态代码块的常用用途,但只能使用一次。

所以静态代码块的优先级要高于非静态代码块、构造方法(在一个类中非静态代码块优先级高于构造方法)。


其次讲一下继承:

继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

在上面的程序中,子类(Child)继承了父类(Parent)并且对父类的out()方法进行了重写。

所以当对象p在调用out()的时候,用的是子类(Child)的out()方法。

如果子类(Child)没有对父类的out()方法进行了重写,那么对象p用的将是父类(Parent)的out()方法。

正如上面所说,因为p调用的是子类(Child)的out()方法,所以输出结果中i,j的值是Child类赋值的值。

因为p是父类(Parent)的实例对象,所以p中i,j的值一定是Parent中的i,j的值。


综上所诉:
对象的初始化顺序:首先执行父类静态的内容,然后去执行子类的静态的内容,然后再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,执行完毕后,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。执行完毕后再去执行子类的构造方法。总之一句话,静态代码块内容最先执行,当然父类的静态代码块一定先于子类的静态代码块执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。


注意:子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的不带参数的构造方法。如果父类没有不带参数的构造方法,那么子类必须用supper关键子来调用父类带参数的构造方法,否则编译不能通过。




运行下面这段代码,观察其结果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.test;
 
public class HelloB extends HelloA {
   
   public HelloB() {
   }
 
   {
     System.out.println( "I'm B class" );
   }
   
   static {
     System.out.println( "static B" );
   }
 
   public static void main(String[] args) {
     new HelloB();
   }
}
 
class HelloA {
   
   public HelloA() {
   }
 
   {
     System.out.println( "I'm A class" );
   }
   
   static {
     System.out.println( "static A" );
   }
   
}

结果如下:

?
1
2
3
4
static A
static B
I'm A class
I'm B class

解析:

1.静态代码块:是在类的加载过程的第三步初始化的时候进行的,主要目的是给类变量赋予初始值。

2.构造代码块:是独立的,必须依附载体才能运行,Java会把构造代码块放到每种构造方法的前面,用于实例化一些共有的实例变量,减少代码量。

3.构造方法:用于实例化变量。


总结:

1是类级别的,2、3是实例级别的,所以1要优先2、3.

它们的执行顺序是1>2>3;




你可能感兴趣的:(java)