7. 接口初始化规则与类加载器准备阶段和初始化阶段的重要意义分析

接口初始化规则

 例1

package com.study.classloader;

public class MyTest5  {

    public static void main(String[] args) {
        System.out.println(MyChild5.b);
    }

}

interface MyParent5{
    public static int a = 5;
}

interface MyChild5 extends MyParent5 {
    public static int b = 6;
}

结果

6

由于在接口中无法像类一样添加静态代码块,并根据其是否被执行知道该类有没有类初始化,所以这里采取将MyParent5.class文件删除再执行的方式,看能否正常执行MyTest5的main方法

删除后再执行,执行结果

6

结论:

当一个接口在初始化时并不要求其父接口都完成了初始化

进一步,把MyChild5.class也同时删除,再执行,执行结果:

6

查看反编译结果

➜  jvm javap -c target.classes.com.study.classloader.MyTest5 
警告: 文件 ./target/classes/com/study/classloader/MyTest5.class 不包含类 target.classes.com.study.classloader.MyTest5
Compiled from "MyTest5.java"
public class com.study.classloader.MyTest5 {
  public com.study.classloader.MyTest5();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: bipush        6
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       8: return
}

结论:

接口本身定义的变量默认就是public static final的常量!!!所以,已经被保存于调用方法所在类MyTest5的常量池中

运行期常量

例2

package com.study.classloader;

import java.util.Random;

public class MyTest5  {

    public static void main(String[] args) {
        System.out.println(MyChild5.b);
    }

}

interface MyParent5{
    public static int a = 5;
}

interface MyChild5 extends MyParent5 {
    public static int b = new Random().nextInt(2);
}

执行结果:

1

删除MyParent5.class与MyChild5.class,再次执行

例3

package com.study.classloader;

import java.util.Random;

public class MyTest5  {

    public static void main(String[] args) {
        System.out.println(MyChild5.b);
    }

}

interface MyParent5{
    public static int a = new Random().nextInt(10);
}

interface MyChild5 extends MyParent5 {
    public static int b = new Random().nextInt(2);
}

执行结果:

0

删除MyParent5.class

再次执行:

结果:

只有在真正使用到父接口的时候(如引用接口中所定义的常量时),才会初始化

类与接口在初始化化时候的区别

例4

package com.study.classloader;

import java.util.Random;

public class MyTest5  {

    public static void main(String[] args) {
        System.out.println(MyChild5.b);
    }

}

interface MyParent5{
    public static int a = new Random().nextInt(10);
}

interface MyChild5 extends MyParent5 {
    public static int b = 5;
}

删除MyParent5.class并执行

结果:

5

例5

package com.study.classloader;

import java.util.Random;

public class MyTest5  {

    public static void main(String[] args) {
        System.out.println(MyChild5.b);
    }

}

class MyParent5{
    public static int a = new Random().nextInt(10);
}

class MyChild5 extends MyParent5 {
    public static int b = 5;
}

删除MyParent5.class并执行

结果:

结论:

接口默认所有变量都为final的常量

类在初始化的时候要求它的父类一定被初始化,但是接口不要求,而是真正使用父接口的时候才初始化父接口(如引用接口中的常量)

类加载器准备阶段和初始化阶段的重要意义分析

例6

package com.study.classloader;

public class MyTest6 {

    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        System.out.println("counter1 : " + Singleton.counter1);
        System.out.println("counter2 : " + Singleton.counter2 );
    }
}

class Singleton{
    public static int counter1;
    public static int counter2 = 0;

    private static Singleton singleton = new Singleton();

    private Singleton(){
        counter1++;
        counter2++;
    }


    public static Singleton getInstance(){
        return singleton;
    }
}

结果:

1

1

例7

package com.study.classloader;

public class MyTest6 {

    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        System.out.println("counter1 : " + Singleton.counter1);
        System.out.println("counter2 : " + Singleton.counter2 );
    }


}

class Singleton{
    public static int counter1;

    private static Singleton singleton = new Singleton();
    private Singleton(){
        counter1++;
        counter2++;
    }

    public static int counter2 = 0;


    public static Singleton getInstance(){
        return singleton;
    }
}

结果:

1

1

counter1 : 1

counter2 : 0

分析:

在调用getInstance()类的静态方法后,类因为首次主动使用进行初始化。

连接阶段的准备阶段,为类中的静态变量赋了初值,其中singleton赋值为null。这时候counter1和counter2都赋值为0。

接下来进入初始化阶段,初始化是按照声明变量从上至下的顺序执行的。

1)给singleton赋值,new对象,这个过程调用了构造方法

2)执行构造方法,给counter1自增1,为1。给counter2自增1,为1

3)给counter2赋值0

猜你喜欢

转载自blog.csdn.net/Cheng_Kohui/article/details/93670272