Java接口的初始化

背景

接口与类真正有所区别的是前面讲述的四种“有且仅有”需要开始初始化场景中的第三种:当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。 ——《深入理解Java虚拟机:JVM高级特性与最佳实践》

这里讲到引用接口中定义的常量会初始化接口,但是书中也写到引用类中的常量不会导致类被初始化,因为编译阶段已经将常量移动到常量池中了,两者的说法有一些矛盾让我很困惑。

public class InterfaceInitTest {

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

}

interface Interface {

    int CONSTANT_INT = 1;

    Object CONSTANT_OBJECT = new Object();

    Object CONSTANT_OBJECT2 = new Object() {
        {
            System.out.println("interface init");
        }
    };

}

1

Process finished with exit code 0

可以看到没有接口并没有被初始化,这和我理解的是一样的,引用类中的常量不会导致类被初始化,引用接口中的常量也不会被初始化。

但是稍微修改一下main函数的代码

public class InterfaceInitTest {

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

}

interface init
java.lang.Object@14ae5a5

Process finished with exit code 0

可以看到同样是引用接口中的常量,有时候接口又会被初始化。

通过在网上找到的一篇文章ConstantValue属性,里面提到常量池中只能引用到基本类型和String类型的字面量。这也就解答了我的困惑:类和接口在被引用常量的时候是否被初始化,取决于这个常量能够在编译时被放进常量池中(排除不支持的类型和运行时常量)。

接口初始化的规则

通过测试发现,以下几种情况接口会被初始化:

  1. 调用接口中不在常量池中的常量(对static字段的引用引发的初始化只会初始化实际定义的接口(尽管可以通过实现类,子接口的名称进行引用(而接口中的static方法不能被继承)))
  2. 调用接口中的静态方法
  3. 当初始化一个类时,将初始化这个类实现的所有的包含default方法的接口和超接口
  4. java.lang.reflect可能会导致接口初始化

初始化接口本身不会导致任何超接口的初始化(注意和第3条的区别)

对于这个问题我查阅了很多书籍和文章,都有讲得不太清楚的地方,所以不太确定结论是否正确,文章存在的疏漏读者也可以评论指正。

posted @ 2020-10-22 18:13  卷卷子  阅读(2213)  评论(1编辑  收藏  举报