String.intern() 方法

intern() 是 Java 中 String 类提供的一个native本地方法(底层由C/C++实现),它的核心作用是优化字符串内存存储、保证字符串常量的唯一性。

一、前置知识:字符串的存储位置

在讲解 intern() 之前,先明确 Java 中字符串的两个核心存储区域(JDK 7 及以后版本,常量池位置有调整):

  1. 字符串常量池(String Pool) :属于堆内存的一部分(JDK 6 及以前在方法区/永久代),用于存储唯一的字符串常量,相同内容的字符串在常量池中只会存在一份。
  2. 堆内存(Heap) :用于存储通过 new 关键字创建的字符串对象,每一次 new 都会生成一个新的对象(即使内容相同)。

两种字符串创建方式的差异:

java 复制代码
// 方式1:字面量赋值,直接从常量池获取(不存在则创建并放入常量池)
String s1 = "abc";
// 方式2:new关键字创建,堆中生成新对象(常量池若无"abc",会先创建常量池版本)
String s2 = new String("abc");

// 验证:s1指向常量池,s2指向堆,引用不同
System.out.println(s1 == s2); // 输出:false

二、intern() 方法的核心定义与工作原理

1. 核心定义

intern() 方法的作用是:将当前字符串对象的引用(或副本)存入字符串常量池,并返回字符串常量池中该字符串的引用

2. 分两种场景工作(核心)

intern() 的行为取决于字符串常量池中是否已经存在与当前字符串内容相同 (通过 equals() 判断相等)的字符串:

场景1:常量池中已存在相同内容的字符串
  • 行为:不会在常量池中创建新对象/引用,直接返回常量池中已有字符串的引用。
  • 示例:
java 复制代码
String s1 = "abc"; // 常量池创建"abc",s1指向常量池
String s2 = new String("abc"); // 堆中创建新对象,s2指向堆

// 调用intern():常量池已有"abc",直接返回常量池引用
String s3 = s2.intern();

// 验证:s3与s1均指向常量池,引用相同
System.out.println(s1 == s3); // 输出:true
System.out.println(s2 == s3); // 输出:false(s2仍指向堆)
场景2:常量池中不存在相同内容的字符串
  • 行为(JDK 7+ 主流版本):直接将堆中当前字符串对象的引用存入常量池,不复制字符串内容,也不创建新对象,最终返回该引用(常量池与堆指向同一个对象)。
  • 行为(JDK 6 及以前):复制堆中字符串的内容,在常量池中创建一个新的字符串对象,返回常量池中新对象的引用(堆对象与常量池对象相互独立)。
  • 示例(JDK 7+):
java 复制代码
// 拼接字符串(这种方式不会自动将"ab"放入常量池)
String s1 = new String("a") + new String("b"); 
// 此时常量池中无"ab",intern()将s1的堆引用存入常量池,返回该引用
String s2 = s1.intern();
// 字面量"ab":直接从常量池获取(即s1的堆引用)
String s3 = "ab";

// 验证:三者指向同一个堆对象
System.out.println(s1 == s2); // 输出:true
System.out.println(s1 == s3); // 输出:true

三、关键注意点

1. intern() 有返回值,需接收返回值才有效

intern() 不会修改调用者本身的引用指向,只会返回常量池中的引用。如果不接收返回值,调用 intern() 几乎没有意义:

java 复制代码
String s = new String("abc");
s.intern(); // 未接收返回值,s仍指向堆中的对象
String sConstant = "abc";
System.out.println(s == sConstant); // 输出:false(无效调用)

// 正确用法:接收intern()返回值,获取常量池引用
String sInterned = s.intern();
System.out.println(sInterned == sConstant); // 输出:true

2. JDK 6 与 JDK 7+ 的核心差异(重点)

版本 常量池位置 常量池无对应字符串时的行为 堆与常量池引用关系
JDK 6 方法区/永久代(与堆分离) 复制堆中字符串内容,创建新的常量池对象 堆对象与常量池对象相互独立(== 为 false)
JDK 7+ 堆内存(独立分区) 直接存储堆中字符串对象的引用 常量池引用指向堆对象(== 为 true)

3. intern() 的核心价值

  • 节省内存空间 :对于大量重复的字符串(如日志中的相同字段、业务中的固定编码),调用 intern() 后可保证常量池中只存储一份,避免堆中创建大量重复对象,减少内存占用。
  • 提高字符串比较效率 :常量池中的字符串可通过 == 直接比较引用(无需调用 equals() 比较内容),效率更高。

四、完整代码示例(JDK 7+ 运行)

java 复制代码
public class StringInternDemo {
    public static void main(String[] args) {
        // 场景1:常量池已存在相同字符串
        String constantStr = "hello";
        String heapStr = new String("hello");
        String internedStr1 = heapStr.intern();

        System.out.println("场景1:");
        System.out.println(constantStr == heapStr); // false(常量池 vs 堆)
        System.out.println(constantStr == internedStr1); // true(均指向常量池)

        // 场景2:常量池不存在相同字符串
        String heapStr2 = new String("hi") + new String("java");
        String internedStr2 = heapStr2.intern();
        String constantStr2 = "hijava";

        System.out.println("\n场景2:");
        System.out.println(heapStr2 == internedStr2); // true(常量池存储堆引用)
        System.out.println(heapStr2 == constantStr2); // true(字面量指向堆引用)
    }
}

运行结果(JDK 7+)

复制代码
场景1:
false
true

场景2:
true
true

总结

  1. intern()String 类的 native 方法,核心作用是将字符串纳入常量池管理,保证唯一性。
  2. 常量池已有对应字符串时,直接返回常量池引用;无对应字符串时(JDK 7+),存储堆对象引用并返回。
  3. 必须接收 intern() 的返回值,才能获取常量池中的有效引用。
  4. 适合大量重复字符串场景,可节省内存、提高比较效率。
相关推荐
毕设源码-郭学长10 小时前
【开题答辩全过程】以 基于SpringBoot框架的民俗文化交流与交易平台的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
好大哥呀10 小时前
Java Web的学习路径
java·前端·学习
f***147710 小时前
SpringBoot实战:高效实现API限流策略
java·spring boot·后端
on the way 12310 小时前
day06-SpringDI 依赖注入
java·spring
C***115011 小时前
Spring aop 五种通知类型
java·前端·spring
BD_Marathon11 小时前
SpringBoot——多环境开发配置
java·spring boot·后端
代码N年归来仍是新手村成员11 小时前
【Java转Go】即时通信系统代码分析(一)基础Server 构建
java·开发语言·golang
关于不上作者榜就原神启动那件事12 小时前
Java中大量数据Excel导入导出的实现方案
java·开发语言·excel
Coder_Boy_13 小时前
基于SpringAI的在线考试系统设计总案-知识点管理模块详细设计
android·java·javascript
Assby13 小时前
如何尽可能精确计算线程池执行 shutdown() 后的耗时?
java·后端