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的,而是要用来解决线上系统的真实问题的.
  • 相关阅读:
    X3850M2安装CertOS 7 KVM 2--Mount
    X3850M2安装CertOS 7 KVM 2--VNC
    X3850M2安装CertOS 7 KVM
    vs2012 opencv 配置
    asp.net MVC code first Migrations : Model 同步到DB中
    HyperV采用硬盘拷贝的方式迁移虚拟机后的问题处理
    事后诸葛亮
    个人作业——软件产品案例分析
    冲刺总结随笔
    Alpha第九天
  • 原文地址:https://www.cnblogs.com/cac2020/p/5046880.html
Copyright © 2011-2022 走看看