当前位置: 首页 > article >正文

java常量池

一 常量池示例

    /**
     *  字符串:"奶茶" 存在堆内存的 字符串常量池。
     * 其他常量(如数字、类名):存在方法区(元空间)的 运行时常量池。
     *
     */
    public static void main(String[] args) {

        String s1 = "奶茶";  // 第一次写“奶茶”,存到公共菜单(常量池)
        String s2 = "奶茶";  // 直接拿菜单上的“奶茶”,不用新做一杯
        String s3 = new String("奶茶"); // 强行做一杯新的,但原料还是菜单上的“奶茶” 。重新创建了一个对象,堆中的地址不同。
        先在堆常量池创建一份(如果没有),然后再在堆中创建一个对象。

//        s1="12";
        System.out.println(s1 == s2);
        System.out.println(s1 == s3);

        System.out.println(s1 .equals(s2) );
        System.out.println(s1 .equals(s3) );

    }
}

二 说明

1

创建方式的本质区别
s1 = “奶茶”

这是通过字符串字面量(String Literal)​创建的字符串。
JVM 会先在字符串常量池​(位于堆内存中)中查找是否有 “奶茶”:
如果存在,直接复用该对象(地址相同)。
如果不存在,在常量池中新建一个对象
​特点:多个相同的字面量会指向同一个对象。

2

s3 = new String(“奶茶”)

这是通过 ​new 关键字在堆内存中强制创建新对象
JVM 会:
先在字符串常量池中检查 “奶茶” 是否存在(如果不存在,则先在常量池中创建)。
然后在堆内存中创建一个全新的 String 对象,内容与 “奶茶” 相同,但地址不同。
​特点:每次 new 都会生成新对象,即使内容相同。

二 常量池存储位置

1

public class Example {
    // 运行时常量池(元空间):存储符号引用和字面量 "Hello"
    private static final String MESSAGE = "Hello";

    public static void main(String[] args) {
        // 字符串常量池(堆内存):存储 "Hello"
        String s1 = "Hello";
        // 堆内存:创建新对象,但引用字符串常量池的 "Hello"
        String s2 = new String("Hello");
        // 字符串常量池(堆内存):新增 "World"
        String s3 = "World";
    }
}

二 运行时常量

1 运行时常量池 ≠ static final 变量

​1. static final 变量的存储
​基本类型(如 static final int MAX = 100)​:
值直接存储在运行时常量池中。
​引用类型(如 static final String S = “OK”)​:
引用(指针)存储在运行时常量池中,实际对象(如 “OK”)在堆内存的字符串常量池中。

2

运行时常量池的内容远不止 static final
除了 static final 变量,运行时常量池还包含:

​类的符号引用:如 java/lang/String。
​方法的符号引用:如 java/io/PrintStream.println。
​动态生成的常量:如通过 String.intern() 添加的字符串。
​字段的符号引用:如 MyClass.count。
​字面量:如代码中直接写的 “Hello” 或 123。

三 关键总结

1 ​Class 文件常量池:

静态数据,存储在 .class 文件中,包含符号引用和字面量。
例如:java/lang/String 的符号引用、字符串 “Hello” 的字面量。

2 ​运行时常量池:

动态数据,存储在方法区(元空间),包含解析后的直接引用和运行时生成的常量。
​不仅仅是 static final 变量:还包含类、方法、字段的元数据,以及动态内容(如 intern() 的字符串)。

3 ​static final 变量的特殊性

基本类型的 static final 变量直接存储在运行时常量池。
引用类型的 static final 变量存储引用(在运行时常量池),对象在堆中

4 Class 文件常量池 vs 运行时常量池

核心区别
对比项Class 文件常量池运行时常量池
存储位置编译后的 .class 文件中(静态文件)JVM 内存的方法区(元空间)中(动态运行时数据)
存储内容符号引用(类名、方法名、字段名等)、字面量(数值、字符串)Class 文件常量池的副本 + 运行期动态添加的常量(如 String.intern() 的字符串)
生命周期永久(随 .class 文件存在)类加载时创建,类卸载时销毁
是否可修改不可修改(静态编译数据)可动态扩展(例如运行时添加新的常量)
符号引用解析未解析(如 java/lang/Object已解析为直接引用(如内存地址)

四 常见误区

1

​误区:认为运行时常量池只存 static final 变量。
​纠正:运行时常量池的核心是类元数据,static final 变量只是其中一部分。

2

​误区:认为 static final String S = new String(“OK”) 的 “OK” 在运行时常量池。
​纠正:new String(“OK”) 的 “OK” 在堆中,但字面量 “OK” 的引用在运行时常量池。

五 String s2 = new String(“Hello”);为什么创建两个对象?

1 为什么会有两个对象?

当执行 String s2 = new String(“Hello”); 时,JVM 会执行以下步骤:

​检查字符串常量池:
首先检查字符串常量池中是否存在字面量 “Hello”。

如果存在,直接复用该对象。
如果不存在,在字符串常量池中创建一个新的 “Hello” 对象。
​在堆中创建新对象:
无论常量池中是否存在 “Hello”,new String(“Hello”) 都会在堆内存中创建一个全新的 String 对象,内容与常量池中的 “Hello” 相同。

结果

​常量池对象:字面量 “Hello” 的对象(共享)。
​堆对象:new 创建的全新 String 对象(独立)。

2 看似重复,但逻辑合理:

字符串常量池的设计是为了 ​共享不可变对象,减少内存占用。
new 关键字的设计是为了 ​显式创建独立对象,满足某些特殊场景需求(如需要不同实例)。

六 字符串常量池在堆内存中吗?

是的!​Java 7 及之后版本,字符串常量池的位置从永久代(PermGen)迁移到了堆内存(Heap)。

​Java 6 及之前:字符串常量池在永久代。
​Java 7+:字符串常量池在堆内存中。
迁移原因:

​永久代大小固定:容易导致 OutOfMemoryError: PermGen space。
​堆内存动态扩展:字符串常量池可以随堆内存动态调整,避免内存溢出。
​垃圾回收:堆内存中的字符串常量池对象可以被垃圾回收(无引用时)。

2 内存结构示意图

+---------------------+      +---------------------+
|   字符串常量池(堆内存)  |      |       堆内存          |
|---------------------|      |---------------------|
|   String对象 "Hello"   | <---- s1(引用地址相同)       |
+---------------------+      |                     |
                             |   new String("Hello") | <---- s2(全新对象)
                             +---------------------+

s1 = "Hello":直接指向字符串常量池中的对象。
s2 = new String("Hello"):指向堆中的新对象,但字面量 "Hello" 会先在常量池中创建(如果不存在)。


3 总结

操作结果
String s1 = "Hello";直接使用字符串常量池中的对象(无重复创建)。
String s2 = new String("Hello");先在常量池中创建 "Hello"(如不存在),再在堆中创建新对象(可能重复,但逻辑合理)。

字符串常量池在堆内存中:Java 7+ 的优化设计,避免永久代内存溢出。
重复创建的合理性new 关键字的设计目标与常量池不同,需根据场景选择。


http://www.kler.cn/a/588759.html

相关文章:

  • Java-servlet(六)详细讲解Servlet-Web.xml标签知识
  • 【第14届蓝桥杯】软件赛CB组省赛
  • 机器视觉最佳光源方案 = 目标特征最大化 + 干扰最小化 + 环境适应性 + 成本可控,机器视觉检测项目中工业光源方案设计原则
  • Wireshark 对 https 请求抓包并展示为明文
  • 第二十二篇 SQL优化之等价改写:从青铜到王者
  • linux 命令 grep
  • 拆解 “ES 已死“ 伪命题:Agentic RAG 时代搜索引擎的终极形态
  • Java 内存区域常见面试题
  • JVM常用概念之信任非静态final字段
  • Podman 1panel中容器管理docker替换为Podman
  • OpenSSL 的主要功能及其示例命令
  • 网络空间安全(31)安全巡检
  • 【eNSP实战】配置Easy IP
  • DataWhale 大语言模型 - 长上下文模型和新型架构
  • 排序算法——堆排序(四)
  • C++|构造函数和析构函数
  • java自带日志系统介绍(JUL)以及和Log4j 2、Logback、SLF4J不同日志工具的对比
  • leetcode日记(99)不同的子序列
  • PyTorch使用-张量的创建
  • CSS 知识点总结1