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. 适合大量重复字符串场景,可节省内存、提高比较效率。
相关推荐
Leinwin4 小时前
OpenClaw 多 Agent 协作框架的并发限制与企业化规避方案痛点直击
java·运维·数据库
薛定谔的悦4 小时前
MQTT通信协议业务层实现的完整开发流程
java·后端·mqtt·struts
enjoy嚣士5 小时前
springboot之Exel工具类
java·spring boot·后端·easyexcel·excel工具类
罗超驿5 小时前
独立实现双向链表_LinkedList
java·数据结构·链表·linkedlist
盐水冰6 小时前
【烘焙坊项目】后端搭建(12) - 订单状态定时处理,来单提醒和顾客催单
java·后端·学习
凸头6 小时前
CompletableFuture 与 Future 对比与实战示例
java·开发语言
wuqingshun3141596 小时前
线程安全需要保证几个基本特征
java·开发语言·jvm
努力也学不会java6 小时前
【缓存算法】一篇文章带你彻底搞懂面试高频题LRU/LFU
java·数据结构·人工智能·算法·缓存·面试
攒了一袋星辰6 小时前
高并发强一致性顺序号生成系统 -- SequenceGenerator
java·数据库·mysql
小涛不学习6 小时前
Spring Boot 详解(从入门到原理)
java·spring boot·后端