Java 中的常量池

两种常量池

一、静态常量池

  • 要了解常量池,需要先对 jvm 的内存模型有一定的了解

1、jvm 内存模型

在这里插入图片描述

  • 如上图所示,JVM 的内存区域基本分为五个部分
本地方法栈
  • 调用操作系统方法所用的栈
程序计数器
  • 就是指示字节码的执行行数的
虚拟机栈
  • 就是执行 java 代码的栈
  • 一般用来存储对象,还包括常量池等
方法区
  • 可以理解成class文件在内存中的存放位置,存放的就是已加载类的一些基本信息

2、静态常量池

  • 所谓静态常量池就是class文件中的常量池,主要用于存放两大类常量:字面量符号引用量
  • 字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等
  • 符号引用包括了如下三种类型的常量:类和接口的全限定名、字段名称和描述符、方法名称和描述符

二、动态常量池

  • 运行时常量池是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在虚拟机堆中,一般说的常量池就是指的这个
  • 运行时常量池最重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入class文件中常量池的内容才能进入堆中的常量池,运行期间也可能将新的常量放入池中
  • String的intern()方法:查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池

字符串常量池

  • 基本来讲我们接触最多的就是字符串常量池

一、经典示例

1、示例代码

String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;
          
System.out.println(s1 == s2);  // true
System.out.println(s1 == s3);  // true
System.out.println(s1 == s4);  // false
System.out.println(s1 == s9);  // false
System.out.println(s4 == s5);  // false
System.out.println(s1 == s6);  // true

2、示例说明

  • s1 == s2 很好理解
  • s1 == s3 是编译器针对 + 操作符的优化,但是这样的优化仅限两边都是常量的时候
  • s1 == s4 new的部分不可预知,编译器无法进行优化
  • s1 == s9 s7和s8都是变量也是无法优化的
  • s4 == s5 都会产生新的对象存放于堆中,肯定不同的
  • s1 == s6 intern 方法就是先去常量池中找,这里显然是找到了

二、几种特例

  • A和B明显就是两个常量而且是立即初始化的,所以这里编译器就可以进行优化了
public static final String A = "ab"; // 常量A
public static final String B = "cd"; // 常量B
public static void main(String[] args) {
     String s = A + B;  // 将两个常量用+连接对s进行初始化 
     String t = "abcd";   
    if (s == t) {   
         System.out.println("s等于t,它们是同一个对象");   
     } else {   
         System.out.println("s不等于t,它们不是同一个对象");   
     }   
 } 
//s等于t,它们是同一个对象
  • 这里虽然也是常量但是没有立即初始化,存在不确定性,编译器无法进行优化,因为编译器也不是万能的呀
public static final String A; // 常量A
public static final String B;    // 常量B
static {   
     A = "ab";   
     B = "cd";   
 }   
 public static void main(String[] args) {   
    // 将两个常量用+连接对s进行初始化   
     String s = A + B;   
     String t = "abcd";   
    if (s == t) {   
         System.out.println("s等于t,它们是同一个对象");   
     } else {   
         System.out.println("s不等于t,它们不是同一个对象");   
     }   
 } 
//s不等于t,它们不是同一个对象

总结

  • 必须要关注编译期的行为,才能更好的理解常量池。
  • 运行时常量池中的常量,基本来源于各个class文件中的常量池。
  • 程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则jvm不会自动添加常量到常量池

补充

  • 整数类型包装类BYTE范围内(-128到127)是存放于常量池的
  • 浮点类型的包装类没有实现常量池技术

参考文章:深入浅出java常量池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值