Java 中 String 的 equals 与 ==
来源:互联网 发布:西北师范大学行知学院 编辑:程序博客网 时间:2024/05/05 09:56
在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。
在 jdk6中,常量池是放在 Perm 区中的,Perm 区和正常的 JAVA Heap 区域是完全分开的。jdk7中,将String常量池 从 Perm 区移动到了 Java Heap区。
8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种:
String s1= "s1";String s2= "s"+"2";String s3= new String("s3");
直接使用双引号声明出来的String对象。编译期先去字符串常量池检查是否存在 “s1” ,如不存在则在常量池开辟一个内存空间存放 “s1”;如有则不用重新开辟空间。然后在栈中开辟一块空间,命名为“s1”,存放的值为常量池中“s1”的内存地址.
拼接静态字符时,用 + 时,编译器会对此优化,视为
String s2 = "s2";
(动态拼接时,尽量使用StringBuffer 或 StringBuilder 的 append )通过关键字 new 定义,
String s3= new String("s3");
这段代码,类似于String s = "s2" ; String s2 = new String(s);
首先"s3"
这里涉及到了第一种使用双引号声明String 对象,先看常量池是否存在…步骤省略。然后是在 堆内存中开辟然后在内存堆中开辟一块空间存放new出来的String实例,在栈中开辟一块空间,命名为“s2”,存放的值为堆中String实例的内存地址,这个过程就是将引用s2指向new出来的String实例.
不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
关于== 与 equals
String 重写的equals 方法:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
可以看到,equals 方法比较的是 value 值。== 比较的是在内存中的地址。
那么下面的程序:
public static void main(String[] args) { String str1 ="monday"; String str2 ="monday"; String str3 ="mon"+"day"; final String str0 = "mon"; String str = "mon"; String str4= str0+"day"; String str5= str+"day"; String str6 = new String("monday"); if(str1==str2){ System.out.println("str1==str2"); }else if(str1.equals(str2)){ System.out.println("str1 only equals str2"); } if(str1==str3){ System.out.println("str1==str3"); }else if(str1.equals(str3)){ System.out.println("str1 only equals str3"); } if(str1==str4){ System.out.println("str1==str4"); }else if(str1.equals(str4)){ System.out.println("str1 only equals str4"); } if(str1==str5){ System.out.println("str1==str5"); }else if(str1.equals(str5)){ System.out.println("str1 only equals str5"); } if(str1==str6){ System.out.println("str1==str6"); }else if(str1.equals(str6)){ System.out.println("str1 only equals str6"); } }
由于字符串常量池共享的特性,可以知道结果:
str1==str2str1==str3str1==str4str1 only equals str5str1 only equals str6
str1 ,str2 都是在常量池中,指向的是同一个对象,str3 编译期优化之后也相同。因此这个三个可以通过 == 来判断。str0 是final 修饰,编译期被解析为常量,因此 str4 与 str3 是相同的。str5 是由非静态字符拼接的,编译期无法确定.
看到这里可以知道对于字符串含有不确定量的字符拼接,其实是通过额外创建一个StringBuffer,通过append 方法来实现的,然后再通过 toString 方法转化为String。因此肯定不是指向的同一个地址。
String 中的 intern
使用String提供的intern方法时,intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。intern 作用是:“如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回”。
要注意的是,String的 String Pool 是一个固定大小的Hashtable,默认值大小长度是1009,如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。
在 jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。在jdk7中,StringTable的长度可以通过一个参数指定:-XX:StringTableSize=99991
String s = new String("1"); String s2 = "1"; System.out.println(s.intern() == s2); String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4);
上述代码片段中,第一个可以知道是true ,因为s.intern() 返回的是字符串常量池中的字符串。但是第二个是在不懂。。。。原文的解释是:
在第一段代码中,先看 s3和s4字符串。String s3 = new String(“1”) + new String(“1”);,这句代码中现在生成了2最终个对象,是字符串常量池中的“1” 和 JAVA Heap 中的 s3引用指向的对象。中间还有2个匿名的new String(“1”)我们不去讨论它们。此时s3引用对象内容是”11”,但此时常量池中是没有 “11”对象的。
接下来s3.intern();这一句代码,是将 s3中的“11”字符串放入 String 常量池中,因为此时常量池中不存在“11”字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个 “11” 的对象,关键点是 jdk7 中常量池不在 Perm 区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向 s3 引用的对象。 也就是说引用地址是相同的。
看不懂。。。。。
关于 intern 方法的使用:
在Java6底下大量使用intern()会导致应用性能的显著下降,还有可能产生OOM错误。但从Java7开始,字符串常量池被移到了Heap空间,Heap空间的大小只受制于机器的真实内存大小,因此,在Java7下使用String.intern()能更有效地减少重复String对象对内存的占用。
参考:
http://tech.meituan.com/in_depth_understanding_string_intern.html;
http://www.importnew.com/15397.html;
http://blog.csdn.net/gaopeng0071/article/details/11741027;
http://my.oschina.net/xiaohui249/blog/170013?p=2#comments
- Java 中 String 的 equals 与 ==
- Java中String的equals==与区别
- (java)String 中 ==与equals方法 的区别
- Java 中 String equals与==的区别
- Java中String判断相等equals与==的区别以及StringBuilder的equals
- Java中Object.equals与String.equals的区别
- String中==与equals区别验证!java中堆与栈的真谛
- java中==与 String中 equals 和对象中equals比较
- java中String对象的== 与 equals的说明
- java 中String类的内存和equals与“=="的比较
- java String对象中‘==’,equals与compareTo函数的使用和区别
- Java String 中 “==” 与 equals()方法的区别
- String中 ==与equals的区别
- java中的string equals 与 == 的比较
- java中的string equals 与 == 的比较
- java中的string equals 与 == 的比较
- java基础 equals与==区别,String的hashcode
- java中的string equals 与 == 的比较
- oracle 数据库修改数据类型,保留原来数据
- [LeetCode] Divide Two Integers
- android中两种广播注册方式
- 切换横竖屏时,Fragment被调用两次
- 设计模式-观察者模式(observer pattern)
- Java 中 String 的 equals 与 ==
- 如何快速搭建Cocos2d-JS游戏开发环境
- extern "C"的用法解析
- 使用PDF分割器将PDF拆分为多个文档
- 手机网站如何禁止访客放大、缩小网页
- js延时执行代码
- ProGuard
- CvArr、Mat、CvMat、IplImage、BYTE转换
- 当心免费公共wifi存安全隐患