博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
String、StringBuffer、StringBuilder
阅读量:5934 次
发布时间:2019-06-19

本文共 8615 字,大约阅读时间需要 28 分钟。

java基础解析系列--String、StringBuffer、StringBuilder

前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容。

 

String

==问题

String s6=new String("jiajun");        String s1="jiajun";        String s2="jiajun";        System.out.println(s1==s2);//true        System.out.println(s1==s6);//false

 

  • 看常量池中是否已有此字符串,如果有,将指针指向这个字符串
  • 如果使用new来创建字符串对象,那么这个字符串是存放在堆中,无论堆中是否已有这个对象

String对象改变

public String substring(int beginIndex) {        if (beginIndex < 0) {            throw new StringIndexOutOfBoundsException(beginIndex);        }        int subLen = value.length - beginIndex;        if (subLen < 0) {            throw new StringIndexOutOfBoundsException(subLen);        }        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);    }        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);    }

 

 
  • 从源码可以看出,任何操作都是创建一个新的对象,不影响原对象

StringBuffer和StringBuidler

初始容量

  • StringBuilder和StringBuffer的构造参数来初始化容量
public StringBuilder() {        super(16);    }AbstractStringBuilder(int capacity) {        value = new char[capacity];    }

 

 
  • 默认情况下容量为16
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;    }    private void ensureCapacityInternal(int minimumCapacity) {        // overflow-conscious code        if (minimumCapacity - value.length > 0) {            value = Arrays.copyOf(value,                    newCapacity(minimumCapacity));        }    }    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;    }

 

 
  • 从源码看出,在执行append方法的时候,会执行ensureCapacityInternal方法来保证容量,而如果超出容量的话,会重新创建一个char数组,并将旧的字符数组复制到新的字符数组

线程安全

public synchronized StringBuffer append(StringBuffer sb) {        toStringCache = null;        super.append(sb);        return this;    } public synchronized StringBuffer append(String str) {        toStringCache = null;        super.append(str);        return this;    }     public StringBuilder append(StringBuffer sb) {        super.append(sb);        return this;    }    public StringBuilder append(String str) {        super.append(str);        return this;    }    public String toString() {        // Create a copy, don't share the array        return new String(value, 0, count);    }public synchronized StringBuffer append(StringBuffer sb) {        toStringCache = null;        super.append(sb);        return this;    } public synchronized StringBuffer append(String str) {        toStringCache = null;        super.append(str);        return this;    }     public StringBuilder append(StringBuffer sb) {        super.append(sb);        return this;    }    public StringBuilder append(String str) {        super.append(str);        return this;    }    public String toString() {        // Create a copy, don't share the array        return new String(value, 0, count);    }

 

  • 可以看出,String的方法是加了synchronzied,也就加了锁,那么而在单线程的情况下或者不用考虑线程安全的情况下,那么StringBuilder的性能是更高的

toString方法

public synchronized String toString() {        if (toStringCache == null) {            toStringCache = Arrays.copyOfRange(value, 0, count);        }        return new String(toStringCache, true);    }public String toString() {        // Create a copy, don't share the array        return new String(value, 0, count);    }

 

 
  • 通过源码发现,toString方法会创建一个新的String对象

性能试验

间接相加和直接相加

public class d {    public static void main(String[] args) {        String s="I"+"love"+"jiajun";        String s1="I";        String s2="love";        String s3="jiajun";        String s4=s1+s2+s3;    }}

 

 

  • 通过反编译的结果可以看出,第一种方式字符串直接相加,在编译器就直接优化了”Ilovejiajun“
  • 而第二种方式间接相加,从结果可以看出,是先创建一个StringBuilder,然后再apend,最后再toString方法,可以发现性能比第一种低
public class d {    public static void main(String[] args) {        String s="I"+"love"+"jiajun";        String s1="I";        String s2=s1+"lovejiajun";        System.out.println(s==s2);    }}

 

 

  • 同样从反编译的结果可以看出,第二种方式并没有被优化,也是通过StringBuilder来实现的,最后通过toString方法创建一个String对象,所以返回的false
  • 但是当s1是用final修饰的却是不一样的,虚拟机会对其进行优化,所以不会像之前一样创建一个StringBuilder,最后在堆中产生一个对象
public class d {    public static void main(String[] args) {                final String s1="I";                String s2=s1+"lovejiajun";        String s3="Ilovejiajun";        //s1==s3    }}

 

 

用+和用append

public class Demo3 {    public static void main(String[] args) {        run1();        run2();    }        public static void run1() {        long start = System.currentTimeMillis();        String result = "";        for (int i = 0; i < 10000; i++) {            result += i;        }        System.out.println(System.currentTimeMillis() - start);    }    public static void run2() {         long start = System.currentTimeMillis();        StringBuilder builder = new StringBuilder();        for (int i = 0; i < 10000; i++) {            builder.append(i);        }        System.out.println(System.currentTimeMillis() - start);    }

 

//输出:223 1
  • 从实验发现,用append效率更高,从实验一发现,当字符串相加的时候,实际上每次都会重新初始化StringBuilder然后执行相加,这样效率并不高

初始化容量

public class Demo3 {    public static void main(String[] args) {        test1();        test2();    }    public static void test1() {        StringBuilder sb = new StringBuilder(7000000);        long start = System.currentTimeMillis();        for (int i = 0; i < 1000000; i++) {            sb.append("jiajun");        }        long end=System.currentTimeMillis()-start;        System.out.println(end);    }    public static void test2() {        StringBuilder sb = new StringBuilder();        long start = System.currentTimeMillis();        for (int i = 0; i < 1000000; i++) {            sb.append("jiajun");        }        long end=System.currentTimeMillis()-start;        System.out.println(end);    }    //输出:18 26}

 

 
  • 通过实验可以看出,适当的初始化容量可以提高性能,因为当不初始化容量的时候,如果此时append超出容量,那么将会从新创建一个char数组,并且进行复制

总结

  • 用new创建对象的时候,会在堆中创建对象,而如果是直接用引号形式的话,会先看常量池是否有此字符串,有的话指向常量池的字符串
  • StringBuilder是非线程安全的,StringBuffer是线程安全的
  • 使用StringBuilder和StringBuffer的时候最好初始化一个合适的容量,因为如果默认容量不够的话,会重新创建一个char数组,再进行复制
  • 字符串相加的时候,直接相加的时候,编译器会进行优化,而如果是间接相加的时候,实际上会创建一个StringBuilder来进行append

 

例如:

StringTest.java

public class StringTest {    public static void main(String[] args) {        String s = "111111" + "2222222" + "33333333333";        String s2=s+"4444";        s=s+"555";    }}

 

编译之后:StringTest.class

public class StringTest{  public static void main(String[] paramArrayOfString)  {    String str1 = "111111222222233333333333";    String str2 = str1 + "4444";    str1 = str1 + "555";  }}

 

 JavaP反编译字节码文件:

C:\Users\liqiang\Desktop>javap -c StringTest.classCompiled from "StringTest.java"public class StringTest {  public StringTest();    Code:       0: aload_0       1: invokespecial #1                  // Method java/lang/Object."
":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String 111111222222233333333333 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."
":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String 4444 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_2 23: new #3 // class java/lang/StringBuilder 26: dup 27: invokespecial #4 // Method java/lang/StringBuilder."
":()V 30: aload_1 31: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: ldc #8 // String 555 36: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 39: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 42: astore_1 43: return}

 

转载地址:http://wmctx.baihongyu.com/

你可能感兴趣的文章
【阅读笔记】Web安全深度剖析
查看>>
微信小程序中使用emoji表情相关
查看>>
浏览器端Event loop简介
查看>>
在 Node.js 中用 pipe 处理数组的实现
查看>>
Web开发中常见的认证机制
查看>>
[译] 设计一个现代的缓存
查看>>
nodejs微信公众号开发——0.起手式
查看>>
Learn Spring - Spring DAO
查看>>
移动APP测试之安全性测试策略分析
查看>>
解读 2018 之运维篇:我们离高效智能的运维还有多远
查看>>
深入redux技术栈
查看>>
Scrum Guides 2017年最新修改
查看>>
Java永久代去哪儿了
查看>>
Microsoft将持续交付功能添加到Visual Studio、Azure
查看>>
为什么你写的代码糟透了?
查看>>
数字时代的精益组织
查看>>
Visual Studio 15.6第四个预览版进一步打造F#功能
查看>>
AppsFlyer将API网关服务从Clojure迁移到Golang
查看>>
机器学习研究的七个迷思
查看>>
阿里巴巴和京东进军美国电商界,分别针对企业用户和普通用户
查看>>