前言
本文说明
- 本文是基于java 8进行分析测试
- 本文会用到一些上篇博客的知识
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);
}
对上面的代码,可以提出两个值得思考的问题
- 此时字符串常量池中有几个对象?是“1112”,“11”,“12”这三个吗?
- 此时的连字符“+”是否还是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”并输出,第二个问题也回答了。
到了揭晓谜底的时刻。上面的种种现象都归结于一个原因,那就是当“+”两边都是编译器可确定的字符串常量时,编译器就会进行优化,直接将两个字符串拼接
那些是编译器可确定的字符串常量呢?,我所了解的有以下两种形式
- String s=“11”+“12”;此时“11”+“12”可被编译器优化
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是线程不安全的,关于线程安全问题,有机会就会再写一篇专门关于线程安全的博客~
这篇博客写到这里就结束啦~拜拜