双等号(==)操作符常常用来和equal方法比较,对于引用类型,==操作符相当于比较内存地址,同一个类型的两个实例,用==判断结果一定是false;equal方法不同对象实现不同。
然,对于String类型做如下测试代码:
String a = "abc"; String b = "abc"; System.out.println(a == b);
变量a、b是String类型的两个变量,String非基础类型,双等号判断内存地址,结果应该是false,但这段代码输出的是true。
why?
针对String类型双等号,equal,intren 我设计了几个case,测试报告如下:
case 1 [a-> "abc", b-> "abc"] a==b, result is [true] a.equals(b), result is [true] case 2 [a-> "abc", b-> "ab" + "c"] a==b, result is [true] a.equals(b), result is [true] case 3 [c-> "c", a-> "abc", b-> "ab" + c] a==b, result is [false] a.equals(b), result is [true] action [String.intern] a=b, result is [true] case 4 [a-> "abc", b-> new String("abc")] a==b, result is [false] a.equals(b), result is [true] action [String.intern] a=b, result is [true]
这个测试结果是比较令我感到诧异的,有很多和我想象中不一样的地方。
1. case1 和 case2的双等号判断,都是true,这本是不同的两个对象做双等号判断,怎么会是true呢?
2. 既然case1 和case2的双等号判断是true,为什么case3 和 case4 的双等号判断又是false了呢?
3. 为什么调用了intern方法之后,原来是false的判断就编程了true了呢?
根据第三点可以猜测一下,intern方法应该是会把引用互相equal的却不是同一个对象的字符串对象的变量合并成指向同一个字符串对象(此句好绕口, 用英语试试? intern will make 2 different reference who refer to 2 different String object refer to 1 same string object), 因为String类型是不可变对象,所以这种处理即可节省内存空间,又不会有什么问题,因为String是不可变对象。
java.lang.String#intern方法的非猜测解释:
这是一个native的方法,虚拟机在运行时有一块内存区域叫做“运行时常量池”,常量池用来存放编译期生成的各种字面量和符号引用。
intern方法的作用是,如果字符串常量池中包含一个等于此String对象的字符串,则返回代表池中这个字符串的对象,否则,将此String对象好汉的字符串添加到常量池中,并返回此String对象的引用。
这样不难解释为什么原本false的结果调用过intern之后就变成了true。
那么现在解释一下1 和 2 两个疑问:
前面所说,常量池用来存放编译期生成的各种字面量和符号引用, case1 和 case2 正好都是字面量,所以他们会被加入到常量池中,常量池中互相equal的字符串肯定是不允许存在的,所以相当于两个变量指向到了同一个对象。
(什么是字面量,自行百度,其实我也是看的百度,但有一个情况需要说一下:
case 2 中 a-> "abc", b-> "ab" + "c" , b由两个字面量拼接而成,b还是字面量;
case 3 中 c-> "c", a-> "abc", b-> "ab" + c,b由一个字面量和变量c 拼接而成,b不是字面量
)
参考资料:1. 《深入理解JAVA虚拟机》 第二版 第二章
2. http://www.cnblogs.com/zdwillie/archive/2013/10/23/3384766.html
附录:(生成测试报告的代码)
public class TestString { public static void main(String args[]){ Map<String, String> cases = new LinkedHashMap<String, String>(); init(cases); for(Map.Entry<String, String> case_ : cases.entrySet()){ System.out.println(String.format("case [%s]", case_.getKey())); test("abc", case_.getValue()); System.out.println(); } } private static void test(String a, String b){ boolean result = (a==b); System.out.println(String.format("a==b, result is [%s]", result)); System.out.println(String.format("a.equals(b), result is [%s]",a.equals(b))); if(!result){ System.out.println("action [String.intern]"); b = b.intern(); System.out.println(String.format("a=b, result is [%s]", a==b)); } } private static void init(Map<String, String> cases){ cases.put("a-> \"abc\", b-> \"abc\"", "abc"); cases.put("a-> \"abc\", b-> \"ab\" + \"c\"", "ab" + "c"); String c = "c"; cases.put("c-> \"c\", a-> \"abc\", b-> \"ab\" + c", "ab" + c); cases.put("a-> \"abc\", b-> new String(\"abc\")", new String("abc")); } }
相关推荐
String类的intern、split方法 String 类的 intern 方法是一个本地方法,定义...String 类的 intern 方法和 split 方法都是非常重要的方法,它们可以帮助我们更好地处理字符串操作,避免内存的浪费,提高程序的性能。
C#中的String.Intern方法是用于将字符串添加到字符串池中的方法。字符串池是一种机制,用于存储字符串的实例,以便重复使用同一个字符串,减少内存占用。在C#中,每个字符串实例都有一个唯一的地址,通过String....
关于String.intern()方法,这个问题都被问烂了,有的文章在分析的时候还在用jdk1.7,jdk1.8之后内存模型发生了变化,内存的变化也会影响intern方法的执行,这里有必要写文章分析一下,请大家务必从头开始看,这样...
* 提高性能:intern 方法可以提高应用程序的性能,因为它可以减少字符串对象的创建和销毁操作。 * 简化代码:intern 方法可以简化代码,因为它可以将字符串对象存储在字符串常量池中,以便重复使用相同的字符串对象...
7. **String与基本类型的转换** - `Integer.parseInt(String s)`/`Double.parseDouble(String s)`:将字符串转换为整型/浮点型数值。 - `String.valueOf(int i)`/`String.valueOf(double d)`:将整型/浮点型数值...
`: 这里先使用`new`创建了一个新的`String`对象,然后调用`intern()`方法将其放入常量池。 比较结果: - `if (a == e)`: 结果为真。因为`e`和`a`最终都指向了字符串常量池中的同一个`"hehe"`实例。 - `if (e == ...
但需要注意的是,运行时动态创建的字符串不会自动放入池中,除非使用`string.Intern()`方法。 3. **字符串连接**:在.NET中,频繁的字符串连接操作(如使用`+`或`StringBuilder`)会显著影响性能。尤其是使用`+`...
在Java编程语言中,`String`类是极其重要的,它提供了许多用于操作字符串的方法,其中之一便是`intern()`。深入理解`String#intern()`方法对于优化内存使用和理解Java的内存模型至关重要。`intern()`方法是一个非常...
如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。 在Java中,字符串常量池存在于方法区中。方法...
Java String的intern方法是Java中一个非常重要的方法,它可以将字符串常量池中的字符串对象返回给我们。今天,我们将深入探究Java String的intern用法解析,了解它的工作原理和应用场景。 Java String的intern方法 ...
Java中的`String`类的`intern()`方法是一个非常有趣且重要的功能,它涉及到字符串的内存管理,特别是字符串常量池。常量池是Java虚拟机(JVM)的一部分,存储预编译的字符串字面量和其他常量。`intern()`方法的作用...
Java String#intern() 内存模型是 Java 语言中一个重要的概念,.string#intern() 方法是 Java 字符串常量池中一个重要的组件。字符串常量池是一个固定大小的 HashMap,桶的数量默认是 1009,从 Java7u40 开始,该...
最后,需要破除一个错误的理解,即使用String.intern()方法不能将一个String对象保存到一个全局String表中。如果具有相同值的Unicode字符串已经在这个表中,那么String.intern()方法将返回该表中的字符串常量的引用...
**六、String与基本类型的转换** 1. `Integer.toString(int i)` 和 `Integer.parseInt(String s)`: 整型与字符串的转换。 2. `Double.parseDouble(String s)` 和 `Double.toString(double d)`: 浮点型与字符串的转换...
Java中的String类提供了丰富的字符串操作方法,这些方法覆盖了字符串的创建、修改、比较、查找以及格式化等多个方面,极大地方便了Java程序中对字符串的处理。 1. `charAt(int index)`方法用于返回字符串中指定索引...
之前我们提到了,String.intern方法会返回字符串常量池中的字符串对象的引用。 而G1垃圾回收器的字符串去重的功能其实和String.intern有点不一样,G1是让两个字符串的底层指向同一个byte[]数组。 有图为证: 上图中...
##### (2)String与其他结构间的转换 1. **基本数据类型、包装类 --> 字符串**: - 可以通过 `String.valueOf()` 方法或者 `+` 运算符来实现转换。 - 示例代码: ```java int num = 100; String s = String....