String 类其实是通过 char 数组来保存字符串的。
String 对象是由final 修饰的,一旦使用 final 修饰的类不能被继承、方法不能被重写、属性不能被修改。而且 String 不只只有类是 final 的,它其中的方法也是由 final 修饰的,换句话说,Sring 类就是一个典型的 Immutable 类。
下面分别存在(创建)了几个对象?
String s1 = "aaa"; // 1个
String s2 = "bbb" + "ccc"; // 1个 (编译器做了优化)
String s3 = s1 + "bbb"; // 3个 (创建一个 StringBuilder 对象然后执行初始化。执行 + 号相当于是执行 new StringBuilder.append() 操作,并自动执行 toString() 方法。)
String s4 = new String("aaa"); // 2个
String 对象底层是使用了 StringBuilder 对象的 append 方法进行字符串拼接的
StringBuffer 对象 代表一个可变的字符串序列,当一个 StringBuffer 被创建以后,通过 StringBuffer 的一系列方法可以实现字符串的拼接、截取等操作。一旦通过 StringBuffer 生成了最终想要的字符串后,就可以调用其 toString 方法来生成一个新的字符串。
StringBuffer 是线程安全的,在字符串拼接上面直接使用 synchronized 关键字加锁,从而保证了线程安全性。 StringBuilder 其实是和 StringBuffer 几乎一样,只不过 StringBuilder 是非线程安全的。
String 在 JDK1.6 之后提供了 intern() 方法,intern 方法是一个 native 方法,它底层由 C/C++ 实现,intern 方法的目的就是为了把字符串缓存起来,在 JDK1.6 中却不推荐使用 intern 方法,因为 JDK1.6 把方法区放到了永久代(Java 堆的一部分),永久代的空间是有限的,除了 Fullgc 外,其他收集并不会释放永久代的存储空间。JDK1.7 将字符串常量池移到了堆内存 中。
在 JDK 1.6 及之前的版本中,常量池是分配在方法区中永久代(Parmanent Generation)内的,而永久代和 Java 堆是两个完全分开的区域。 从JDK 1.7开始去永久代,字符串常量池已经被转移至 Java 堆中,开发人员也对 intern 方法做了一些修改。因为字符串常量池和 new 的对象都存于 Java 堆中,为了优化性能和减少内存开销,当调用 intern 方法时,如果常量池中已经存在该字符串,则返回池中字符串;否则直接存储堆中的引用,也就是字符串常量池中存储的是指向堆里的对象。
public static void main(String[] args) {
String a = new String("ab");
String b = new String("ab");
String c = "ab";
String d = "a";
String e = new String("b");
String f = d + e;
System.out.println(a.intern() == b); // false
System.out.println(a.intern() == b.intern()); // true
System.out.println(a.intern() == c); // true
System.out.println(a.intern() == f); // false
}
- StringBuilder 的初始容量是 16,当然也可以指定 StringBuilder 的初始容量。扩容后的字符串长度会是原字符串长度增加一倍 + 2,如果扩容后的长度还比拼接后的字符串长度小的话,那就直接扩容到它需要的长度 newCapacity = minCapacity,然后再进行数组的拷贝。