String补充

String几乎是平时最常用到的对象, 这里记录一下关于它的一些内容



关于不可变

  • String到底是如何不可变的?

源码中String对象用于存储字符的属性是这样的private final char value[], 它之所以不可变, 首先是因为value变量是private修饰的并且没有提供get,set方法, 因此外部无法修改; 其次value是被final修饰的, 表示它是一个常量, 无法修改

  • String真的不可变么?

String的值是存在value数组里的, 虽然value无法改变, 但是数组里的内容我们可以修改, 比如下面, 就把字符串Hello World变成了Hello_World

    public void testString() throws Exception {
        String str = "Hello World";
        Field field = String.class.getDeclaredField("value");
        field.setAccessible(true);
        char[] value = (char[]) field.get(str);
        System.out.println(str);
        value[5] = '_';
        System.out.println(str);
    }

关于编译优化

  • 编译时能够确定的值(包括常量变量)会进行优化
        String a = "a";
        final String fa = "a";
        String s1 = "ab";
        String s2 = "a" + "b";
        String s3 = new String("ab");
        String s4 = a + "b";
        String s5 = fa + "b";
        System.out.println(s1 == s2);  // true
        System.out.println(s1 == s3);  // false
        System.out.println(a == fa);   // true
        System.out.println(s1 == s4);  // false
        System.out.println(s1 == s5);  // true
  • 循环字符串拼接,要尽量在外层定义StringBuilder
        // 好的写法
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= 100; i++) {
            sb.append(i);
        }
        String str = sb.toString();
        // 坏的写法
        String str = "a";
        for (int i = 1; i <= 100; i++) {
            str = str + i;
        }

关于线程安全

String不可变, 一般不会有线程安全问题, 现实场景中经常对字符串进行一些操作, 一般是通过StringBuilder来进行的, 它是非线程安全的; 对应的线程安全版本为StringBuffer, 它相当于把StringBuilder的所有方法都加了锁(包括toString方法).