zoukankan      html  css  js  c++  java
  • Java中常量以及常量池

    1、举例说明 变量 常量 字面量

    1 int a=10;
    2 float b=1.234f;
    3 String c="abc";
    4 final long d=10L;

    a,b,c为变量,d为常量 两者都是左值;10,1.234f,"abc",10L都是字面量;

    2、常量池:

    常量池专门用来用来存放常量的内存区域,常量池分为:静态常量池运行时常量池;

    静态常量池:*.class文件中的常量池,class文件中的常量池不仅仅包含字符串,数值字面量,还包含类、方法的信息,占用class文件绝大部分空间。

    运行时常量池:是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。

    备注:java虚拟机内存分为虚拟机栈、虚拟机堆、本地方法栈、程序计数器、方法区(jdk8中,移除了方法区,转而用Metaspace区域替代)

    2.1 字符串常量池

    复制代码
     1 String s1 = "Hello";
     2 String s2 = "Hello";
     3 String s3 = "Hel" + "lo";
     4 String s4 = "Hel" + new String("lo");
     5 String s5 = new String("Hello");
     6 String s6 = s5.intern();
     7 String s7 = "H";
     8 String s8 = "ello";
     9 String s9 = s7 + s8;
    10           
    11 System.out.println(s1 == s2);  // true
    12 System.out.println(s1 == s3);  // true
    13 System.out.println(s1 == s4);  // false
    14 System.out.println(s1 == s9);  // false
    15 System.out.println(s4 == s5);  // false
    16 System.out.println(s1 == s6);  // true
    复制代码

         java程序经过编译和运行两步:

         s1 == s2,编译时,将字面量"Hello"直接放入class文件的常量池中,从而实现复用,载入运行时常量池后,s1、s2指向的是同一个内存地址,所以相等。

         s1 == s3,编译时,这种拼接会被优化,编译器直接拼好,在class文件中被优化成String s3 = "Hello";,所以s1 == s3成立。

         s1 == s4,编译时,new String("lo") 如何生成 在哪生成还不确定,是一个不可预料的部分,编译器不会优化,必须等到运行时才可以确定结果,所生成后的引用在堆中,而不是方法区,所以地址肯定不同。

    java字符串不变   

     s1 == s9 编译时,s7、s8在赋值的时候使用的字符串字面量,但是拼接成s9的时候,s7、s8作为两个变量,都是不可预料的,编译器毕竟是编译器,不可能当解释器用,所以不做优化,等到运行时,s7、s8拼接成的新字符串,在堆中地址不确定,不可能与方法区常量池中的s1地址相同。
    jvm常量池,堆,栈内存分布

         s4 == s5已经不用解释了,绝对不相等,二者都在堆中,但地址不同。

         s1 == s6这两个相等完全归功于intern方法(手工在常量池添加常量),s5在堆中,内容为Hello ,intern方法会尝试将Hello字符串添加到常量池中,并返回其在常量池中的地址,因为常量池中已经有了Hello字符串,所以intern方法直接返回地址;而s1在编译期就已经指向常量池了,因此s1和s6指向同一地址,相等。

    2.2 8种基本类型的包装类和对象池 

    java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。

     1 public class Test{
     2 
     3 public static void main(String[] args){
     4 
     5    //5种整形的包装类Byte,Short,Integer,Long,Character的对象,
     6 
     7    //在值小于127时可以使用常量池
     8 
     9    Integer i1=127;
    10 
    11    Integer i2=127;
    12 
    13    System.out.println(i1==i2)//输出true
    14 
    15    //值大于127时,不会从常量池中取对象
    16 
    17    Integer i3=128;
    18 
    19    Integer i4=128;
    20 
    21    System.out.println(i3==i4)//输出false
    22 
    23    //Boolean类也实现了常量池技术
    24 
    25    Boolean bool1=true;
    26 
    27    Boolean bool2=true;
    28 
    29    System.out.println(bool1==bool2);//输出true
    30 
    31    //浮点类型的包装类没有实现常量池技术
    32 
    33    Double d1=1.0;
    34 
    35    Double d2=1.0;
    36 
    37    System.out.println(d1==d2)//输出false
    38 
    39  
    40 
    41 }
    42 
    43 }

    2.3 查看常量池

    1 String s = "hi";

    将代码编译成class文件后,用winhex打开二进制格式的class文件。如图:

     

         class文件的结构:

      1.开头的4个字节是class文件魔数,用来标识这是一个class文件,说白话点就是文件头,既:CA FE BA BE。

         2.紧接着4个字节是java的版本号,这里的版本号是34,是用jdk8编译的。

         3.接下来就是常量池入口,入口处用2个字节标识常量池常量数量,本例中数值为00 1A,十进制是26,也就是有25个常量,其中第0个常量是特殊值,所以只有25个常量。

         4.常量池中存放了各种类型的常量,他们都有自己的类型,并且都有自己的存储规范,字符串常量以01开头(1个字节),接着用2个字节记录字符串长度,然后就是字符串实际内容。本例中为:01 00 02 68 69。

         接下来再说说运行时常量池,由于运行时常量池在方法区中,我们可以通过jvm参数:-XX:PermSize、-XX:MaxPermSize来设置方法区大小,从而间接限制常量池大小。

         假设jvm启动参数为:-XX:PermSize=2M -XX:MaxPermSize=2M,然后运行如下代码:

    复制代码
    1 //保持引用,防止自动垃圾回收
    2 List<String> list = new ArrayList<String>();
    3         
    4 int i = 0;
    5         
    6 while(true){
    7     //通过intern方法向常量池中手动添加常量
    8     list.add(String.valueOf(i++).intern());
    9 }
    复制代码

         程序立刻会抛出:Exception in thread "main" java.lang.outOfMemoryError: PermGen space异常。PermGen space正是方法区,足以说明常量池在方法区中。

         在jdk8中,移除了方法区,转而用Metaspace区域替代,所以我们需要使用新的jvm参数:-XX:MaxMetaspaceSize=2M,依然运行如上代码,抛出:java.lang.OutOfMemoryError: Metaspace异常。同理说明运行时常量池是划分在Metaspace区域中。

    参考:

    触摸java常量池

    学习技术不是用来写HelloWorld和Demo的,而是要用来解决线上系统的真实问题的.
  • 相关阅读:
    不务正业系列-浅谈《过气堡垒》,一个RTS玩家的视角
    [LeetCode] 54. Spiral Matrix
    [LeetCode] 40. Combination Sum II
    138. Copy List with Random Pointer
    310. Minimum Height Trees
    4. Median of Two Sorted Arrays
    153. Find Minimum in Rotated Sorted Array
    33. Search in Rotated Sorted Array
    35. Search Insert Position
    278. First Bad Version
  • 原文地址:https://www.cnblogs.com/cac2020/p/5046880.html
Copyright © 2011-2022 走看看