JVM(三)之final关键字与static关键字探索(一)

前言

上一节对静态属性的所属类的初始化进行了分析,本小节将对

  • fianl关键字加static一起会使用进行探索分析
  • 编译期常量和运行期常量的区别
  • 本节涉及到的助记符
  • 数组来说字节码中的秘密

一、fianl关键字加static一起会使用进行探索分析

/**
 * @author lijk
 * @date 2019/12/25 16:55
 */
public class MyTest1 {
     
    public static void main(String[] args) {
     
        System.out.println(FinalPojo.str);
    }
}

class FinalPojo{
     
    public final static String str="FinalPojo field";
    static {
     
        System.out.println("FinalPojo Initialization");
    }
}

输出结果为:

FinalPojo field

Process finished with exit code 0

首先分析,如果说不加final的情况,类会被初始化,FinalPojo Initialization会被打印出来,但是加了final之后,FinalPojo没有被初始化,这个时候就有一个问题出现了,为什么会是这样的一个结果那?
现在公布答案,这个结果和final有着极其重要的关系;

  • 分析结果就是常量在编译阶段就会被存入到调用常量的方法所在的类的常量池当中,本质上,调用类并没有直接引用到定义常量的类,因此并不会触发定义常量的类的初始化,显然静态代码块并没执行。
  • 注意:这里指的是将常量存放到MyTest1 的常量池汇总,之后MyTest1 和FinalPojo就没有关系了,甚至,我们可以将FinalPojo文件删除了

现在删除掉FinalPojo.class文件,验证下是否失败。
JVM(三)之final关键字与static关键字探索(一)_第1张图片
JVM(三)之final关键字与static关键字探索(一)_第2张图片
运行代码输出结果为:

FinalPojo field

Process finished with exit code 0

点开MyTest1.class文件,常量直接在编译出来的文件中了已经,不需要再去源文件中获取常量信息

JVM(三)之final关键字与static关键字探索(一)_第3张图片

二、编译期常量和运行期常量的区别

/**
 * @author lijk
 * @date 2019/12/25 16:55
 */
public class MyTest1 {
     
    public static void main(String[] args) {
     
        System.out.println(FinalPojo.str);
    }
}

class FinalPojo{
     
    public final static String str= UUID.randomUUID().toString();

    static {
     
        System.out.println("FinalPojo Initialization");
    }
}

如果常量编译期无法确定,那么会在运行期调用常量;
代码运行结果为:

FinalPojo Initialization
a53e3b9c-e601-4338-9ac8-7b5e604b2686

Process finished with exit code 0

每一个常量的值并不是编译期间可以确定的,那么其值不会放到被调用类的常量池中,这时在程序运行时,会导致主动使用这个常量所在的类,显然回到这个类会被初始化。

三、本节涉及到的助记符

/**
 * @author lijk
 * @date 2019/12/25 16:55
 */
public class MyTest1 {
     
    public static void main(String[] args) {
     
        System.out.println(FinalPojo.str);
    }
}

class FinalPojo{
     
//    public final static String str= UUID.randomUUID().toString();
    public final  static String str="123456";
    public static int i=1;

    public static int x=128;

    public static int y=127;

    public static short z=127;

    static {
     
        System.out.println("FinalPojo Initialization");
    }
}

代码编译完成,通过javap反编译指令查看字节码文件FinalPojo.class;

 com.mfhcd.jvm.chapter_1.webnesday.FinalPojo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  static {
     };
    Code:
       0: iconst_1
       1: putstatic     #2                  // Field i:I
       4: sipush        128
       7: putstatic     #3                  // Field x:I
      10: bipush        127
      12: putstatic     #4                  // Field y:I
      15: bipush        127
      17: putstatic     #5                  // Field z:S
      20: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      23: ldc           #7                  // String FinalPojo Initialization
      25: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      28: return
}

static {};静态代码块之前就不多说了,后面会多这部分进行补充。主要是初始化过程中都做了哪些事情?当然需要把助记符给大家介绍一下:

  • 助记符

    • ldc表示将String类型的常量值从常量池中推送至栈顶
    • bipush表示将单字节(-128-127【2的(8-1)次方】)的常量值推送到栈顶(除(0-5))
    • sipush表示将短整型常量值(-32768-32767【2的(16-1)次方】)推送到栈顶
    • iconst_(0-5之间数字) 表示将int类型1推送到栈顶(0-5之间的数字)
    • getStatic表示获取静态变量的值(上一小细节有介绍过【类的主动使用】)
    • putStatic表示设置静态变量的值(上一小细节有介绍过【类的主动使用】)
    • invokeStatic表示调用静态方法(上一小细节有介绍过【类的主动使用】)
    • anewarray:表示创建一个引用类型的数组,并将其引用值压入数组(后面有分析)
    • newarray:表示创建一个指定原始类型(如int、float、char等)并将其引用值压入栈顶(后面有分析)

四、 数组来说字节码中的秘密

/**
 * @author lijk
 * @date 2019/12/25 16:55
 */
public class MyTest1 {
     
    public static void main(String[] args) {
     
        FinalPojo[] finalPojos=new FinalPojo[1];
    }
}

class FinalPojo{
     
    static {
     
        System.out.println("FinalPojo Initialization");
    }
}

输出结果为:

Process finished with exit code 0

FinalPojo类并没有初始化;

/**
 * @author lijk
 * @date 2019/12/25 16:55
 */
public class MyTest1 {
     
    public static void main(String[] args) {
     
        FinalPojo[] finalPojos=new FinalPojo[1];
        System.out.println(finalPojos.getClass());
        System.out.println(finalPojos.getClass().getSuperclass());

    }
}

class FinalPojo{
     
    static {
     
        System.out.println("FinalPojo Initialization");
    }
}

对应数组实例来说,其类型是由jvm在运行期动态生成的,表示[Lcom.mfhcd.jvm.chapter_1.webnesday.FinalPojo;
这种形式,动态生成的类型,其父类就是Object;
对于数组来说,JavaDoc经常将构成数组的元素为Component,实际上就是将数组降低一个维度后的类型(不在是类本身)

  • 这里介绍下jvm中数组是如何表示的例如上面的[L表示any non-primitives(Object),翻译过来为任何非基元(对象)也就是非基本类型的对象[引用类型]

    • [Z = boolean
    • [B = byte
    • [S = short
    • [I = int
    • [J = long
    • [F = float
    • [D = double
    • [C = char
    • [L = any non-primitives(Object) (引用类型数组,上面的为基本类型数组)
      下面简单演示几个:
/**
 * @author lijk
 * @date 2019/12/25 16:55
 */
public class MyTest1 {
     
    public static void main(String[] args) {
     
        FinalPojo[] finalPojos=new FinalPojo[1];
        System.out.println(finalPojos.getClass());
        int[] i=new int[1];
        System.out.println(i.getClass());
        short[] s=new short[1];
        System.out.println(s.getClass());
        double[] d=new double[1];
        System.out.println(d.getClass());

    }
}

class FinalPojo{
     
    static {
     
        System.out.println("FinalPojo Initialization");
    }
}

输出结果为:

class [Lcom.mfhcd.jvm.chapter_1.webnesday.FinalPojo;
class [I
class [S
class [D

Process finished with exit code 0

以上就是引用类型数组和基本类型数组的总结分析,如果有不正确的地方,还是需要读者指正的,感谢万分…

你可能感兴趣的:(JVM)