Java之字符串

Apr 26, 2021


不可变字符串String

String对象是不可变的。查看 JDK 文档你就会发现,String 类中每一个看起来会修改 String 值的方法,实际上都是重新创建了一个String对象,以包含修改后的字符串内容。

字符串拼接方法

  • “ + ”操作符

代码如下:

public class Str {
	public static void main(String[] args) {
		
		String s="NIHAO";
		s = s + "QWE";//此时会重新生成一个新的String对象
		System.out.println(s);
	}
}

将上述代码反编译后,我筛选了一下,如下:

如果你看不懂字节码指令没关系,可以看我的这篇文章字节码指令

  public static void main(java.lang.String[] arg0);
     0  ldc <String "NIHAO"> [2]
     2  astore_1
     3  new java.lang.StringBuilder [3]
     6  dup
     7  invokespecial java.lang.StringBuilder() [4]
    10  aload_1
    11  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [5]
    14  ldc <String "QWE"> [6]
    16  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [5]
    19  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [7]
    22  astore_1
    23  getstatic java.lang.System.out : java.io.PrintStream [8]
    26  aload_1
    27  invokevirtual java.io.PrintStream.println(java.lang.String) : void [9]
    30  return

从这个例子中,编译器创建了一个StringBuilder对象,用以构造最早的String,并以每个字符串调用一次StringBuilder的append()方法,总共2次,然后就会调用toString方法来返回一个新的String对象。

  • concat 方法

代码如下:

public class Str {
	public static void main(String[] args) {
		String s="NIHAO";
		s = s.concat("QWE");
		System.out.println(s);
	}
}

将上述代码反编译后,我筛选了一下,如下:

  public static void main(java.lang.String[] arg0);
     0  ldc <String "NIHAO"> [2]
     2  astore_1
     3  aload_1
     4  ldc <String "QWE"> [3]
     6  invokevirtual java.lang.String.concat(java.lang.String) : java.lang.String [4]
     9  astore_1
    10  getstatic java.lang.System.out : java.io.PrintStream [5]
    13  aload_1
    14  invokevirtual java.io.PrintStream.println(java.lang.String) : void [6]
    17  return

反编译后好像看不了什么东西出来,因为concat也是java.lang.String里面的方法,所以的话我觉得可以直接去IDE看它的如何实现的。如下:

    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

从concat的底部实现可以看到,其实它最终也是要new一个String对象的,所以前面为什么提到Sting的不可变性。顺便看看Arrays.copyOf的方法内部,如下:

    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
  • StringBuffer 方法

代码如下:

public class Str {
	public static void main(String[] args) {
		
		StringBuffer s=new StringBuffer("liuliu");
		s = s.append("QWE").append("dsad");
		System.out.println(s);
	}

}

StringBuffer底层实现原理如下:

    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

继续深入看看append方法内部如下:

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

总结一小下,StringBuffer类继承了StringBufferAbstractStringBuilder,然后调用StringBufferAbstractStringBuilder类里面的那个append方法进行拼接字符串。append会直接拷贝字符到内部的字符数组中,如果字符数组长度不够,会进行扩展。

  • StringBuilder 方法

代码如下:

public class Str {
	public static void main(String[] args) {
		
		StringBuilder s=new StringBuilder("liuliu");
		s = s.append("QWE").append("dsad");
		System.out.println(s);
	}

}

StringBuilder底层实现原理如下:

    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

StringBuilder和StringBuffer类似的,区别就是在于StringBuffer是线程安全的,StringBuilder是线程不安全的。会发现StringBuffer调用append方法时,会比StringBuilder多了一个 toStringCache 字段。字段上的解释是返回最后一次toString的缓存值,一旦StringBuffer被修改就清除这个缓存值。而且StingBuffer的append方法中多了一个 synchronized 进行声明。

  • StringUtils.join方法 这个建议看其他博主~

总结:效率比较 StringBuilder < StringBuffer < concat < + < StringUtils.join