简单了解常量池

本文深入探讨Java中常量池的概念,包括其如何优化内存使用,以及基本包装类如Integer如何利用常量池。同时,文章详细讲解了自动装箱和自动拆箱的过程,通过实例说明了它们在不同情况下的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


首先来看下一面这一段代码

public class main {

    public static void main(String[] args) {
        Integer n1 = 127;
        Integer n2 = 127;
        System.out.println(n1 == n2);
        Integer n3 = 128;
        Integer n4 = 128;
        System.out.println(n3 == n4);
        Integer n5 = new Integer(127);
        System.out.println(n1 == n5);
    }
}

会有以下输出
在这里插入图片描述
第一行与第二行打印为何有不同?原因如下:
Java为了提高程序性能,为很多基本类型的包装类/字符串建立了常量池

一、常量池

1.基本特点

相同的值只存储一份,节省内存,共享访问,并且他的值也是不可被修改的,类似于static修饰的属性。

2.基本的包装类

  • Boolean,Byte,Short,Integer,Long,Charater,Float,Double

3.建立常量池的包装类

Float与Double没有常量池

  • Boolean:true,false
  • Byte:-128~127
  • Character:0~127
  • Short、Int、Long:-128~127

二、回到例子

由于127在常量池内,故在定义n1与n2时,除非像n5,显示地创建一个新的对象,否则他们都指向同一块内存。

  • 注意:
    常量式赋值创建,放在栈内存(常量化):例如n1、n2
    new对象行创建,放在堆内存(不会常量化):n5

这也解释了为什么n1与n5引用不同,因为他们创建对象存放的位置不同。因为堆的容量大,读取慢,栈的容量小,读取快,故常量化可以加速(一般new出来的对象占用内存都比较大,故显现实地使用new来创建Interger时,ide会保守地不常量化)。

而面对String类型时,也会进行同样地优化

public class main {

    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        String s3 = "ab" + "c"; // 都是常量,编译器优化,下同
        String s4 = "a" + "b" + "c";
        System.out.println(s1 == s2); // true
        System.out.println(s1 == s3); // true
        System.out.println(s1 == s4); // true
    }
}

三、自动拆箱与自动装箱

下面自动装箱的意思为:对10进行包装成为类,而i2的引用指向这个存放在栈上的对象,变为Integer类型。而自动拆箱则与之相对,将包装去掉,变回int类型。

public class main {

    public static void main(String[] args) {
        int i1 = 10;
        Integer i2 = 10; // 自动装箱且i2在栈内存
        System.out.println(i1 == i2);   // true
        //   自动拆箱,基本数据类型与包装类进行比较,包装类自动拆箱

        Integer i3 = new Integer(10);   // 堆内存
        System.out.println(i1 == i3);	// true
        //   自动拆箱,基本数据类型与包装类进行比较,包装类自动拆箱

        System.out.println(i2 == i3);   // false
        //  两个对象比较,比较他们的地址
        
        Integer i4 = new Integer(5);
        Integer i5 = new Integer(5);
        System.out.println(i1 == i4+i5);    //  true
        System.out.println(i2 == i4+i5);    //  true
        System.out.println(i3 == i4+i5);    //  true
        //   自动拆箱,基本数据类型与包装类进行比较,包装类自动拆箱
        
        Integer i6 = i4 + i5;
        // + 操作使i4、i5自动拆箱,得到10,故i6 == i2
        System.out.println(i1 == i6);   // true
        System.out.println(i2 == i6);   // true
        System.out.println(i3 == i6);   // false
    }
}

其中,i4+i5的结果为int类型。
而对于String类型也有相类似的操作

public class main {

   public static void main(String[] args) {
       String s0 = "abcdef"; // 栈内存
       String s1 = "abc";    // 栈内存且s2、s1指向相同的内存
       String s2 = "abc";    // 栈内存
       String s3 = new String("abc"); // 堆内存
       String s4 = new String("abc"); // 堆内存
       System.out.println(s1 == s2); // true 常量池
       System.out.println(s1 == s3); // false 一个栈内存、一个堆内存
       System.out.println(s3 == s4); // false 两个堆内存

       String s5 = s1 + "def";  // 涉及到变量,编译器不优化
       String s6 = "abc" + "def"; // 都是常量,编译器自动优化为"abcdef"
       String s7 = "abc" + new String("def"); // 涉及到new对象,编译器不优化
       System.out.println(s5 == s6); // false
       System.out.println(s5 == s7); // false
       System.out.println(s6 == s7); // false
       System.out.println(s0 == s6); // true
   }
}

从s5的例子也能看出,String类型为immutable的

<think>嗯,用户问的是Java中的字符串常量是否存储在常量池中。首先,我需要回忆一下Java中字符串常量池的相关知识。记得在Java里,字符串常量池是用来存储字符串字面量的,比如"Hello"这样的。根据引用的资料,字符串常量池在JDK7之前是在方法区,之后移到了堆里。 然后,用户提到的“字符串常量”应该是指直接写在代码里的字面量,比如String s = "abc";这里的"abc"就是字符串常量。根据引用[2],字符串常量会被放入字符串常量池中,并且不可变,所以可以共享。也就是说,多个相同的字面量会指向池里的同一个对象,节省内存。 不过要注意,如果是通过new String("abc")这种方式创建的,会在堆中新建对象,而不是直接使用常量池里的。这时候可能需要调用intern()方法才能放入池中。引用[3]中的例子也说明了,直接赋值的字面量会比较地址相同,而new出来的对象地址不同。 另外,引用[4]提到类文件常量池和运行时常量池的区别,类被加载时,字面量会被加载到运行时常量池,而字符串常量池是全局共享的。所以字符串常量确实存储在常量池中,但具体位置随着JDK版本变化。 需要确认用户是否了解不同版本JDK的变化,可能需要简单说明一下。同时,用户可能想知道如何确保字符串被池化,比如使用intern()方法,或者直接赋值。还要注意字符串不可变性对常量池的影响,确保解释清楚为什么相同的字面量会指向同一个对象。 总结回答时,应该分点说明:存储位置、共享机制、不同创建方式的影响,以及JDK版本的变化。这样用户就能清楚理解字符串常量池的作用和存储情况了。</think>### 回答 是的,**Java 字符串常量(即直接通过字面量定义的字符串)会被存储于字符串常量池(StringTable)中**,具体分析如下: --- #### 1. **字符串常量池的定位与作用** - 字符串常量池是 JVM 中专门用于存储字符串字面量的特殊区域。例如: ```java String s1 = "hello"; // "hello" 直接存储在字符串常量池中 String s2 = "hello"; // 复用常量池中的 "hello" ``` 此时 `s1 == s2` 结果为 `true`,因为它们指向常量池中的同一对象[^3]。 - 在 JDK 7 及之前版本,字符串常量池位于方法区(Method Area);**JDK 8 及之后版本**,字符串常量池被移至堆内存(Heap)中[^1][^4]。 --- #### 2. **字符串常量的存储规则** - **唯一性**:常量池中不会存储内容相同的字符串。例如: ```java String s3 = "world"; // 首次创建,存入常量池 String s4 = "world"; // 直接复用常量池中的 "world" ``` - **共享性**:所有使用相同字面量的字符串变量会共享常量池中的同一对象,这是由 String 的不可变性保证的[^2]。 - **与 `new String()` 的区别**: ```java String s5 = new String("hello"); // 在堆中创建新对象,不与常量池共享 String s6 = "hello"; // 直接引用常量池中的对象 System.out.println(s5 == s6); // 输出 false ``` 通过 `new` 创建的字符串对象不会直接放入常量池,除非显式调用 `intern()` 方法[^4]。 --- #### 3. **常量池的关联机制** - **类文件常量池**:编译时,字面量(如 `"hello"`)会被记录在 `.class` 文件的常量池中。 - **运行时常量池**:类加载时,字面量从类文件常量池加载到运行时常量池,最终字符串常量池(StringTable)会管理这些实际字符串对象的存储和复用。 --- #### 4. **总结 - **字符串常量(字面量)一定存储于字符串常量池**,且全局唯一。 - **动态生成的字符串(如 `new String()` 或拼接结果)** 默认不在常量池中,需通过 `intern()` 方法手动加入。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值