虽然在项目开发中,需要经常用上String处理字符串,但一直对它的理解还停留在如何使用的阶段,所以趁着这段时间复习一下,也写下博文希望把知识记得更深刻.
如有不正之处,请多多谅解和指正,谢谢.
创建String对象基本有两种方法
我们看以下例子
public static void main(String[] args) {
String s1 = "Hello World"; //第一种创建方法
String s2 = "Hello World";
String s3 = new String("Hello World"); //第二种创建方法
//String中的 == 是比较他们是否指向同一个内存空间
System.out.println(s1 == s2);
//运行结果为ture
System.out.println(s1 == s3);
//运行结果为false
}
从这里看出s1和s2是分配到同一个内存空间里,而s3是另外一个内存空间,这两种方法虽然得出值都是Hello World但是他们的处理是大大不同的
如上图所示
其实当s1即将要被赋值的时候Jvm会看内存里是否存在Hello World这个值的内存空间,如果没有就直接创建该值的内存空间,将s1指向到这个内存空间当中
然后s2要被赋值时Jvm已经查到存储Hello World这个值的内存空间,所以也就直接将s2指向到这个内存空间当中
s3比较特别因为是它是通过new出来的一个实例,Jvm不会管内存里面是否已经存在Hello World这个内存空间,而是重新开辟一个空间让s3指向过去
所以最终得出这个结果.
当学习一个类最好的方法就是查看该类源码
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
private int hash; // Default to 0
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
public String() {
this.value = new char[0];
}
......
}
从中我们可以看到String不管是类还是属性都被定义成final,所以每当我们对String对象进行从新赋值时,实际上就是改变内存空间的指向,而不是在原内存空间进行值的改变
很简单的道理,之前例子s1 和 s2 他们指向也是指向同一个内存空间,我改变了s2的值,因为s2指向的内存空间发生了变化,s1也会随这个变化而变化,最终导致我只想该s2结果也把s1的值也一起改了
这就是把String定义为final的原因
因为String是不可变类,无法对字符串进行直接的修改
当做大量的字符串处理时通过String会消耗很多资源,一般我们做大量字符处理时,都会用上StringBuffer,StringBuilder这两个类
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
......
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
}
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
......
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
从源码中我们可以看到,不管在继承的类还是实现的接口都是一样的,唯一的不同是StringBuffer的append方法是带有synchronized,也就是说StringBuffer是线程安全的,而StringBuilder是线程非安全,因为String定义是final的关系,所以String天生就是线程安全的.
我们看下他们的执行效率
package com.rwj.test;
public class StringDemo {
public static void main(String[] args) {
string();
stringBuffer();
stringBuilder();
}
private static void string(){
long startTime = System.currentTimeMillis();
String s = new String();
for(int i = 0; i <= 100000; i++){
s += i;
}
long endTime = System.currentTimeMillis();
System.out.println("String: " + (endTime - startTime) + "毫秒.");
}
private static void stringBuffer(){
long startTime = System.currentTimeMillis();
//线程安全
StringBuffer s = new StringBuffer();
for(int i = 0; i <= 100000; i++){
s.append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuffer: " + (endTime - startTime) + "毫秒.");
}
private static void stringBuilder(){
long startTime = System.currentTimeMillis();
//非线程安全
StringBuilder s = new StringBuilder();
for(int i = 0; i <= 100000; i++){
s.append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuilder: " + (endTime - startTime) + "毫秒.");
}
}
运行结果为
所以需要做到大量字符时