之前经常看到一个问题就是说到底String&StringBuilder&StringBuffer 有什么区别.究其原因 要需要看他们的源代码才可以清楚的明白是为什么
String类详解
下面这是对String类的描述:
打开源代码的时候你会发现String类是被 final关键字修饰的
被 final修饰的变量会在class文件常量池中保存一个副本
public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
*
* Object Serialization Specification, Section 6.2, "Stream Elements"
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
...
1.可以注意到String类是final类,也就是说Stirng类不能被继承,而且所有的成员方法都默认为final方法. final是不允许被继承的
2.String是由一个char的数组来保存所谓的字符串的
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
3.在String类返回类型是String的方法 都不是在原有的字符串上进行的,而是new了一个新的字符串对象.也就是说之前的字符串并没有进行改变.
重要的事情 -->>>
' 对String对象任何改变都不能影响到原来的object,相关的所有String的操作都要生成新的对象'
-
String str = "abc";
String str = new String("abc");
上面两个语句有什么区别吗<有~>
要考虑到编译的问题,在class文件中有一部分来储存编译期间生成的字面常量以及符号引用
String str1 = "abc";
String str2= "abc";
String str3 = new String("abc");
String str4 = new String("abc");
System.out.println(str1 == str2 );
System.out.println(str1 == str3 );
System.out.println(str3 == str4 );
//Console结果
true
false
false
String str1 = "abc";
--->>> 但是同一个字符串只会定义一次,运行期间字面常量都会被储存到常量池里面.
在定义字面常量的时候 JVM会优先在常量池里查找是否有相同的字面常量,如果有就直接引用到已经存在的字面常量.如果没有找到就将在常量池里面开辟一个空间来储存这个新的字面常量,并将引用指向该字面常量.
String str3 = new String("abc");
--->>> 通过new关键字生成对象是在堆区(heap)进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的.因此通过new来创建出的一定是不同的对象,不管内容是否相同.
String StringBuilder StringBuffer区别?
既然String这么强大为什么还会要用StringBuilder或者StringBuffer呢?
String类不方便进行多次的数组操作 所以在进行多次的数据操作的时候就需要用到StringBuilder或者StringBuffer类中的方法进行操作.
接下来看一段代码
public static void main(String args[]) {
String string = "";
for (int i = 0; i < 100 ; i++) {
string += "hello";
}
}
在这里面累加的过程就是将原有的String变量指向的对象内容去除与"hello"进行字符串累加操作再存进另一个新的String对象中,再让string变量指向新生成的对象
在编译的字节码中其实每次循环的时候new了一个StringBuilder对象,再进行append()操作,最后再通过toString()返回String对象,也就是说在整个循环的过程中new了100个新的对象,其实并没有必要 这给JVM的GC带来了很多不必要的麻烦造成资源浪费.
事实上单单这一行代码string += "hello";
已经被JVM自动优化为了
StringBuilder str = new StringBuilder();
str.append("hello");
str.toString();
但是同样的操作如果你使用 StringBuilder类
public static void main(String args[]) {
String str= new StringBuilder();
for (int i = 0; i < 100 ; i++) {
str.append( "hello)";
}
}
程序只进行了一次new操作,只生成了一个对象,append方法只是在现有的对象上操作并不会产生新的对象.所以在会进行多次操作的时候StringBuilder会比String性能好的多
那么问题来了StringBuilder StringBuffer有什么区别呢?
其实区别很小 只是StringBuffer类在成员方法的前面多了一个synchronized
为了在多线程操作的时候起到保护安全的
//StringBuilder
@Override
public StringBuilder reverse() {
super.reverse();
return this;
}
//StringBuffer
@Override
public synchronized StringBuffer reverse() {
toStringCache = null;
super.reverse();
return this;
}