先看一下String的源码:
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;
......此处省略N多代码
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
}
在上面 贴出了String的源代码,我们可以看到,String类是final修饰的,然后还有个成员属性 value 它也是final的并且是一个char类型的数组
通过构造方法可以看到,我们传递的参数值 是直接赋值给了 value
那么我们说明 String它是一个不可变字符串,底层是一个char类型的数组
实际上我们经常 这样写 String a = "abc"; 这个时候 声明的变量值是在常量池中的。
到此,有的同学可能要说了,String里面我们不是可以使用 + 连接符 来进行String的拼接吗。
的确如此,我们是可以使用 "+" 来拼接字符串,不过 这里 jdk的虚拟机是做了优化的,并不是表面看到的使用 连接符 对原来的String做了拼接,那么我们来看下 它是如何编译的。
有如下代码:
public class Test{
public static void main(String [] args){
String str = "abc";
String str1= "def";
str = str+str1;
System.out.println(str);
}
}
我们在控制台 中 通过 javac 编译,然后通过java执行 看到结果是
结果都知道是 "abcdef" 那么这个地方的str到底是否被改变,我们可以看编译后的class字节码文件
我们通过 javap -c Test 来观察,结果如下图
我们看到Code 的 第 0 和 第 3 标识处,是我们声明的变量值 "abc" 和 "def"
再往下走,我们在java中的代码是使用了 "+" 连接符号的,此处看到 Code 第 6 处 new StringBuilder
再往下走,看到 Code 的第 14 和 第 18 ,使用了StringBuilder的 append(),那么我们可以得出一个结论,我们在使用String的时候,使用连接符,并不是表面看到的是直接操作原来的变量做值的拼接,而是使用的StringBuilder对象,追加的内容,
最后 在 Code的21处,有一个 toString()方法,是把当前StringBuilder的对象变成了String。
好了,到此,我们剖析String源码到此结束。
结论就是 String底层是一个不可变字符串,使用连接符的时候,实际上是经过了StringBuilder的优化处理的。并不是在原来的String对象中做追加