`

Java中的StringBuilder类功能详解

阅读更多
Java中的StringBuilder类功能详解

字符串连接可以通过两种方法实现,其中一种是在Java中提供的一个StringBuilder类(这个类只在J2SE5及以上版本提供,以前的版本使用StringBuffer类)。
字符串是Java程序中最常用的一种数据结构之一。在Java中的String类已经重载的"+"。也就是说,字符串可以直接使用"+"进行连接,如下面代码所示:

String s = "abc" + "ddd"; 但这样做真的好吗?当然,这个问题不能简单地回答yes or no。要根据具体情况来定。在Java中提供了一个StringBuilder类(这个类只在J2SE5及以上版本提供,以前的版本使用StringBuffer类),这个类也可以起到"+"的作用。那么我们应该用哪个呢?

下面让我们先看看如下的代码:

package string;
        public class TestSimplePlus    {
        public static void main(String[] args)        {
            String s = "abc";
            String ss = "ok" + s + "xyz" + 5;
            System.out.println(ss);        }
    } 

上面的代码将会输出正确的结果。从表面上看,对字符串和整型使用"+"号并没有什么区别,但事实真的如此吗?下面让我们来看看这段代码的本质。

我们首先使用反编译工具(如jdk带的javap、或jad)将TestSimplePlus反编译成Java Byte Code,其中的奥秘就一目了然了。在本文将使用jad来反编译,命令如下:

jad -o -a -s d.java TestSimplePlus.class


反编译后的代码如下:

package string;
   import java.io.PrintStream;
   public class TestSimplePlus  {
      public TestSimplePlus()      {
      //   0  0:aload_0         
      //   1  1:invokespecial   #8   < Method void Object()>      
      //    2    4:return          
      }   
    public static void main(String args[])      {
        String s = "abc";
      //    0    0:ldc1            #16  < String "abc">   
      //    1    2:astore_1       
        String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString();
      //    2    3:new             #18  < Class StringBuilder>
      //    3    6:dup             
      //    4    7:ldc1            #20  < String "ok"> 
      //    5    9:invokespecial   #22  < Method void StringBuilder(String)> 
      //    6   12:aload_1      
      //    7   13:invokevirtual   #25  < Method StringBuilder StringBuilder.append(String)>
      //    8   16:ldc1            #29  < String "xyz">
      //    9   18:invokevirtual   #25  < Method StringBuilder StringBuilder.append(String)> 
      //   10   21:iconst_5      
      //   11   22:invokevirtual   #31  < Method StringBuilder StringBuilder.append(int)>
      //   12   25:invokevirtual   #34  < Method String StringBuilder.toString()>
      //   13   28:astore_2
        System.out.println(ss);
      //   14   29:getstatic       #38  < Field PrintStream System.out>
      //   15   32:aload_2       
      //   16   33:invokevirtual   #44  < Method void PrintStream.println(String)>
      //   17   36:return           
     }  
}  

读者可能看到上面的Java字节码感到迷糊,不过大家不必担心。本文的目的并不是讲解Java Byte Code,因此,并不用了解具体的字节码的含义。

使用jad反编译的好处之一就是可以同时生成字节码和源代码。这样可以进行对照研究。从上面的代码很容易看出,虽然在源程序中使用了"+",但在编译时仍然将"+"转换成StringBuilder。因此,我们可以得出结论,在Java中无论使用何种方式进行字符串连接,实际上都使用的是StringBuilder类。

那么是不是可以根据这个结论推出使用"+"和StringBuilder类的效果是一样的呢?这个要从两个方面的解释。如果从运行结果来解释,那么"+"和StringBuilder是完全等效的。但如果从运行效率和资源消耗方面看,那它们将存在很大的区别。

当然,如果连接字符串行表达式很简单(如上面的顺序结构),那么"+"和StringBuilder类基本是一样的,但如果结构比较复杂,如使用循环来连接字符串,那么产生的Java Byte Code就会有很大的区别。先让我们看看如下的代码:

package string;
      import java.util.*;
      public class TestComplexPlus   { 
      public static void main(String[] args)       {
           String s = "";
           Random rand = new Random();
           for (int i = 0; i <  10; i++)           {
               s = s + rand.nextInt(1000) + " ";
           }
           System.out.println(s); 
      }
   } 

上面的代码返编译后的Java Byte Code如下:

package string;
   import java.io.PrintStream;
  import java.util.Random;
   public class TestComplexPlus  {
       public TestComplexPlus()      {
      //    0    0:aload_0       
      //    1    1:invokespecial   #8   < Method void Object()>     //    2    4:return            
    } 
    public static void main(String args[])      {
          String s = "";
      //    0    0:ldc1            #16  < String "">
      //    1    2:astore_1
          Random rand = new Random();
      //    2    3:new             #18  < Class Random>
      //    3    6:dup             
      //    4    7:invokespecial   #20  < Method void Random()>
      //    5   10:astore_2        
          for(int i = 0; i <  10; i++)
      //*   6   11:iconst_0       
      //*   7   12:istore_3        
      //*   8   13:goto            49
           s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString();
      //    9   16:new             #21  < Class StringBuilder>
      //   10   19:dup             
      //   11   20:aload_1          
      //   12   21:invokestatic    #23  < Method String String.valueOf(Object)>
      //   13   24:invokespecial   #29  < Method void StringBuilder(String)>
      //   14   27:aload_2       
      //   15   28:sipush          1000
      //   16   31:invokevirtual   #32  < Method int Random.nextInt(int)> 
      //   17   34:invokevirtual   #36  < Method StringBuilder StringBuilder.append(int)>
      //   18   37:ldc1            #40  < String " ">
      //   19   39:invokevirtual   #42  < Method StringBuilder StringBuilder.append(String)>
      //   20   42:invokevirtual   #45  < Method String StringBuilder.toString()>
      //   21   45:astore_1        
      //   22   46:iinc            3  1
      //   23   49:iload_3        
      //   24   50:bipush          10
      //   25   52:icmplt          16
          System.out.println(s); 
      //   26   55:getstatic       #49  < Field PrintStream System.out> 
      //   27   58:aload_1         
      //   28   59:invokevirtual   #55  < Method void PrintStream.println(String)>
      //   29   62:return            
    }  
} 

大家可以看到,虽然编译器将"+"转换成了StringBuilder类,但创建StringBuilder对象的位置却在for语句内部。这就意味着每执行一次循环,就会创建一个StringBuilder对象(对于本例来说,是创建了10个StringBuilder对象),虽然Java有垃圾回收器,但这个回收器的工作时间是不定的。如果不断产生这样的垃圾,那么仍然会占用大量的资源。解决这个问题的方法就是在程序中直接使用StringBuilder类来连接字符串,代码如下:

package string;
   import java.util.*;
   public class TestStringBuilder  {
      public static void main(String[] args)      {
          String s = "";
          Random rand = new Random();
          StringBuilder result = new StringBuilder();
          for (int i = 0; i <  10; i++)          {
              result.append(rand.nextInt(1000));
              result.append(" ");
          }
          System.out.println(result.toString());
      }
  }

 上面代码反编译后的结果如下:

package string;
   import java.io.PrintStream;
  import java.util.Random;
   public class TestStringBuilder  {
       public TestStringBuilder()      {
      //    0    0:aload_0        
       //    1    1:invokespecial   #8   < Method void Object()>      //    2    4:return         
       }
       public static void main(String args[])      {
          String s = "";
      //    0    0:ldc1            #16  < String "">
      //    1    2:astore_1
                  Random rand = new Random();
      //    2    3:new             #18  < Class Random>
      //    3    6:dup           
      //    4    7:invokespecial   #20  < Method void Random()>
      //    5   10:astore_2
                  StringBuilder result = new StringBuilder();
      //    6   11:new             #21  < Class StringBuilder>
      //    7   14:dup              
      //    8   15:invokespecial   #23  < Method void StringBuilder()>
      //    9   18:astore_3
                  for(int i = 0; i <  10; i++)
      //*  10   19:iconst_0        
      //*  11   20:istore          4
      //*  12   22:goto            47          {              result.append(rand.nextInt(1000));
      //   13   25:aload_3       
      //   14   26:aload_2      
      //   15   27:sipush          1000
      //   16   30:invokevirtual   #24  < Method int Random.nextInt(int)>
      //   17   33:invokevirtual   #28  < Method StringBuilder StringBuilder.append(int)>
      //   18   36:pop
                           result.append(" ");
      //   19   37:aload_3       
      //   20   38:ldc1            #32  < String " ">
      //   21   40:invokevirtual   #34  < Method StringBuilder StringBuilder.append(String)>
      //   22   43:pop
                       }
      //   23   44:iinc            4  1
      //   24   47:iload           4
      //   25   49:bipush          10
      //   26   51:icmplt          25
          System.out.println(result.toString());
      //   27   54:getstatic       #37  < Field PrintStream System.out>
      //   28   57:aload_3
     //   29   58:invokevirtual   #43  < Method String StringBuilder.toString()>
      //   30   61:invokevirtual   #47  < Method void PrintStream.println(String)>
      //   31   64:return
                }
  }
 
从上面的反编译结果可以看出,创建StringBuilder的代码被放在了for语句外。虽然这样处理在源程序中看起来复杂,但却换来了更高的效率,同时消耗的资源也更少了。

在使用StringBuilder类时要注意,尽量不要"+"和StringBuilder混着用,否则会创建更多的StringBuilder对象,如下面代码所:

for (int i = 0; i <  10; i++)  {
      result.append(rand.nextInt(1000));
      result.append(" ");  }

 改成如下形式:

for (int i = 0; i <  10; i++)  {
       result.append(rand.nextInt(1000) + " ");
  }

则反编译后的结果如下:

 for(int i = 0; i <  10; i++)
  //*  10   19:iconst_0    
  //*  11   20:istore          4
  //*  12   22:goto            65
   {
    result.append((new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString());
  //   13   25:aload_3 
  //   14   26:new             #21  < Class StringBuilder>
  //   15   29:dup  
           
从上面的代码可以看出,Java编译器将"+"编译成了StringBuilder类,这样for语句每循环一次,又创建了一个StringBuilder对象。

如果将上面的代码在JDK1.4下编译,必须将StringBuilder改为StringBuffer,而JDK1.4将"+"转换为StringBuffer(因为JDK1.4并没有提供StringBuilder类)。StringBuffer和StringBuilder的功能基本一样,只是StringBuffer是线程安全的,而StringBuilder不是线程安全的。因此,StringBuilder的效率会更高。

分享到:
评论

相关推荐

    java中stringBuilder的用法详解

    主要介绍了java中stringBuilder的用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    java StringBuilder详解

    NULL 博文链接:https://hui-jing-880210.iteye.com/blog/2173195

    详解C++中StringBuilder类的实现及其性能优化

    在Java和C#中,StringBuilder可以创造可变字符序列来动态地扩充字符串,那么在C++中我们同样也可以实现一个StringBuilder并且用来提升性能,下面就来详解C++中StringBuilder类的实现及其性能优化

    java StringBuilder类的详解及简单实例

    主要介绍了java StringBuilder类的详解及简单实例的相关资料,实现了StringBuilder类的追加、插入、替换、删除等操作,需要的朋友可以参考下

    Java 中String StringBuilder 与 StringBuffer详解及用法实例

    主要介绍了Java 中String StringBuilder 与 StringBuffer详解及用法实例的相关资料,需要的朋友可以参考下

    Java中String、StringBuffer、StringBuilder的区别详解

    java中String、StringBuffer、StringBuilder是编程中经常使用的字符串类,他们之间有什么区别呢?下面小编给大家总结了Java中String、StringBuffer、StringBuilder的区别详解,需要的朋友参考下吧

    java基础案例与开发详解案例源码全

    9.3 StringBuilder类的使用233 9.4 日期类简介234 9.5 Java语言国际化时间获取与计算238 9.6 Random类和Math类240 9.7 本章习题243 第10章 10.1 异常概述246 10.2 使用try和catch捕获异常..2 50 10.3 使用throw和...

    java String、StringBuilder和StringBuffer的区别详解

    主要介绍了java String、StringBuilder和StringBuffer的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    java CharSequence、String、StringBuffer、StringBuilder详解

    NULL 博文链接:https://hui-jing-880210.iteye.com/blog/2173150

    Java基础知识点总结.docx

    &lt; java.lang &gt;StringBuilder字符串缓冲区:★★★☆ 74 基本数据类型对象包装类★★★☆ 75 集合框架:★★★★★,用于存储数据的容器。 76 &lt; java.util &gt; Collection接口 77 &lt; java.util &gt;Iterator接口 78 &lt; java....

    疯狂JAVA讲义

    学生提问:既然内部类是外部类的成员,是否可以为外部类定义子类,在子类中再定义一个内部类来重写其父类中的内部类? 211 6.7.4 局部内部类 211 6.7.5 匿名内部类 212 6.7.6 闭包(Closure)和回调 215 6.8 ...

    Java入门教程(微学苑)-part1

    3.19 Java包装类、拆箱和装箱详解 54 3.20 包装类的应用 54 3.20.1.1 1) 实现 int 和 Integer 的相互转换 54 3.20.1.2 2) 将字符串转换为整数 55 3.20.1.3 3) 将整数转换为字符串 55 3.21 自动拆箱和装箱 56 3.22 再...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    【基础】java中String、StringBuffer、StringBuilder的区别 21 【基础】运行时异常和非运行时异常 参见 21 运行时异常 21 非运行时异常 22 【基础】java引用类型 23 强引用(StrongReference) 23 软引用...

    javajdk源码解析-Code-Adventure:Java基础、JDK源码解析、程序人生

    java jdk源码解析 :open_book: ​程序猿探险记 Kong子:“学而不思则罔,思而不学则殆。” :memo: ​目录 Java基础 Java如何将一个Java对象序列化到文件里 静态代理与动态代理 | 初始进洞(上) 静态代理与动态代理 | ...

    String、StringBuilder和StringBuffer超详解

    一、String类 String的部分源码如下 public final class String implements java.io.Serializable, Comparable, CharSequence { /** The value is used for character storage. */ private final char value[]; ...

    java8集合源码分析-Outline:大纲

    java8 集合源码分析 JAVA: 基本语法 static 修饰变量 方法 静态块(初始化块 构造函数 ) 静态内部类() 静态导包 final() transient() foreach循环原理() volatile底层实现() equals和hashcode(, ) string,...

    基于JAVA中Jersey处理Http协议中的Multipart的详解

    之前在基于C#开发彩信用最原始的StringBuilder拼接字符串方式处理过Multipart。现在在做一个项目的时候,由于之前的技术路线都是使用Jersey处理Http这块,为了保持技术路线一致,研究了一下如何使用Jersey处理Http...

    详解python的四种内置数据结构

    对于每种编程语言一般都会...可以动态的增加可减少这一点则有点像java中的stringBuilder对象,列表中有一点值得注意的是在对列表进行复制是,不能简单的使用赋值语句,这样会使得两个变量同时指向一个列表,当一个改变

    关于JDK8中的字符串拼接示例详解

    字符串拼接问题应该是每个Java程序员都熟知的事情了,几乎每个Java程序员都读过关于StringBuffer/StringBuilder来拼接字符串。下面这篇文章主要给大家介绍了关于JDK8中的字符串拼接的相关资料,需要的朋友可以参考下...

    JVM性能优化(五)源码图解jvm字节码

    对于程序本身的优化,可以借鉴很多前辈的经验,但是有些时候,在从源码角度分析的话,不好鉴别出哪个效率高,如对字符串拼接的操作,是直接“+”号拼接效率高还是使用StringBuilder效率高呢? 这个时候,就需要通过...

Global site tag (gtag.js) - Google Analytics