在Java编程中,字符串是最常用的数据类型之一,但是对于字符串的操作往往需要注意内存的使用和性能问题。本文我们将深入探讨Java中的字符串优化技术,重点关注于String类的intern()方法,以及如何正确地使用它来优化字符串操作
今日内容介绍,大约花费9分钟
图片
昨天介绍了深入理解Java字符串常量池,介绍了String创建方法以及字符串常量池,今天介绍String一个方法String.intern(),大家可以看看美团写的美团技术团队深入解析 String.intern()文章,这是精品中精品,可是大家看了之后会觉得,我要放弃学习Java,因为有点看不懂,那么我简化给大家讲讲,前提是小伙伴已经理解昨天深入理解Java字符串常量池这篇文章一下几点:
- 1.使用双引号声明的字符串对象会保存在字符串常量池中
- 2.使用 new 关键字创建的字符串对象会先从字符串常量池中找,如果没找到就创建一个,然后再在堆中创建字符串对象;如果找到了,就直接在堆中创建字符串对象
思考:new String("spring") + new String("葵花宝典");操作如何进行性能优化
1.String.intern()简介
String str = new String("spring") + new String("葵花宝典");
如果要把上面str内容存放到常量池,就需要使用intern()方法
注意:Java 7时,字符串常量池从永久代中移动到了堆中,但是永久代还没有完全被移除。Java 8时,永久代被彻底移除。
Java 7之前和Java 7之后,String.intern()方法在执行时的策略发生了变化,这一变化直接影响了内存的利用方式
- 在Java 7之前,无论对象是否已经存在于堆中,String.intern()方法都会在字符串常量池中创建一个新的对象。
- Java 7之后,由于字符串常量池被移动到了堆中,执行String.intern()方法时,如果堆中已经存在了该对象,字符串常量池中就不会创建新的对象,而是直接保存堆中对象的引用。这一优化节省了一部分内存空间。
2. String.intern()举例说明
可能小伙伴还没有理解,那么别怕,通过以下例子进行说明
2.1. new String("spring葵花宝典")
String str1 = new String("spring葵花宝典");
String str2= str1.intern();
System.out.println(str1 == str2);
思考:大家猜猜上面代码结果是什么?可能小伙伴猜不出来,那么我直接来解释一下
第一行,字符串常量池中会先创建一个spring葵花宝典的对象,然后堆中会再创建一个spring葵花宝典的对象,str1 引用的是堆中的对象。
第二行,str1 执行 intern() 方法,该方法会从字符串常量池中查找spring葵花宝典,如果常量池中存在spring葵花宝典字符串是否存在,因为第一行代码已经在字符串常量池创建spring葵花宝典,所以 str2引用的是字符串常量池中的对象
图片
image
str1 和 str2 的引用地址是不同的,str1一个来自堆,str2一个来自字符串常量池,所以输出的结果为 false。
2.2. new String("spring") + new String("葵花宝典")
String str1 = new String("spring") + new String("葵花宝典");
String str2= str1.intern();
System.out.println(str1 == str2);
思考:2.1输出结果是false,那么2.2结果也是false?
可能小伙伴猜错了,代码输出结果为true,这是为啥?
第一行,字符串常量池中会先创建两个对象spring和葵花宝典,然后堆中会再创建两个匿名对象spring和葵花宝典,最后还有一个spring葵花宝典对象(一会解释),str1 引用的是堆中spring葵花宝典对象。
第二行,str1 执行 intern() 方法,该方法会从字符串常量池中查找spring葵花宝典对象是否存在,此时字符串常量池不存在,但是堆中存在
图片
image
字符串常量池中保存的是堆中spring葵花宝典对象的引用,也就是说,str1 和 str2 的引用地址是相同,所以输出的结果为 true
具体步骤如下:
- 创建 "spring" 字符串对象,存储在字符串常量池中。
- 创建 "葵花宝典" 字符串对象,存储在字符串常量池中。
- 执行 new String("spring"),在堆上创建一个字符串对象,内容为 "spring"。
- 执行 new String("葵花宝典"),在堆上创建一个字符串对象,内容为 "葵花宝典"。
- 执行 new String("spring") + new String("葵花宝典"),会创建一个 StringBuilder 对象,并将 "spring" 和 "葵花宝典" 追加到其中,然后调用 StringBuilder 对象的 toString() 方法,将其转换为一个新的字符串对象,内容为 "spring葵花宝典"。这个新的字符串对象存储在堆上。
特别说明:编译器遇到 + 号这个操作符的时候,会将 new String("spring") + new String("葵花宝典") 编译代码如下:
new StringBuilder().append("spring").append("葵花宝典").toString();
实际步骤如下:
- 创建一个 StringBuilder 对象。
- StringBuilder 对象上调用 append("spring"),将 "spring" 追加到 StringBuilder 中。
- 在 StringBuilder 对象上调用 append("葵花宝典"),将 "葵花宝典" 追加到 StringBuilder 中。
- 在 StringBuilder 对象上调用 toString() 方法,将 StringBuilder 转换为一个新的字符串对象,内容为 "spring葵花宝典"
3. String.intern()使用注意事项
尽管String.intern()方法能够有效地优化字符串操作,但是在使用时需要注意以下几点:
- 不要滥用intern()方法: 虽然intern()方法可以确保所有具有相同内容的字符串共享相同的内存空间,但也不要随意使用,因为字符串常量池是有大小限制的,过多的字符串可能会导致性能下降。
- 注意内存消耗: 使用intern()方法可能会增加内存消耗,因为它会将字符串对象存储到常量池中,而常量池是位于堆内存中的一部分。