String累加问题

前言

本文说明

  1. 本文是基于java 8进行分析测试
  2. 本文会用到一些上篇博客的知识
    String为什么要用equals而不用==?

“+”连字符

“+”连字符原理

一个栗子

public static void main(String[] args) {
        String s="11";
        String s1="12";
        System.out.println(s+s1);
    }

我们反编译一下这段代码,看看JVM在做什么

Compiled from "StringAppendTest.java"
public class StringAppendTest {
  public StringAppendTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String 11
       2: astore_1
       3: ldc           #3                  // String 12
       5: astore_2
       6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: new           #5                  // class java/lang/StringBuilder
      12: dup
      13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
      16: aload_1
      17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      20: aload_2
      21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      27: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      30: return
}

上面涉及到了JVM指令集,感兴趣的同学可以自己去学一下,可以加深对JVM的理解(好吧,其实我也不是很熟悉~是时候把JVM提上议程了)
对上面指令的总结一下就是如下:

System.out.println(new StringBuilder().append(s).append(s1).toString());

是的,“+”的底层原理就是调用了StringBuilder的append方法!(不要急,StringBuilder待会说)
那是不是所有的“+”连字符都会new一个StringBuilder呢?
既然这样问了,很明显答案是否定,也有特栗!少侠接着看
一个栗子

public static void main(String[] args) {
		String s="11"+"12";
        System.out.println(s);
    }

对上面的代码,可以提出两个值得思考的问题

  1. 此时字符串常量池中有几个对象?是“1112”,“11”,“12”这三个吗?
  2. 此时的连字符“+”是否还是new了一个StringBuilder对象呢?

首先先解决第一个问题
我们可以通过如下代码来测试一个此时字符串常量池中是否有“1112”,“11”,“12”,代码如下

public static void main(String[] args) {
        String s="11"+"12";
        String s1=new String("1")+new String("1");
        s1.intern();
        String s2=new String("1")+new String("2");
        s2.intern();
        String s3="11";
        String s4="12";
        System.out.println(s1==s3);
        System.out.println(s2==s4);
    }

输出结果如下:

D:\jdk8\bin\java.exe -javaagent:D:\IDEA2018\lib\idea_rt.jar=60183:D:\IDEA2018\bin -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\lanjie\out\production\lanjie StringAppendTest
true
true

Process finished with exit code 0

对上面代码的解释:

  • String s1=new String(“1”)+new String(“1”);这句话可以保证在创建“11”这个字符串对象的同时没有在常量池创建“11”,s2同理
  • s1.intern();Java6+当调用intern时分为如下三种情况
    1.首先判断字符串常量池中是否有该字符串对象,若有则返回池中该字符串引用
    2.若字符串常量池中没有,则去Java堆中查找,若有,则将堆中此字符串对象引用添加到字符串常量池中,并返回该引用
    3.若堆中也没有,则在池中创建该字符串对象,并返回引用
  • s1=s3,s2=s4说明,在执行intern时,池中并没有该对象,所以直接将堆中引用添加入池中,并返回,因此当创建s3,s4,他们也是指向堆中的对象,故相等(具体请看上篇博客)

上面的输出结果说明当JVM执行完String s=“11”+“12”;这句代码后,字符常量池中是没有“11”,“12”这两个字符串的

那字符串常量池中是否有“1112”呢?我们继续用代码测试

public static void main(String[] args) {
        String s="11"+"12";
        String s1=new String("11")+new String("12");
        s1.intern();
        String s2="1112";
        System.out.println(s1==s2);
    }

输出结果如下:

D:\jdk8\bin\java.exe -javaagent:D:\IDEA2018\lib\idea_rt.jar=60587:D:\IDEA2018\bin -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\lanjie\out\production\lanjie StringAppendTest
false

Process finished with exit code 0

上面代码说明在执行String s=“11”+“12”;字符串常量池中已有“1112”这个字符串
所以我们可以做如下总结String s=“11”+“12”;这句话只会产生一个字符串对象,那就是“1112”,因此我们回答了第一个问题。不过不要着急下一个问题,我们接着这个话题继续延申一下。
其实还有一种情况,代码如下:

public static void main(String[] args) {
        String s="11"+new String("12");
        System.out.println(s);
    }

上面代码你们觉得字符串常量池中会有几个字符串呢?
直接揭晓答案,三个
代码验证如下

public static void main(String[] args) {
        String s="11"+new String("12");
        String s1=new String("1")+new String("1");
        s1.intern();
        String s2=new String("1")+new String("2");
        s2.intern();
        String s3="11";
        String s4="12";
        System.out.println(s1==s3);
        System.out.println(s2==s4);
    }

输出结果如下:
1.验证“11”、“12”是否存在于字符串常量池

D:\jdk8\bin\java.exe -javaagent:D:\IDEA2018\lib\idea_rt.jar=61109:D:\IDEA2018\bin -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\lanjie\out\production\lanjie StringAppendTest
false
false

Process finished with exit code 0

2.验证“1112”是否存在于字符串常量池

public static void main(String[] args) {
        String s="11"+new String("12");
        String s1=new String("11")+new String("12");
        s1.intern();
        String s2="1112";
        System.out.println(s1==s2);
    }

输出结果如下:

D:\jdk8\bin\java.exe -javaagent:D:\IDEA2018\lib\idea_rt.jar=61163:D:\IDEA2018\bin -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\lanjie\out\production\lanjie StringAppendTest
false

Process finished with exit code 0

有的同学就会问这是为什么呢?答案稍后揭晓!

接下来我们继续看之前提出的第二个问题,哈哈,你们是不是已经忘了第二个问题了,第二个问题是,当执行完String s=“11”+“12”;,此时的连字符“+”是否还是new了一个StringBuilder对象呢?
我们继续反编译一下代码

Compiled from "StringAppendTest.java"
public class StringAppendTest {
  public StringAppendTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String 1112
       2: astore_1
       3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       6: aload_1
       7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      10: return
}

通过反编译的结果我们发现,并没有new一个StringBuilder对象,而是“11”+“12”直接变成了“1112”并输出,第二个问题也回答了。
到了揭晓谜底的时刻。上面的种种现象都归结于一个原因,那就是当“+”两边都是编译器可确定的字符串常量时,编译器就会进行优化,直接将两个字符串拼接
那些是编译器可确定的字符串常量呢?,我所了解的有以下两种形式

  1. String s=“11”+“12”;此时“11”+“12”可被编译器优化
  2. final String s="11"; final String s1="12"; String s2=s+s1;此时的s2=s+s1可悲编译器优化

用“+”进行大量循环拼接字符串

先看一个栗子,当我们用“+”拼接十万次字符串,会发生什么呢?

public static void main(String[] args) {
        long start=System.currentTimeMillis()/1000;
        String s="abc";
        for(int i=0;i<100000;i++){
            s+=i;
        }
        long end=System.currentTimeMillis()/1000;
        System.out.println(end-start);
    }
D:\jdk1.8\bin\java.exe "-javaagent:D:\IntelliJ IDEA 2018.2.5\lib\idea_rt.jar=57700:D:\IntelliJ IDEA 2018.2.5\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk1.8\jre\lib\charsets.jar;D:\jdk1.8\jre\lib\deploy.jar;D:\jdk1.8\jre\lib\ext\access-bridge-64.jar;D:\jdk1.8\jre\lib\ext\cldrdata.jar;D:\jdk1.8\jre\lib\ext\dnsns.jar;D:\jdk1.8\jre\lib\ext\jaccess.jar;D:\jdk1.8\jre\lib\ext\jfxrt.jar;D:\jdk1.8\jre\lib\ext\localedata.jar;D:\jdk1.8\jre\lib\ext\nashorn.jar;D:\jdk1.8\jre\lib\ext\sunec.jar;D:\jdk1.8\jre\lib\ext\sunjce_provider.jar;D:\jdk1.8\jre\lib\ext\sunmscapi.jar;D:\jdk1.8\jre\lib\ext\sunpkcs11.jar;D:\jdk1.8\jre\lib\ext\zipfs.jar;D:\jdk1.8\jre\lib\javaws.jar;D:\jdk1.8\jre\lib\jce.jar;D:\jdk1.8\jre\lib\jfr.jar;D:\jdk1.8\jre\lib\jfxswt.jar;D:\jdk1.8\jre\lib\jsse.jar;D:\jdk1.8\jre\lib\management-agent.jar;D:\jdk1.8\jre\lib\plugin.jar;D:\jdk1.8\jre\lib\resources.jar;D:\jdk1.8\jre\lib\rt.jar;D:\lanjie\out\production\lanjie com.csdn.stringTest
27

Process finished with exit code 0

如上代码所示,用连字符“+”拼接十万次需要耗费大约27秒左右的时间
那我们拼接一百万一千万次呢?是的我们应该猜到了,拼接效率会大大降低,根据之前我们分析的“+”连字符原理,在没有进行编译器优化的时候,其底层实现为new StringBuilder进行append,这样会导致我们在堆区new出大量的StringBuilder,肯定会造成效率的损失。

StringBuilder正式登场

通过上一部分我们就晓得“+”连字符的底层实现就是new一个StringBuilder对象,并调用其append()方法,讲到这里熟悉我的同学就知道下一步了,上源码!!!我们今天着重看append方法哈,就不都看了~

package java.lang;

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
	//StringBuilder的初始容量为16
    public StringBuilder() {
        super(16);
    }

     //也可以指定容量大小
    public StringBuilder(int capacity) {
        super(capacity);
    }

	//或者以一个字符串初始化,那么其初始容量就是字符串的长度+16
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

	//CharSequence的主要实现类为String,StringBuffer,StringBuilder 
	//所以也可以以String,StringBuffer,StringBuilder初始化
    public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

	//这个表明StringBulider可以append一切对象,不过需要将他们先转为String
    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

	//发现后面调用的都是父类append,所以我们移步AbstractStringBuilder
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

    public StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
    }

    @Override
    public StringBuilder append(CharSequence s) {
        super.append(s);
        return this;
    }

    /**
     * @throws     IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder append(CharSequence s, int start, int end) {
        super.append(s, start, end);
        return this;
    }

    @Override
    public StringBuilder append(char[] str) {
        super.append(str);
        return this;
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder append(char[] str, int offset, int len) {
        super.append(str, offset, len);
        return this;
    }

    @Override
    public StringBuilder append(boolean b) {
        super.append(b);
        return this;
    }

    @Override
    public StringBuilder append(char c) {
        super.append(c);
        return this;
    }

    @Override
    public StringBuilder append(int i) {
        super.append(i);
        return this;
    }

    @Override
    public StringBuilder append(long lng) {
        super.append(lng);
        return this;
    }

    @Override
    public StringBuilder append(float f) {
        super.append(f);
        return this;
    }

    @Override
    public StringBuilder append(double d) {
        super.append(d);
        return this;
    }
}
package java.lang;

import sun.misc.FloatingDecimal;
import java.util.Arrays;

/**
 * A mutable sequence of characters.
 * <p>
 * Implements a modifiable string. At any point in time it contains some
 * particular sequence of characters, but the length and content of the
 * sequence can be changed through certain method calls.
 *
 * <p>Unless otherwise noted, passing a {@code null} argument to a constructor
 * or method in this class will cause a {@link NullPointerException} to be
 * thrown.
 *
 * @author      Michael McCloskey
 * @author      Martin Buchholz
 * @author      Ulf Zibis
 * @since       1.5
 */
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() {
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

	//进行数组扩容
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            //正儿八经的进行扩容,之前那些都是对容量的确定
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	//确定一个新的容量	
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        //新的容量为原来的长度*2+2
        int newCapacity = (value.length << 1) + 2;
        //若新的小于传入参数,则将传入参数赋予新容量
        //传入的参数就是原本长度+append进来对象的长度
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        //返回一个合适的容量
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }
	//确定最大容量
    private int hugeCapacity(int minCapacity) {
    	//若超出最大容量,则抛出OutOfMemoryError
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }
	
	//进行数组的复制
    public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
    {
        if (srcBegin < 0)
            throw new StringIndexOutOfBoundsException(srcBegin);
        if ((srcEnd < 0) || (srcEnd > count))
            throw new StringIndexOutOfBoundsException(srcEnd);
        if (srcBegin > srcEnd)
            throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
	
	//这个表明可以append一切对象,不过需要将他们先转为String	
    public AbstractStringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

	//append一个String
    public AbstractStringBuilder append(String str) {
    	//判空操作
        if (str == null)
            return appendNull();
        //传入字符串长度
        int len = str.length();
        //扩容
        ensureCapacityInternal(count + len);
        //将str加到value后(注意此时的value已经扩容)
        str.getChars(0, len, value, count);
        //长度增加
        count += len;
        return this;
    }

    // Documentation in subclasses because of synchro difference
    //同append一个String一样
    public AbstractStringBuilder append(StringBuffer sb) {
        if (sb == null)
            return appendNull();
        int len = sb.length();
        ensureCapacityInternal(count + len);
        sb.getChars(0, len, value, count);
        count += len;
        return this;
    }

    /**
     * @since 1.8
     */
     //同append一个String一样
    AbstractStringBuilder append(AbstractStringBuilder asb) {
        if (asb == null)
            return appendNull();
        int len = asb.length();
        ensureCapacityInternal(count + len);
        asb.getChars(0, len, value, count);
        count += len;
        return this;
    }

    // Documentation in subclasses because of synchro difference
    @Override
    //CharSequence的主要实现类为String,StringBuffer,StringBuilder 
    public AbstractStringBuilder append(CharSequence s) {
        if (s == null)
            return appendNull();
        if (s instanceof String)
            return this.append((String)s);
        if (s instanceof AbstractStringBuilder)
            return this.append((AbstractStringBuilder)s);

        return this.append(s, 0, s.length());
    }

	//若传入的对象是null那么仍然会将“null”这个单词append
    private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

	//append一个CharSequence
    public AbstractStringBuilder append(CharSequence s, int start, int end) {
    	//当s为空时,直接将"null"赋值给s
        if (s == null)
            s = "null";
        //判断传入参数是否合法    
        if ((start < 0) || (start > end) || (end > s.length()))
            throw new IndexOutOfBoundsException(
                "start " + start + ", end " + end + ", s.length() "
                + s.length());
        int len = end - start;
        //数组扩容
        ensureCapacityInternal(count + len);
        //数组赋值
        for (int i = start, j = count; i < end; i++, j++)
            value[j] = s.charAt(i);
        count += len;
        return this;
    }

	//append一个字符数组
    public AbstractStringBuilder append(char[] str) {
        int len = str.length;
        ensureCapacityInternal(count + len);
        System.arraycopy(str, 0, value, count, len);
        count += len;
        return this;
    }

	//从数组的指定位置开始append
    public AbstractStringBuilder append(char str[], int offset, int len) {
        if (len > 0)                // let arraycopy report AIOOBE for len < 0
            ensureCapacityInternal(count + len);
        System.arraycopy(str, offset, value, count, len);
        count += len;
        return this;
    }
	
	//append一个布尔值,先扩容然后将字符串“true”或“false”加入数组
    public AbstractStringBuilder append(boolean b) {
        if (b) {
            ensureCapacityInternal(count + 4);
            value[count++] = 't';
            value[count++] = 'r';
            value[count++] = 'u';
            value[count++] = 'e';
        } else {
            ensureCapacityInternal(count + 5);
            value[count++] = 'f';
            value[count++] = 'a';
            value[count++] = 'l';
            value[count++] = 's';
            value[count++] = 'e';
        }
        return this;
    }
	
	//append一个字符
    public AbstractStringBuilder append(char c) {
        ensureCapacityInternal(count + 1);
        value[count++] = c;
        return this;
    }

	//append一个int型
    public AbstractStringBuilder append(int i) {
    	//若i为最小值,直接append最小值字符串,此时就是调用的append一个字符串那个函数
        if (i == Integer.MIN_VALUE) {
            append("-2147483648");
            return this;
        }
        //判断i的长度,若为负数还得加一位负号的长度
        int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1
                                     : Integer.stringSize(i);
        int spaceNeeded = count + appendedLength;
        //扩容
        ensureCapacityInternal(spaceNeeded);
        //数组复制
        Integer.getChars(i, spaceNeeded, value);
        count = spaceNeeded;
        return this;
    }
    
    //append一个long类型,同int原理相同
    public AbstractStringBuilder append(long l) {
        if (l == Long.MIN_VALUE) {
            append("-9223372036854775808");
            return this;
        }
        int appendedLength = (l < 0) ? Long.stringSize(-l) + 1
                                     : Long.stringSize(l);
        int spaceNeeded = count + appendedLength;
        ensureCapacityInternal(spaceNeeded);
        Long.getChars(l, spaceNeeded, value);
        count = spaceNeeded;
        return this;
    }
	
	//这两个你们自己看。。。。
    public AbstractStringBuilder append(float f) {
        FloatingDecimal.appendTo(f,this);
        return this;
    }

	//
    public AbstractStringBuilder append(double d) {
        FloatingDecimal.appendTo(d,this);
        return this;
    } 
}

当我们进行大量循环累加字符串时,可以用StringBuilder

看一个测试栗子

public static void main(String[] args) {
        StringBuilder sBuilder=new StringBuilder();
        sBuilder.append("abc");
        long stringBuilderStart=System.currentTimeMillis()/1000;
        for(int i=0;i<10000000;i++){
            sBuilder.append(""+i);
        }
        long stingBuilderEnd=System.currentTimeMillis()/1000;
        System.out.println(stingBuilderEnd-stringBuilderStart);
    }
D:\jdk8\bin\java.exe -javaagent:D:\IDEA2018\lib\idea_rt.jar=59232:D:\IDEA2018\bin -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\lanjie\out\production\lanjie StringAppendTest
1

Process finished with exit code 0

用StringBuilder累加一千万次才使用1秒的时间,而使用连字符“+”累加十万次就得需要27秒,这个效率提升还是很高很高的,因此在进行大量字符串累加时,建议使用StringBuilder
提起StringBuilder我们就不得不说StringBuffer啦

StringBuffer也来啦

StringBuffer同StringBuilder一样,也是继承于AbstractStringBuilder,其append方法,也是调用父类append,StringBuffer同StringBuilder唯一的区别就是,StringBuffer内大部分方法都是同步方法因此StringBuffer是线程安全的,反之StringBuilder是线程不安全的,关于线程安全问题,有机会就会再写一篇专门关于线程安全的博客~

这篇博客写到这里就结束啦~拜拜

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值