Java中关于String类型的几个问题
如何比较两个字符串?用"="还是equals()?
简单来说,“==
”是用来检测俩引用是不是指向内存中的同一个对象,而equals()
方法则检测的是两个对象的值是否相等。只要你想检测俩字符串是不是相等的,你就必须得用equals()
方法。
如果你知道“字符串保留(string intern
)”的概念那就更好了:
String
的intern()
方法返回字符串对象的规范化表示形式。它遵循以下规则:对于任意两个字符串s
和t
,当且仅当s.equals(t)
为true
时,s.intern()==t.intern()
才为true
。为什么呢?
一个初始时为空的字符串池,它由类String私有地维护。当调用intern()
方法时,如果池已经包含一个等于此String
对象的字符串(该对象由equals()方法确定),则返回池中的字符串。否则,将此String
对象添加到池中,并且返回此String
对象的引用。
所有字面值字符串和字符串赋值常量表达式都是内部的。
String str1 = "abc"; String str2 = new String("abc"); //true System.out.println(str1 == str2); //false System.out.println(str1.equals(str2)); //true System.out.println(str1 == str2.intern()); //true
substring()方法具体是都干了些啥?
在JDK6中,这个方法只会在标识现有字符串的字符数组上 给一个窗口来表示结果字符串,但是不会创建一个新的字符串对象。如果需要创建个新字符串对象,可以这样在结果后面"+"一个空的字符串:
str.substring(m, n) + "";
这么写的话就会创建一个新的字符数组来表示结果字符串。同时,这么写也有一定的几率让你的代码跑的更快,因为垃圾回收器会吧没有在使用的大字符串回收而留下子字符串。
Oracle JDK7中的substring()
方法会创建一个新的字符数组,而不用之前存在的。下面举出详细的列子说明substring()
在JDK6和JDK7中的区别。
String str = "abcdef"; str = str.substring(1,3); System.out.println(str);
当substring()
被调用时,发生了什么?
你肯定知道,字符串str
是final
不可变的,所以当str被重新赋值为str.substring(1,3)
时,它应该指向一个全新的字符串,就像下面图片显示的那样:
但是这张图并不完全正确,它并没有说明内存堆中真正发生了什么。那么substring()
方法的到底是怎样工作的呢?在JDK6和JDK7中又有什么区别呢?
JDK6中的substring()
实际上String
类的原理是将字符串存储在一个字符数组中的,在JDK6中,类String
包括3个属性域:char value[]
, int offset
, int count
,分别用来存储实际的字符数组、数组的第一个索引、字符的个数。
当substring()
方法被调用时,一个新的字符串被创建,但是这个字符串的字符数组value
仍然指向了原来那个数组的内存堆!惟一不同的是两个字符串的offset
和count
属性值不同,如下图:
下面是简化后的String
类源码:
//JDK 6 String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; } public String substring(int beginIndex, int endIndex) { //check boundary return new String(offset + beginIndex, endIndex - beginIndex, value); }
可以很明显的看出,String
对象中的value
域并没有变化,还是指向之前的那个value
。
JDK6中的substring()方法引出的一个问题
如果有一个很长很长的字符串,你每次只需要截取其中很短的一小段,但是如果你用substring()
的话实际会得到很长很长的字符串的引用,所以当使用的时候必然导致性能降低!
JDK7中的substring()
这个问题在JDK7中被改进了,创建了一个新的字符数组:
String类在JDK7中的源码:
//JDK 7 public String(char value[], int offset, int count) { //check boundary this.value = Arrays.copyOfRange(value, offset, offset + count); } public String substring(int beginIndex, int endIndex) { //check boundary int subLen = endIndex - beginIndex; return new String(value, beginIndex, subLen); }
可以看到,在构造函数中,创建了一个全新的字符数组value
,并且String
类中不再使用offset
和count
做为属性了。
String & StringBuilder & StringBuffer的区别
String
vs StringBuilder
:StringBuilder
是可变的,这就意味你在创建对象之后还可以去修改它的值。StringBuilder
vs StringBuffer
:StringBuffer
是同步的,意味着它是线程安全的,但是就会比StringBuilder
慢些。
版权声明
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。 本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。