参考于:https://www.cnblogs.com/dolphin0520/p/3778589.html

String

  • 被final修饰
  • 大部分方法也被final修饰,反例有
CaseInsensitiveComparator 尽量不要说这个点,会引入 比较器排序知识点,未掌握
  • String对象的改变,不会影响原对象,而是创建新的对象,所以,String对象不建议经常变动,否则,会造成大量的运行时常量池内存浪费。
  • 每次String str = “zhangsan”生成,都会去常量池中查找”zhangsan”,如果有,将地址值修改为原来内容的地址值;没有,那就单独开辟内存空间。

new关键字来生成对象是在堆区进行的,堆区进行对象生成的过程是不会去检测该对象是否已经存在的,所以每次String str = new String(“zhangsan”),都是不同的新对象。即:凡是new出来的,正如字面意思,都是新对象。

String str1 = "hello world";
String str2 = new String("hello world");
String str3 = "hello world";
String str4 = new String("hello world");
         
        System.out.println(str1==str2);
 false
        System.out.println(str1==str3);
 true
        System.out.println(str2==str4); false

在分析下面代码

public class Main {
         
    public static void main(String[] args) {
        String string = "";
        for(int i=0;i<10000;i++){
            string += "hello";
        }
    }
}

为了更好的分析,我们先使用 javac Main.java

编译一下 Main.class 文件

接下来 就可以使用 javap Main.class 得到 注意观察 下面 变红的

Compiled from "Main.java"
public class Main {
  public Main();
    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
       2: astore_1
       3: iconst_0
       4: istore_2
       5: iload_2
       6: sipush        10000
       9: if_icmpge     38
      12: new           #3                  // class java/lang/StringBuilder
      15: dup
      16: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      19: aload_1
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: ldc           #6                  // String hello
      25: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      28: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      31: astore_1
      32: iinc          2, 1
      35: goto          5
      38: return
}

看到 是不是很扯淡?我没有写出来StringBuild,更没有调用append() 方法,别担心,这是 JVM 为了优化 减少性能浪费的。即便如次,我们也能看出来的1w,即:即便JVM 优化,依旧创建了1w个StringBuilder。你想想哪里优化了?其实是使用append()方法。 append()方法,会在原来的基础上修改原来的字符,返还This,不会造成大量的新常量池浪费,减少了gc次数,提高效率,但是创建了那么多的StringBuilder对象,效率还是不够高。

String对比StringBuilder

Builder拥有更好的性能,每次+操作,实际是在原来进行添加修改,减少常量池的损耗,为什么这么说,详细,往下看源码

StringBuilder  

@Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

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

StringBuffer 
@Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }

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

你看,不管是StringBuilder,还是StirngBuffer,在使用append的时候,如果参数类型是String,那么直接相加,如果不是String的引用类型,那么就会先转为String,在进行相加。

所以,在大量相加字符串面前,不管是StringBuilder还是StringBuffer,性能都比String要好。

StringBuilder与StringBuffer对比。

因为Buffer中加入了 synchronized 关键字,这个关键字是在多线程访问时起到安全保护作用的。

所以,StringBuffer是线程安全的。

总而言之 :

不考虑线程安全 大量“加”操作的时候,builder > buffer >String

在线程安全下,大量使用“加”操作,只能使用buffer。

如果看完,还要问Buffer为什么比String要性能好,因为他是有append()方法。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注