关于jvm初始化阶段的接口初始化问题

深入理解jvm第2版中写明“一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化”,但是前文又有说,final修饰的常量在编译阶段会存入调用类的常量池中,实际上并没有直接引用定义常量的类,因此不会出发定义常量的类的初始化,而接口中都是由static final修饰的常量,引用接口中定义的常量会初始化不就和这条矛盾了吗?有什么可以说明“一个接口初始化,其父接口没有初始化”的例子吗?noob求各位jvm大佬指教!😥😥😥

#Java#
全部评论
被static final修饰的不一定是常量
点赞 回复 分享
发布于 2019-07-17 16:48
static final修饰的常量分为两种 一种是在编译器能够确定的比如public static final String iField2 = "heihie";这种常量在编译期会放进常量池中,当使用到该常量时不会触发类的初始化 第二种是只有在运行期能够确定的比如public static final String uuid = UUID.randomUUID().toString();这种常量不运行是肯定不知道他的值的,所以在编译期也不可能放进常量池中(值都不知道还怎么放进去),当使用到该常量时会发出类的初始化。 然后你提问的第一句话一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化这里的常量指上面的第二种。第二句话final修饰的常量在编译阶段会存入调用类的常量池中,实际上并没有直接引用定义常量的类,因此不会出发定义常量的类的初始化这里的常量指的是上面的第一种。
点赞 回复 分享
发布于 2019-07-17 17:03
public class MyTest6 { public static String printWhenInit(String s){ System.out.println(s); return s.substring(s.indexOf(" ")); } public static void main(String[] args){ System.out.println(SubI.iField); //静态的运行时才能确定的常量,这时存有该常量的接口被初始化,其父类不会被初始化 // System.out.println(SubI.iField2); //静态的编译器可以确定的常量,这时三个接口都不会被初始化,原因和类一样 } } interface SuperI { public static final String superField = MyTest6.printWhenInit(" initializing SuperI.superField "); } interface I extends SuperI{ public static final String iField = MyTest6.printWhenInit("initializing I.iField "); public static final String iField2 = "heihie"; } interface SubI extends I { public static final String subField = MyTest6.printWhenInit(" initializing SubI.subField "); } 执行main就能验证子接口I初始化了而父接口SuperI没有初始化(因为如果SuperI初始化了的话会打印initializing SuperI.superField)
点赞 回复 分享
发布于 2019-07-17 18:07
SuperI.class Compiled from "SuperI.java" public interface SuperI {   public static final java.lang.String superField;   static {};     Code:        0: ldc           #1                  // String  initializing SuperI.superField        2: invokestatic  #2                  // Method Main.printWhenInit:(Ljava/lang/String;)Ljava/lang/String;        5: putstatic     #3                  // Field superField:Ljava/lang/String;        8: return } I.class Compiled from "I.java" public interface I extends SuperI {   public static final java.lang.String iField;   public static final java.lang.String iField2;   static {};     Code:        0: ldc           #1                  // String initializing I.iField        2: invokestatic  #2                  // Method Main.printWhenInit:(Ljava/lang/String;)Ljava/lang/String;        5: putstatic     #3                  // Field iField:Ljava/lang/String;        8: return } SubI.class Compiled from "SubI.java" public interface SubI extends I {   public static final java.lang.String subField;   static {};     Code:        0: ldc           #1                  // String  initializing SubI.subField        2: invokestatic  #2                  // Method Main.printWhenInit:(Ljava/lang/String;)Ljava/lang/String;        5: putstatic     #3                  // Field subField:Ljava/lang/String;        8: return } static {}就是<clinit>方法,环境是JDK 1.8,可见3个接口都初始化了
点赞 回复 分享
发布于 2019-07-17 18:48
public class MyTest6 { public static String printWhenInit(String s){ System.out.println(s); return s.substring(s.indexOf(" ")); } public static void main(String[] args){ System.out.println(SubI.iField3); //静态的运行时才能确定的常量,这时存有该常量的接口被初始化,其父类不会被初始化 // System.out.println(SubI.iField2); //静态的编译器可以确定的常量,这时三个接口都不会被初始化,原因和类一样 } } interface SuperI { public static final String superField = MyTest6.printWhenInit(" initializing SuperI.superField "); } interface I extends SuperI{ public static final String iField = MyTest6.printWhenInit("initializing I.iField "); public static final String iField2 = "heihie"; public static final String iField3 = UUID.randomUUID().toString(); } interface SubI extends I { public static final String subField = MyTest6.printWhenInit(" initializing SubI.subField "); } 你运行下这个就看到了,打印了initializing I.iField说明I初始化了,但是没打印initializing SuperI.superField说明父接口没有初始化
点赞 回复 分享
发布于 2019-07-17 19:05

相关推荐

01-21 12:26
暨南大学 golang
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务