Java类中各部分的执行顺序

直接看代码,这个例子写的比较复杂,包含了大多数情况,所以读起来得耐心点。

public class ClassLoadTest {

    ClassLoadTest(){
        System.out.println("构造一个ClassLoaderTest");
    }

    {
        System.out.println("CLassLoader的非静态代码块");
    }

    static {
        System.out.println("CLassLoader的静态代码块");
    }

    public static void main(String[] args){

        A a = new A("a");

        System.out.println("构造一个子类对象,并赋值给子类引用");
        C c = new C();
        System.out.println("构造一个子类对象,并赋值给父类引用");
        B b = new C();
        System.out.println("构造一个父类对象,并赋值给父类引用");
        B b1 = new B();

    }
}

class A {

    A(String s){
        System.out.println("A 构造方法-----开始");
        B b1 = new B("构造方法中生成的B");
        System.out.println("A 的构造方法"+s);
        funA2();
        System.out.println("A 构造方法--------------完毕");
    }

    {
        System.out.println("A 的非静态代码块-----开始");
        funA2();
        funA1();
        System.out.println("A 的非静态代码块-------------完毕");
    }

    B b1 = new B("非静态全局变量生成的B");

    static B b2 = new B("静态全局变量生成的B");

    static {
        System.out.println("A的静态代码块-----开始");
        B b1 = new B("静态代码块");
        System.out.println("A 的静态代码块--------------完毕");
    }

    public static void funA1(){
        System.out.println("A 的静态方法");
    }

    public void funA2(){
        System.out.println("A 的成员方法");
    }
}

class B{

    String s = "b";

    static {
        System.out.println("B 的静态代码块");
    }

    {
        System.out.println("B 的非静态代码块");
    }

    B(){
        System.out.println("B 的无参构造方法");
    }

    B(String s){

        System.out.println("构造了一个 B--"+s);
    }

    public static void funA1(){

        System.out.println("B 的静态方法");
    }

    public void funA2(){
        System.out.println("B 的成员方法");
    }

    private void fun3(){
        System.out.println("B 的private成员方法");

    }
}

class C extends B{

    String s = "c";

    static {
        System.out.println("C 的静态代码块");
    }

    {
        System.out.println("C 的非静态代码块");
    }

    C(){
         System.out.println("C的无参构造方法");
    }

    private static void fun3(){
    }

    @Override
    public void funA2(){
        funA1();
        System.out.println("override");
    }
}

CLassLoader的静态代码块
B 的静态代码块
B 的非静态代码块
构造了一个 B–静态全局变量生成的B
A的静态代码块—–开始
B 的非静态代码块
构造了一个 B–静态代码块
A 的静态代码块————–完毕
A 的非静态代码块—–开始
A 的成员方法
A 的静态方法
A 的非静态代码块————-完毕
B 的非静态代码块
构造了一个 B–非静态全局变量生成的B
A 构造方法—–开始
B 的非静态代码块
构造了一个 B–构造方法中生成的B
A 的构造方法a
A 的成员方法
A 构造方法————–完毕
构造一个子类对象,并赋值给子类引用
C 的静态代码块
B 的非静态代码块
B 的无参构造方法
C 的非静态代码块
C的无参构造方法
构造一个子类对象,并赋值给父类引用
B 的非静态代码块
B 的无参构造方法
C 的非静态代码块
C的无参构造方法
构造一个父类对象,并赋值给父类引用
B 的非静态代码块
B 的无参构造方法

类的初始化原则:

  1. 静态的东西最先运行。静态全局变量和静态代码块谁先运行取决于谁写在前面。且静态的东西永远只运行一次。第二次new的时候既不会初始化static变量,也不会运行static代码块。
  2. 对于非静态的东西,先初始化变量,最后运行构造方法,与代码写的顺序无关。
  3. 构造子类时,首先会构造一个父类。等父类构造完成后才会构造子类。非静态代码块和对象绑定,所以父类的非静态代码块是在构造父类对象的时候运行的。
  4. 另外可以看到,运行Main方法并不需要生成对象,直接用类调用就行了,但是会调用static代码块。如果有static对象的话也会先生成,最后调用static的Main方法。

几个问题:

  • 构造方法中能不能调用成员函数?可以,但实际使用中要注意空指针,因为成员方法用到的变量不一定在构造对象的时候就已经初始化了。例如,在Android中就不能在Application的构造方法中调用getSharedPreferences()。
  • 非静态代码块,成员变量,构造方法的调用顺序?全局变量和非静态代码块的执行顺序看谁写在前面,构造方法总是最后调用。
  • 非静态代码块中能够调用成员函数?可以,静态函数也可以。

为什么构造方法总是最后执行的原因:

构造方法的作用是对类初始化。具体来讲是对类的变量进行初始化。那么首先得有变量才能对变量进行初始化。所以总是变量先构造出引用,最后构造函数才得到执行。

补充:

今天做美丽联合的笔试,遇到这么一个奇怪的题:

public class Meili {

    static Meili meili = new Meili();

    {
        System.out.println("非静态代码块");
    }

    static {
        System.out.println("静态代码块");
    }

    public static void main(String[] args){
        System.out.println("开始执行Main方法");
        Meili meili = new Meili();
    }
}

求输出是什么?

注意这块代码和上面的例子不同之处在于,它直接用执行类(包含Main方法的类)来生成对象。结果是:

非静态代码块
静态代码块
开始执行Main方法
非静态代码块

这段代码的执行顺序是这样的:先生成static的Meili对象,于是执行代码块,但是奇怪的是居然先执行了非静态代码块。当静态的Meili对象生成完了之后去执行Main方法。然后在Main方法中生成另一个Meili对象,此时由于静态代码块已经执行过了,所以只会执行非静态代码块,程序结束。

这段代码诡异的地方在于,每个Meili对象都持有一个Meili对象,即第三行的静态Meili引用所指的对象。如果去掉static会栈溢出,因为会无限调用构造方法。但是为什么非静态代码块会在静态代码之前调用呢?之前的测试证明了静态代码块是一定会在非静态代码块之前执行的呀?希望知道的大佬不吝赐教。

你可能感兴趣的:(java基础)