在我们编程的过程中,发现字符串做操是很常见的行为,最近在研读《Java编程思想》,结合一下自己所学总结一下Java中的字符串。
让我们看一下Java中最常见的几个与字符串相关的类的UML图:
其中String是不可变的(only read),所谓是不可变是因为每一次改变的操作都回新new一个对象
看java源码:
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);//如果beginIndex == 0就返回自己,不然就new一个新对象
}
</pre><pre name="code" class="java">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);//new一个新对象
}
而StringBuilder和StringBuffer是可变的字符串,前者是线程不安全,后者是线程安全的,根据自己的需求进行选择
也看一下java源码:
public AbstractStringBuilder deleteCharAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
System.arraycopy(value, index+1, value, index, count-index-1);
count--;
return this;//返回的是自己
}
有一点我这里需要特别说明的是String中的运算符重载
"+"和"+=",java中仅有的重载的运算符,java本身不支持运算符重载。
例如:String str = "a" + "b" + "c" +10;
问这里面产生了多少字符串呢?
我记不清在哪里看的了,说每次+就会产生新的String对象,昨天看《Java编程思想》时,才知道自己错,JVM知道String字符串连接效率低下,对它做了优化!
请看代码!
public class Main {
public static void main(String[] args) {
String str = "a";
String string = "b" + str + "c" + 47;
System.out.println(string);
}
}
经过 javac Main.java
javap -c Main之后
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String a
2: astore_1
3: <span style="font-size:24px;color:#ff6600;">new #3 // class java/lang/StringBuilder</span>
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String b
12: invokevirtual #6 // Method java/lang/StringBuilder.<span style="font-size:24px;color:#330099;">append</span>:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.<span style="font-size:24px;color:#330099;">append</span>:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String c
21: invokevirtual #6 // Method java/lang/StringBuilder.<span style="font-size:24px;color:#330099;">append</span>:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: bipush 47
26: invokevirtual #8 // Method java/lang/StringBuilder.<span style="font-size:24px;color:#330099;">append</span>:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_2
33: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_2
37: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: return
}
我们可以看到是new了一个StringBuilder来优化String的"+"操作,并调用append来连接字符串
要注意:
1.一次String的"+"的操作和StringBuilder操作在JVM是一样的,但此在循环中String的连接时创建多个StringBuilder,而StringBuilder的append只创建一次.
2.不要这样使用StringBuilderd的append()方法:sb.append(str1 + "-"+ str2);会令创建对象
String,StringBuilder和StringBuffer上的操作,帮助文档和源码中写的很清楚,可以自行查阅!
字符串中的操作有一个容易被我们忽略的地方:toString()
无意识的递归:
请看下面代码:
<span style="white-space:pre"> </span>public class Main {
public static void main(String[] args) {
Main main = new Main();
System.out.println(main);
}
public String toString() {
return "Main address: " + this + "\n";// 这里的this与字符串连接会先调用toString(),就造成了递归,然后就StackOverFlow
// super.toString() 修改方法
}
}
字符串还有格式化输出,这部分只做blog的完成性补充!
System.out.printlf();
System.out.format();
以及专门为格式化封装的Fomatter类!