java String、StringBuffer、StringBuilder区别

    String类源码分析: 

  1. String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被 final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用 以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。
  2. String类中char value[],int  offset,int count,int hash成员属性,可以看出String类其实是通过char数组来保存字符串的。
  3.  从String类提供的方法比如subString,concat,replace等操作都算不是在原有的字符串对象上进行操作,subString是新new一个String对象,后两个都是新new一个char 数组,也就是说进行这些操作后,最原始的字符串并没有被改变。

       特别提醒:对String对象的任何改变都不影响到原对象,相关的任何修改操作都会生成新的对象。

   String StringBuffer  StringBuilder的区别;

      在 系统里面String可以满足任何操作,为什么还要有StringBuffer和StringBuilder类,给大家看段代码我想会更清楚的表达出来。

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

    我想这段代码大家很清楚就明白是什么意识,无非就是字符串循环相加,这样理解是没有错误的,在上面我们通过String类了解过,String类的任何操作都是新new一个对象, 而不是在原有的基础上进行相加,比如:string +="hello"这句话,如果10000次,那么就要创建10000个新StringBuilder对象然后进行append操作,最后通过toString方法返回String对象。也就 是说这个循环执行完毕new出了10000个对象,试想一下,如果这些对象没有被回收,会造成多大的内存资源浪费。从上面还可以看 出:string+="hello"的操作事实上会自动被JVM优化成:

  StringBuilder str = new StringBuilder(string);

  str.append("hello");

  str.toString();

    为了验证我的推理是正确的,可以通过如下代码进行验证:

public class Main {
    private static int time = 50000;
    public static void main(String[] args) {
        testString();
        testOptimalString();
    }  
     
    public static void testString () {
        String s="";
        long begin = System.currentTimeMillis();
        for(int i=0; i<time; i++){
            s += "java";
        }
        long over = System.currentTimeMillis();
        System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
    }
     
    public static void testOptimalString () {
        String s="";
        long begin = System.currentTimeMillis();
        for(int i=0; i<time; i++){
            StringBuilder sb = new StringBuilder(s);
            sb.append("java");
            s=sb.toString();
        }
        long over = System.currentTimeMillis();
        System.out.println("模拟JVM优化操作的时间为:"+(over-begin)+"毫秒");
    }
     
}

   运行结果为:

操作java.lang.String类型使用的时间为:3194毫秒
模拟JVM优化操作的时间为:3197毫秒 

    如果系统中很多地方都这样进行拼接,这将是计算机的灾难,为了提高字符串拼接的性能,让我们看一段StringBuilder的代码:

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

     从这里可以明显看出,这段代码的for循环开始到结束只进行了new操作,创建一个StringBuilder对象,字符串拼接都是通过append操作是在原有对象的基础上进行的。因此在循环了10000次之后,这段代码所占的资源要比上面小得多。有人有说了,既然StringBuilder可以提高字符串拼接的性能,那么怎么还要有StringBuffer,让我们在来看一段代码你就会明白。

 

   

StringBuilder 的insert操作代码
public StringBuilder insert(int index, char str[], int offset, int len){
  super.insert(index, str, offset, len);
  return this;
}

StringBuffer 的insert操作代码
public synchronized  StringBuffer insert(int index, char str[], int offset, int len){
  super.insert(index, str, offset, len);
  return this;
}

   通过两段代码可以发现就是StringBuffer比StringBuilder多了一个synchronized多了一把锁,也就是说StringBuffer是线程安全的。

  

String StringBuffer  StringBuilder的性能;

    讲过这三个方法的区别,通过程序来区别一下这三个类的性能,到底哪个性能更好些。

   

public class test {
    private static int longCount = 50000;
    public static void main(String[] args) {
        testString();
        testStringBuffer();
        testStringBuilder();
        test1String();
        test2String();
    }
     
     
    public static void testString () {
        String s="";
        long begin = System.currentTimeMillis();
        for(int i=0; i<longCount; i++){
            s += "java";
        }
        long over = System.currentTimeMillis();
        System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
    }
     
    public static void testStringBuffer () {
        StringBuffer sb = new StringBuffer();
        long begin = System.currentTimeMillis();
        for(int i=0; i<longCount; i++){
            sb.append("java");
        }
        long over = System.currentTimeMillis();
        System.out.println("操作"+sb.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
    }
     
    public static void testStringBuilder () {
        StringBuilder sb = new StringBuilder();
        long begin = System.currentTimeMillis();
        for(int i=0; i<longCount; i++){
            sb.append("java");
        }
        long over = System.currentTimeMillis();
        System.out.println("操作"+sb.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
    }
     
    public static void test1String () {
        long begin = System.currentTimeMillis();
        for(int i=0; i<longCount; i++){
            String s = "I"+"love"+"java";
        }
        long over = System.currentTimeMillis();
        System.out.println("字符串直接相加操作:"+(over-begin)+"毫秒");
    }
     
    public static void test2String () {
        String s1 ="I";
        String s2 = "love";
        String s3 = "java";
        long begin = System.currentTimeMillis();
        for(int i=0; i<longCount; i++){
            String s = s1+s2+s3;
        }
        long over = System.currentTimeMillis();
        System.out.println("字符串间接相加操作:"+(over-begin)+"毫秒");
    }
     
}

 

    运行结果为:   

操作java.lang.String类型使用的时间为:3149毫秒
操作java.lang.StringBuffer类型使用的时间为:3毫秒
操作java.lang.StringBuilder类型使用的时间为:2毫秒
字符串直接相加操作:1毫秒
字符串间接相加操作:8毫秒

 下面对上面的执行结果进行一般性的解释:

  1)对于直接相加字符串,效率很高,因为在编译器便确定了它的值,也就是说形如"I"+"love"+"java"; 的字符串相加,在编译期间便被优化成了"Ilovejava"。这个可以用javap -c命令反编译生成的class文件进行验证。对于间接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。

  2)String、StringBuilder、StringBuffer三者的执行效率:

  StringBuilder > StringBuffer > String

  当然这个是相对的,不一定在所有情况下都是这样。

  比如String str = "hello"+ "world"的效率就比 StringBuilder st  = new StringBuilder().append("hello").append("world")要高。

  因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:

  当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;

  当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。

你可能感兴趣的:(java,String,StringBuilder,StringBuffer)