1. String
1.1. 是什么
不可变、线程安全的字符串
1.2. 使用
| public class StringTest |
| { |
| public static void main(String[] args) |
| { |
| String val = new String("test1"); |
| String val1 = new String("test1"); |
| System.out.println(val == val1); |
| |
| String val2 = "test2"; |
| String val3 = "test2"; |
| System.out.println(val2 == val3); |
| |
| String val4 = "te" + "st2"; |
| System.out.println(val2 == val4); |
| |
| String val5 = String.valueOf("test3"); |
| String val6 = String.valueOf("test3"); |
| System.out.println(val5 == val6); |
| |
| |
| String aa = new String("1111"); |
| String bb = new String("1111"); |
| String val9 = String.valueOf(aa); |
| String val10 = String.valueOf(bb); |
| System.out.println(val9 == val10); |
| |
| String val7 = new String("test4"); |
| String val8 = "test4"; |
| String val7Intern = val7.intern(); |
| System.out.println(val8 == val7); |
| System.out.println(val8 == val7Intern); |
| System.out.println(val8 == val7); |
| } |
| } |
1.3. 源码分析
1.3.1. 类的定义
| |
| public final class String |
| |
| implements java.io.Serializable, Comparable<String>, CharSequence |
| { |
| |
| |
| |
| private final char value[]; |
| |
| |
| private int hash; |
| } |
String是不可变的
- 类使用final修饰
- 内部属性char value[]使用final修饰,说明引用不能改变
- 且内部没有对外提供修改内部属性char value[]的方法
1.3.2. 构造方法
| |
| public String() { |
| |
| this.value = "".value; |
| } |
| |
| |
| public String(String original) { |
| |
| this.value = original.value; |
| this.hash = original.hash; |
| } |
| |
| |
| public String(char value[]) { |
| |
| this.value = Arrays.copyOf(value, value.length); |
| } |
| |
| |
| public String(StringBuffer buffer) { |
| |
| synchronized(buffer) { |
| this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); |
| } |
| } |
| |
| public String(StringBuilder builder) { |
| |
| this.value = Arrays.copyOf(builder.getValue(), builder.length()); |
| } |
| |
| |
| public String(char value[], int offset, int count) { |
| if (offset < 0) { |
| throw new StringIndexOutOfBoundsException(offset); |
| } |
| if (count <= 0) { |
| if (count < 0) { |
| throw new StringIndexOutOfBoundsException(count); |
| } |
| if (offset <= value.length) { |
| this.value = "".value; |
| return; |
| } |
| } |
| |
| if (offset > value.length count) { |
| throw new StringIndexOutOfBoundsException(offset + count); |
| } |
| |
| |
| this.value = Arrays.copyOfRange(value, offset, offset+count); |
| } |
1.3.2.1. 解释new String("test1") != new String("test1")
| String val = new String("test1"); |
| String val1 = new String("test1"); |
| System.out.println(val == val1); |
我们查看字节码,结果如下:

调用的字节码时NEW,会在堆中创建字符串,所以两者不同
1.3.3. 常量池
英文名叫constant pool,指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量
1.3.3.1. 解释"test2"=="test2"
| String val2 = "test2"; |
| String val3 = "test2"; |
| System.out.println(val2 == val3); |
| |
| String val4 = "te" + "st2"; |
| System.out.println(val2 == val4); |
我们查看字节码,结果如下:

可以看出上面三行都调用了LDC
字节码,他表示在常量池中加载字符串,而"test2"
这个字符串在编译器会存入.class文件中,因此三者相等
1.3.4. equals方法
| public boolean equals(Object anObject) { |
| |
| if (this == anObject) { |
| return true; |
| } |
| |
| if (anObject instanceof String) { |
| String anotherString = (String)anObject; |
| int n = value.length; |
| |
| if (n == anotherString.value.length) { |
| char v1[] = value; |
| char v2[] = anotherString.value; |
| int i = 0; |
| |
| while (n != 0) { |
| if (v1[i] != v2[i]) |
| return false; |
| i++; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
1.3.5. toCharArray方法
| public char[] toCharArray() { |
| |
| |
| char result[] = new char[value.length]; |
| |
| System.arraycopy(value, 0, result, 0, value.length); |
| return result; |
| } |
| |
1.3.6. toString
| public String toString() { |
| |
| return this; |
| } |
1.3.7. valueOf
| public static String valueOf(Object obj) { |
| |
| return (obj == null) ? "null" : obj.toString(); |
| } |
1.3.7.1. 解释String.valueOf("test3") == String.valueOf("test3")
| String val5 = String.valueOf("test3"); |
| String val6 = String.valueOf("test3"); |
| System.out.println(val5 == val6); |

对于String val5 = String.valueOf("test3")
这种代码,编译器首先会把他当作String val5 = "test3"
处理,把"test3"
放入常量池中,然后调用String.valueOf
方法返回常量池中的"test3"
字符串,所以两者相等。
| String aa = new String("1111"); |
| String bb = new String("1111"); |
| String val9 = String.valueOf(aa); |
| String val10 = String.valueOf(bb); |
| System.out.println(val9 == val10); |

String aa = new String("1111")
这种先在堆中创建字符串"1111"
,然后String val9 = String.valueOf(aa)
返回的是堆中的字符串,所以两者不等
1.3.8. intern方法
| |
| |
| |
| public native String intern(); |
| |
1.3.8.1. 解释new String("test4").intern() == "test4"
| String val7 = new String("test4"); |
| String val8 = "test4"; |
| String val7Intern = val7.intern(); |
| System.out.println(val8 == val7); |
| System.out.println(val8 == val7Intern); |
| System.out.println(val8 == val7); |

String val7 = new String("test4")
是堆中的字符串"test4"
,String val8 = "test4"
是常量池中的"test4"
,String val7Intern = val7.intern()
intern首先检查常量池中是否有"test4"
,发现有直接返回
1.3.9. subString
| String substring(int beginIndex, int endIndex) { |
| |
| if (beginIndex < 0) { |
| throw new StringIndexOutOfBoundsException(beginIndex); |
| } |
| if (endIndex > value.length) { |
| throw new StringIndexOutOfBoundsException(endIndex); |
| } |
| int subLen = endIndex beginIndex; |
| if (subLen < 0) { |
| throw new StringIndexOutOfBoundsException(subLen); |
| } |
| |
| return ((beginIndex == 0) && (endIndex == value.length)) ? this |
| : new String(value, beginIndex, subLen); |
| } |
1.4. 常见问题
1.4.1. toString和valueOf的区别
| String aa = null; |
| |
| System.out.println(String.valueOf(aa)); |
前者没有做为空判断,后者做了。
1.4.2. String的不可变性
String这个类是由final修饰的,意味着不能被继承
String内部通过char数组实现,而这个数组是用final修饰的。意味着一旦赋值就不能改变引用,而且String也没有提供修改字符数组内容的方法
用下面的例子解释:
| String a = "aaa"; |
| a = "bbb"; |
| |
| |
| String c= a.subString(1,2); |

1.4.3. 线程安全
因为不可变所以线程安全
| public class TestString |
| { |
| public static void main(String[] args) throws InterruptedException |
| { |
| String string = "0"; |
| TestThread testThread = new TestThread(string); |
| testThread.start(); |
| testThread.join(); |
| |
| System.out.println(string); |
| } |
| } |
| |
| class TestThread extends Thread |
| { |
| private String string; |
| public TestThread(String string) |
| { |
| this.string = string; |
| } |
| |
| @Override |
| public void run() |
| { |
| this.string += "test"; |
| System.out.println(Thread.currentThread().getName() + ":" + this.string); |
| } |
| } |
1.4.4. String对+的重载
实际上使用的StringBuilder,并且调用append方法,最后调用toString方法
-
普通+

-
循环+

1.4.5. replaceFirst、replaceAll、replace区别
String replaceFirst(String regex, String replacement)
基于正则的替换,替换第一个String replaceAll(String regex, String replacement)
基于正则的替换,替换全部String replace(Char Sequencetarget, Char Sequencereplacement)
普通的比较替换,替换全部
1.4.6. String s = new String("abc")创建了几个字符串对象
- 当加载类时,"abc"被创建并驻留在了字符创常量池中(如果先前加载中没有创建驻留过)。
- 当执行此句时,因为"abc"对应的String实例已经存在于字符串常量池中,所以JVM会将此实例复制到会在堆(heap)中并返回引用地址
2. StringBuilder
2.1. 是什么
线程安全的、可变字符串
其实就是在StringBuilder的基础上加了synchronized关键字
2.2. 如何使用
| public class TestStringBuilder |
| { |
| public static void main(String[] args) throws InterruptedException |
| { |
| StringBuffer stringBuffer = new StringBuffer(); |
| |
| Thread thread1 = new Thread(() -> { |
| for (int i = 0; i < 5000; i++) |
| { |
| stringBuffer.append("aaaa"); |
| } |
| }); |
| Thread thread2 = new Thread(() -> { |
| for (int i = 0; i < 5000; i++) |
| { |
| stringBuffer.append("aaaa"); |
| } |
| }); |
| |
| thread1.start(); |
| thread2.start(); |
| |
| thread1.join(); |
| thread2.join(); |
| |
| |
| System.out.println(stringBuffer.toString()); |
| System.out.println(stringBuffer.length() == 5000 * 2 * 4); |
| |
| } |
| } |
| |
2.3. 原理分析
2.3.1. 构造函数
| public final class StringBuffer |
| extends AbstractStringBuilder |
| implements java.io.Serializable, CharSequence |
| { |
| public StringBuffer() { |
| |
| super(16); |
| } |
| |
| } |
| |
| abstract class AbstractStringBuilder implements Appendable, CharSequence { |
| |
| char[] value; |
| int count; |
| |
| AbstractStringBuilder(int capacity) { |
| value = new char[capacity]; |
| } |
| } |
2.3.2. append方法
| |
| public synchronized StringBuffer append(String str) { |
| toStringCache = null; |
| super.append(str); |
| return this; |
| } |
| |
2.3.3. toString
| |
| public synchronized String toString() { |
| if (toStringCache == null) { |
| toStringCache = Arrays.copyOfRange(value, 0, count); |
| } |
| return new String(toStringCache, true); |
| } |
2.3.4. subString
| public synchronized String substring(int start, int end) { |
| return super.substring(start, end); |
| } |
3. StringBuffer
3.1. 是什么
线程安全的、可变字符串
其实就是在StringBuilder的基础上加了synchronized关键字
3.2. 如何使用
| public class TestStringBuilder |
| { |
| public static void main(String[] args) throws InterruptedException |
| { |
| StringBuffer stringBuffer = new StringBuffer(); |
| |
| Thread thread1 = new Thread(() -> { |
| for (int i = 0; i < 5000; i++) |
| { |
| stringBuffer.append("aaaa"); |
| } |
| }); |
| Thread thread2 = new Thread(() -> { |
| for (int i = 0; i < 5000; i++) |
| { |
| stringBuffer.append("aaaa"); |
| } |
| }); |
| |
| thread1.start(); |
| thread2.start(); |
| |
| thread1.join(); |
| thread2.join(); |
| |
| |
| System.out.println(stringBuffer.toString()); |
| System.out.println(stringBuffer.length() == 5000 * 2 * 4); |
| |
| } |
| } |
| |
3.3. 原理分析
3.3.1. 构造函数
| public final class StringBuffer |
| extends AbstractStringBuilder |
| implements java.io.Serializable, CharSequence |
| { |
| public StringBuffer() { |
| |
| super(16); |
| } |
| |
| } |
| |
| abstract class AbstractStringBuilder implements Appendable, CharSequence { |
| |
| char[] value; |
| int count; |
| |
| AbstractStringBuilder(int capacity) { |
| value = new char[capacity]; |
| } |
| } |
3.3.2. append方法
| |
| public synchronized StringBuffer append(String str) { |
| toStringCache = null; |
| super.append(str); |
| return this; |
| } |
| |
3.3.3. toString
| |
| public synchronized String toString() { |
| if (toStringCache == null) { |
| toStringCache = Arrays.copyOfRange(value, 0, count); |
| } |
| return new String(toStringCache, true); |
| } |
3.3.4. subString
| public synchronized String substring(int start, int end) { |
| return super.substring(start, end); |
| } |
4. StringBuilder vs StringBuffer vs String
| String | StringBuffer | StringBuilder |
---|
是否线程安全 | √ | √ | × |
是否可变 | × | √ | √ |
5. 参考链接
原创作者: ThinkerQAQ 转载于: https://www.cnblogs.com/ThinkerQAQ/p/18953468