最近我有了新的理解
- String
public final class String
implements java.io.Serializable, Comparable<String>, 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
String 类中使用 final 关键字修饰字符数组来保存字符串,因此String对象不可变,这句话不正确
我们知道被 final 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final 关键字修饰的数组保存字符串并不是 String 不可变的根本原因,因为这个数组保存的字符串是可变的。
如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改。
String 真正不可变有下面几点原因:
- 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法.
- String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变.
- StringBuilder/StringBuffer
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
两者继承父类AbstractStringBuilder ,底层也是字符数组,当时没有private和final修饰,而且提供很多修改字符数组的方法
- 线程安全性:String不可变,常量,安全,StringBuilder/StringBuffer都继承AbstractStringBuilder ,存在很多公共方法.但是StringBuffer 有同步锁,线程安全.反之,StringBuilder没有同步锁,不安全.
- 性能:每次改变String的时候,都会新建String对象,浪费内存,降低性能.StringBuffer 有锁机制,原地改变,还是会浪费性能.StringBuilder不存在锁机制,原地改变,不考虑安全前提,性能好.
总结:大数据,多线程,安全->StringBuffer ;大数据,单线程->StringBuilder;小数据,安全->String
String str1 = "he";
String str2 = "llo";
String str3 = "world";
String str4 = str1 + str2 + str3;
// class version 52.0 (52)
// access flags 0x21
public class practice/begin/Code25_Demo {
// compiled from: Code25_Demo.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 8 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lpractice/begin/Code25_Demo; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 10 L0
LDC "he"
ASTORE 1
L1
LINENUMBER 11 L1
LDC "llo"
ASTORE 2
L2
LINENUMBER 12 L2
LDC "world"
ASTORE 3
L3
LINENUMBER 13 L3
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 3
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 4
L4
LINENUMBER 15 L4
RETURN
L5
LOCALVARIABLE args [Ljava/lang/String; L0 L5 0
LOCALVARIABLE str1 Ljava/lang/String; L1 L5 1
LOCALVARIABLE str2 Ljava/lang/String; L2 L5 2
LOCALVARIABLE str3 Ljava/lang/String; L3 L5 3
LOCALVARIABLE str4 Ljava/lang/String; L4 L5 4
MAXSTACK = 2
MAXLOCALS = 5
}
对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。
String[] arr = {"he", "llo", "world"};
String s = "";
for (int i = 0; i < arr.length; i++) {
s += arr[i];
}
System.out.println(s);
// class version 52.0 (52)
// access flags 0x21
public class practice/begin/Code25_Demo {
// compiled from: Code25_Demo.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 8 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lpractice/begin/Code25_Demo; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 14 L0
ICONST_3
ANEWARRAY java/lang/String
DUP
ICONST_0
LDC "he"
AASTORE
DUP
ICONST_1
LDC "llo"
AASTORE
DUP
ICONST_2
LDC "world"
AASTORE
ASTORE 1
L1
LINENUMBER 15 L1
LDC ""
ASTORE 2
L2
LINENUMBER 16 L2
ICONST_0
ISTORE 3
L3
FRAME APPEND [[Ljava/lang/String; java/lang/String I]
ILOAD 3
ALOAD 1
ARRAYLENGTH
IF_ICMPGE L4
L5
LINENUMBER 17 L5
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
ILOAD 3
AALOAD
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 2
L6
LINENUMBER 16 L6
IINC 3 1
GOTO L3
L4
LINENUMBER 19 L4
FRAME CHOP 1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 2
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L7
LINENUMBER 22 L7
RETURN
L8
LOCALVARIABLE i I L3 L4 3
LOCALVARIABLE args [Ljava/lang/String; L0 L8 0
LOCALVARIABLE arr [Ljava/lang/String; L1 L8 1
LOCALVARIABLE s Ljava/lang/String; L2 L8 2
MAXSTACK = 4
MAXLOCALS = 4
}
编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象。浪费空间.因此,在循环中进行字符串拼接,直接使用StringBuilder比较好.