zoukankan      html  css  js  c++  java
  • 一道有趣的类加载面试题

    题目

    运行如下代码的Test1与Test2分别输出什么结果

    
    public class Parent {
        static {
            System.out.println("Parent static invoke");
        }
        public static final String FINAL_STR="FINAL_STR";
        public static final Test FINAL_OBJECT=new Test();
        public Parent(){
            System.out.println("Parent init");
        }
    }
    public class Child extends Parent {
        static {
            System.out.println("Child static invoke");
        }
        public Child(){
            System.out.println("child init");
        }
    }
    public class Test {
        public static void main(String[] args) {
            System.out.println(Child.FINAL_STR);
        }
    }
    public class Test2 {
        public static void main(String[] args) {
            System.out.println(Child.FINAL_OBJECT);
        }
    }
    
    

    结果:

    运行Test1结果

    FINAL_STR
    

    运行Test2结果

    Parent static invoke
    cn.lonecloud.Test@5e2de80c
    

    解析:

    Test1结果解析:

    1. 由于在mian方法中打印语句调用的是Child.FINAL_STR变量。
    2. 从Child的类中可以得知,FINAL_STR为final并且为static变量,其在调用static final变量的时候不会触发类的初始化操作。所以结果如上

    Test2结果解析:

    1. 由于Test2中引用的对象为父类Parent的静态变量,由于并不是常量池中的对象,所以,会触发Parent的初始化操作。

    深究:

    Test1字节码:

    Compiled from "Test.java"
    public class cn.lonecloud.Test {
      public cn.lonecloud.Test();
        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: ldc           #4                  // String FINAL_STR
           5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
           8: return
    }
    
    
    1. ldc #4 // String FINAL_STR其为获取静态化变量方法,其为将常量压入栈中,由于静态变量在JVM中存在常量池的概念,会将字符串进行优化,所以并不会触发类初始化

    Test2字节码:

    Compiled from "Test.java"
    public class cn.lonecloud.Test {
      public cn.lonecloud.Test();
        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: getstatic     #3                  // Field cn/lonecloud/Child.FINAL_OBJECT:Lcn/lonecloud/Test;
           6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
           9: return
    }
    

    由于需要使用到Child类的父类中的FINAL_OBJECT变量,未使用到Child类中的变量,所以不会对Child类进行初始化,初始化的为其父类。

    查看JVM加载情况

    通过添加JVM参数-XX:+TraceClassLoading可以查看类加载情况

    可见,会对Child,Parent类进行类加载操作,但是调用static方法,只有Parent类会调用static进行初始化操作。

    总结

    1. 如果引用了常量池变量(String,以及基本类型相关变量),如果该变量为final static进行修饰的时候,则不会对类进行初始化操作
    2. 如果为非常量池变量,如果调用方存在父子类关系,则实际JVM会加载子类与父类,但是如果使用的为父类的final变量,并不会触发类的初始化操作。
  • 相关阅读:
    二分搜索树的深度优先遍历和广度优先遍历
    数据结构与算法之非比较排序【Java】
    数据结构与算法之比较排序【Java】
    像素 转换 px dp
    Toast
    MySQL丶auto_increment
    MariaDB · 性能优化 · Extended Keys
    加唯一索引怎么会导致丢数据
    语句执行错误一· Count(Distinct) ERROR
    innodb参数 &#183; innodb_flush_log_at_trx_commit
  • 原文地址:https://www.cnblogs.com/lonecloud/p/11757316.html
Copyright © 2011-2022 走看看