String的“==”比较
基本
==
- 基本类型,值相等,即返回true
- 引用类型,对象的地址相等,即返回true
equals
- 比较对象的内容(说法,并不是十分准确,但易理解)相同,即返回true
例子:
1 | String a = new String("abc"); |
Object中默认的equals()方法也是通过比较两个对象的地址,来判断两个对象是否相等的,所以默认的equals()方法没有什么意义,我们需要重写自定义类的equals()方法。
String类重写了equals()方法,使得String对象的equals()方法,是按照String的内容进行比较的。
深入
例子一:
1 | String a = new String("ab");//运行时确定,常量池中存在了“ab”,堆中存在了对象a |
“ab”直接量和new String(“ab”)的区别:
当Java程序直接使用形如“ab”的字符串直接量的时候(包括可以在编译时期就计算出来的字符串值)时,JVM会使用常量池来管理这些字符串;当使用new String(“ab”)时候,JVM会先使用常量池来管理“ab”直接量,再调用String类的构造器来创建一个新的String对象,新创建的String对象被保存在堆中。换句话说,new String(“ab”)一共产生了两个对象。
String.intern()方法是一个Native方法,它的作用是:
如果字符串常量池中已经存在了一个等于此String对象的字符串,则返回常量池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
注意,在不同的JDK版本上有区别,在JDK 1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用,在JDK 1.7中intern()的实现,不会再复制实例,只是在常量池中记录首次出现的实例的引用,并且返回这个引用。
JVM常量池保证了相同的字符串直接量只有一个,不会产生多个副本。a,b所引用的字符串在编译时候就已经确定下来了,因此他们都将引用常量池中的同一个对象。
使用new String()创建的字符串是运行时创建出来的,他被保存在运行时数据,(即堆内存上),不会被放入运行时常量池。
例子二:
1 | pulic static void main(String[] args){ |
上面这个例子,如果运行在JDK 1.6上,会出现运行时常量池溢出的异常,因为intern()方法会一直把新出现的字符串实例所包含的字符串添加到永久代中。而在JDK 1.7中这段代码,会一直运行下去,因为在JDK 1.7中,不会再复制实力例的内容,而只是在常量池中记录首次出现的实例的引用。
例子三:
1 | String abc = new StringBuilder().append("a").append("b").append("c").toString(); |
在JDK 1.6中,返回两个false,而在JDK1.7中返回一个true,一个false。
因为在JDK 1.6中abc.intern()会将abc对象的内容“abc”复制到永久代,然后返回这个永久代中字符串“abc”的引用,所以false,abdb.intern()之前,“abc”已经存在于字符串常量池中了,所以intern()返回的是常量池中“abc”的地址,所以false。
而在JDK 1.7中,abc.intern()只是在常量池中记录了这个abc这个对象的地址,并且返回了这个地址,所以true,而abdb.intern()之前,“abd”已经存在于字符串常量池中了,所以intern()返回的是常量池中“abd”的地址,所以false。
例子二和例子三,体现了不同的字符串常量池的实现。