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. 适合大量重复字符串场景,可节省内存、提高比较效率。
相关推荐
左左右右左右摇晃8 分钟前
手写Tomcat原理整理
java·开发语言·笔记·tomcat
孫治AllenSun11 分钟前
【JVM】四大引用类型分析
java·开发语言·jvm
于慨13 分钟前
安装java
java
夕除15 分钟前
sign 是什么
java·前端
Penina101840 分钟前
代码如何导入idea
java·intellij idea
.Hypocritical.1 小时前
Maven笔记——2
java·笔记·maven
huohuopro1 小时前
手写 Tomcat 步骤教程
java·网络
snow@li1 小时前
Java:Lombok 完整讲解
java
Shadow(⊙o⊙)1 小时前
高并发内存池:Part-1——定长内存池
java·前端·javascript
国服第二切图仔1 小时前
17-config命令 - 配置管理系统
java·前端·javascript