理解Java常量池

Posted 黄智霖-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Java常量池相关的知识,希望对你有一定的参考价值。

注:本文的论述和代码测试均站在HotSpot的角度

前言

  在java虚拟机规范中,字符串常量池存储在方法区,而方法区是规范中的一个概念模型,没有限制虚拟机如何实现。在JDK1.6之前,字符串常量池存储于永久代中,而在1.7之后转移到了堆中,并且方法区在1.8中已经使用元空间来实现,放弃了永久代实现。

概述

  Java中字符串常量的产生有两种方法:

  • 直接使用双引号""声明的字符串,会直接进入常量池
  • 使用String类提供的intern方法,会从字符串常量池中查询当前字符串是否存在,如果不存在就会将其存入常量池

验证

注:这里暂时只分析1.7之后的情况;假设代码示例中使用""引用的字符串常量在之前都不存在于字符串常量池中;假设涉及到的对象都在堆上分配

  • 场景1:
		String str1 = new String("789");
        String str2 = "789";
        System.out.println(str1 == str2);
        //输出false

分析:第一行代码使用了"789",其会被放到字符串常量池中;str1是通过new String创建的一个新对象,存在于堆中,创建的时候发现常量池中已经有一个"789"了,那么将str1对象的value指向常量池中对应对象的char数组;之后通过str2引用了"789",此时发现常量池中已经有对应的字符串串,直接返回其引用。
  上面提到了,String的底层是通过char数组存储的,虽然str1和str2指向不同的对象,但是在创建str1的时候发现常量池中已经有对应的对象了,那么直接使用常量池中对象的char数组。

在这里插入图片描述
  这里简单验证一下:

        String str1 = new String("789654");
        String str2 = "789654";
        Field field = String.class.getDeclaredField("value");
        field.setAccessible(true);
        Object value1 = field.get(str1);
        Object value2 = field.get(str2);
        System.out.println(value1 == value2);
        ((char[]) value2)[2] = 'x';
        System.out.println("str1:" + str1 + ",str2:" + str2);
        //输出
        true
        str1:78x654,str2:78x654

  发现str1和str2的value是同一个对象,修改了str2对象的value(常量池中)之后,str1对应的value也发生了变化。

  • 场景2:
        String str1 = new String("abc") + "12312";
        String str2 = "abc12312";
        System.out.println(str1 == str2);
        //输出 false

分析:第一行代码中有两个字符串会进入字符串常量池,分别是"abc"和"12312",中途创建了一个对象new String(“abc”),这里的+号是通过StringBuilder实现的。str2引用了"abc12312",由于此时常量池中没有该字符串,所以会被存入常量池。str1指向堆中的对象,str2指向常量池中的对象。
在这里插入图片描述

  • 场景3:
        String str1 = new String("abc") + "12312";
        str1.intern();
        String str2 = "abc12312";
        System.out.println(str1 == str2);
        //输出 true

分析:和场景2的区别就是多了一个str1.intern()方法的调用。在第一行代码执行完成后,常量池中有"abc"和"12312",堆中有"abc12312"。通过调用str1.intern方法,发现常量池中不存在"abc12312",那么尝试将其存入常量池:常量池中的对象指向堆中的对象。而str2指向常量池中的对象,所以他们相同。
在这里插入图片描述

  • 场景4
		String str1 = "abc" + "12312";
        String str2 = "abc12312";
        System.out.println(str1 == str2);
        //输出true

分析:“abc” + “12312"会被编译器优化为"abc12312”,所以str1和str2都指向常量池中的对象。

以上是关于理解Java常量池的主要内容,如果未能解决你的问题,请参考以下文章

理解Java常量池

理解Java常量池

java常量池

java常量池理解

Java常量池理解与总结

Java常量池理解与总结