zoukankan      html  css  js  c++  java
  • 9.String StringBuffer StringBuilder

    String,StringBuffer和StringBuilder三者的讲解

      对于StringBuffer和StringBuilder是对Stirng的一个优化。

      之前已经说过了,String对象一旦创建后,String中的内容是不能够改变的。每次改变String都会创建新的对象。这样的话会造成相应空间的浪费。

    介于此jdk开 发人员计出了StringBuffer和StringBuilder,对于后者而言它们的内容是能够动态改变的。而StringBuffer和StringBuilder的区别就在于StringBuffer是线程安全的StringBuffer中的绝大部分方法都加了同步关键字)。

      而StringBuilder是线程不安全的。因为StringBuilder是线程不安全的,所以其运行效率会更高,如果一个字符串 是在方法里面定义的话,这种情况仅仅可能有一个线程访问它,不存在不安全的因素,这时推荐使用StringBuilder。

      如果一个实例变量是在类里面定义的,并且在多线程 下会访问到,这时最好使用StringBuffer。

    扩展:

    1 String类

        String类在java的java.lang.String包下面,需要特别说明的是String类是final关键字修饰的,也就是说String类是不能够被继承的,String中的内容一旦被创建后是不能被修改的。Stirng是对象也不是8种基本数据类型

       1) 具体的讲解请看下面的例子:.

    package com.yonyou.test;
     
     
    class Test{
      public static void main(String[] args) {
        String str=new String("龙不吟");
        str=new String("虎不啸");//原始String对象中str的内容到底变了没有?
            System.out.println(str);
         
        //下面也是一个String的例子
        String str2="天下太平";
        str2=str2+"国泰民安";//原始String对象中的str2到底变了没有?
        System.out.println(str2);
          
    }
     }

    首先说明上述原始String对象中的内容都没有改变!

           对于这个问题大家可以这样理解:

           如果大家看过String的源码可以发现,String字符串中的内容存储在char数组中的。

           在判断原始String对象str和str2的是否改变了,这里需要明白一个问题,在java中相关对象的引用变量一般都存在栈中,而相关的对象都是存在堆中的,栈中的值指向了它所引用的对象(堆中相应的对象的地址)。

         栈 :由JVM分配区域,用于保存线程执行的动作和数据引用。栈是一个运行的单位,Java中一个线程就会相应有一个线程栈与之对应。

                一般是用于存储的基本数据类型的局部变量(注意这里仅仅是局部的,对于全局变量不能这样定义哦?)

                

         堆 :由JVM分配的,用于存储对象等数据的区域。一般用于存储我们new出来的对象。

     

         常量池 :在堆中分配出来的一块存储区域,用于存储显式 的String,float或者integer.例如String str="abc"; abc这个字符串是显式声明,所以存储在常量池。

    对于java内存的更详细的讲解请参考:http://www.cnblogs.com/xiohao/p/4278173.html 这里不再累述。

          例如:

          创建一个对象String str=new String("Hello World");

          对于变量str而言,它代表的是引用变量,它的值是存储在栈中的,而new String("Hello World")会创建一个新的对象,而对象的值是存储在堆中的。而引用

          变量str指向对中的对象new String("Hello World"); 这样看来视乎上面的问题就很好解释了。

           由于是String修饰的str和str2而言,它们的引用变量的本身是不能够改变,但是它们指向的对象,比如说指向的堆中地址却是可以改变的。

           所以说上面String对象str和str2所对应的原始对象都没有改变,仅仅是str和str2所对应的引用变量的指向发生的改变。这段话有一些绕

           理解起来不是那么容易,请多读几遍,反复思考一下。

           

          2) 接下来大家可以来理解一下

      

    package com.yonyou.test;
     
     
    class Test{
      public static void main(String[] args) {
         String str=new String("Hello World");
         String str2="Hello World";
         System.out.println("str和str2的equals值相同吗?"+str.equals(str2));
         System.out.println("str和str2的==值相同吗?"+(str==str2));//注意此处的str==str2必须用括号括起来,
                                                                     // 否则的话 字符连接符号 +的优先级高于==,实际上进行的比较是
                                                                     //str和str2的==值相同吗?Hello World和Hello World是否相同
     }
    }

    输出结果为:

     str和str2的equals值相同吗?true
     str和str2的==值相同吗?false

     这些结果右是怎么输出来的呢?

     首先我们需要明白equals和==的区别和联系

     对于equals而言,它是 Object类中方法如下:

    ...
    
         * @param   obj   the reference object with which to compare.
         * @return  <code>true</code> if this object is the same as the obj
         *          argument; <code>false</code> otherwise.
         * @see     #hashCode()
         * @see     java.util.Hashtable
         */
    
     public boolean equals(Object obj) {
     return (this == obj);
        }

     通过在这里查看Object类中equals的方法我们知道,如果一个类没有重写Object类中的equals方法的话,那么它的作用和==的作用是一样的。说白了是

       没有任何区别的。但是如果用户可以根据自己的需求进行重写equals方法那样的话,equals比较的返回值就和相关的需求相关了。

       如在jdk中的String类中的equals方法是这样重写的:

    /**
         * Compares this string to the specified object.  The result is {@code
         * true} if and only if the argument is not {@code null} and is a {@code
         * String} object that represents the same sequence of characters as this
         * object.
         *
         * @param  anObject
         *         The object to compare this {@code String} against
         *
         * @return  {@code true} if the given object represents a {@code String}
         *          equivalent to this string, {@code false} otherwise
         *
         * @see  #compareTo(String)
         * @see  #equalsIgnoreCase(String)
         */
        public boolean equals(Object anObject) {
     if (this == anObject) {
         return true;
     }
     if (anObject instanceof String) {
         String anotherString = (String)anObject;
         int n = count;
         if (n == anotherString.count) {
      char v1[] = value;
      char v2[] = anotherString.value;
      int i = offset;
      int j = anotherString.offset;
      while (n-- != 0) {
          if (v1[i++] != v2[j++])
       return false;
      }
      return true;
         }
     }
     return false;
        }

    这样我们就可以发现,对于String对象中的equals方法而言,它的目的是比较两个对象所对应的值是否相同,注意仅仅是两个对象的值,跟这两个对象

    的引用(地址没有任何关系)。

        而对于==而言,它在java中的主要作用则是用来比较java中一些基本类型(如int,long,float等)的值是否相同以及比较两个对象是否有相同(即两个对象的引用

        地址是否相同)。

        这也就明白了为什么上面的对象equals的值相同而==的值不同。

          3)对于下面声明的这个变量创建了几个对象?

              String str=new Stirng("xyz"); //创建了几个对象

              String str2="abc";//创建了几个对象

              首先说“String str=new Stirng("xyz");”创建了一个或者两个

                   对于“xyz”这个对象而言,它是存放在字符串的缓冲区中的,不管出现多少遍,都是缓冲区中的那一个。而new String()每次都会创建一个新的对象。所以如果之前创建

              过“xyz”这个对象的话,那么久创建一个对象,而如果之前要是没有创建过这个字符串的话,那么就会创建两个对象。

              其次说String str2=“abc”会创建零个或者一个。

                  这个是为什么我就不哆嗦了。

               需要注意str和str2是变量名不是对象。

             

              请看下面的这条语句创建了几个对象?

              String str="a"+"b"+"c"+"d"+"e"+"f"+"g"+"h"+"i"+"j"+"k";

              没错这里仅仅创建了一个对象即str=”abcdefghijk“;

              为了更好的说明这个问题我们来看下面的例子:

       

    package com.xiaohao.test;
     
    public class Test{
        public static void main(String[] args) {
          String str1="ab";
          String str2="a"+"b";
          String str3="b";
          String str4="a"+str3;
          System.out.println("str1和str2相等吗?"+(str1==str2));
          System.out.println("str1和str4相等吗?"+(str1==str4));
        }
     
    }

     上面程序的输出结果为:

           str1和str2相等吗?true
           str1和str4相等吗?false

          这说明javac编译的时候可以对字符串常量直接相加的表达式进行优化,不必等到运行期在进行加法处理,而是在编译的时候直接去掉加号,直接将其编译成这些常量

          相连的结果。而对于str4而言由于str3是变量,不是字符串常量,所以最终的结果为false。

         

          下面来讲StringBuffer和StringBuilder

          对于StringBuffer和StringBuilder是对Stirng的一个优化。

          之前已经说过了,String对象一旦创建后,String中的内容是不能够改变的。每次改变String都会创建新的对象。这样的话会造成相应空间的浪费。介于此jdk额开

          发人员计出了StringBuffer和StringBuilder,对于后者而言它们的内容是能够动态改变的。而StringBuffer和StringBuilder的区别就在于StringBuffer是线程安全的

        (StringBuffer中的绝大部分方法都加了同步关键字)而StringBuilder是线程不安全的。因为StringBuilder是线程不安全的,所以其运行效率会更高,如果一个字符串

          是在方法里面定义的话,这种情况仅仅可能有一个线程访问它,不存在不安全的因素,这时推荐使用StringBuilder。如果一个实例变量是在类里面定义的,并且在多线程

          下会访问到,这时最好使用StringBuffer

           为了更好的理解StringBuffer和StringBuilder的效率问题,请看下面的例子:

    package com.xiaohao.test;
     
    public class Test2 {
        public static void main(String[] args) {
            String str=new String();
            StringBuffer sb1=new StringBuffer();
            StringBuilder sb2=new StringBuilder();
            long startTime=System.currentTimeMillis();
            for(int i=0;i<100000;i++)
            {
              str=str+i;
            }
            long endTime=System.currentTimeMillis();
            System.out.println("Stirng消耗的时间为:"+(endTime-startTime));
             
            startTime=System.currentTimeMillis();
            for(int i=0;i<100000;i++)
            {
                sb2.append(i);
            }
            endTime=System.currentTimeMillis();
            System.out.println("StirngBuilder消耗的时间为:"+(endTime-startTime)); 
             
            startTime=System.currentTimeMillis();
            for(int i=0;i<100000;i++)
            {
               sb1.append(i);
            }
           endTime=System.currentTimeMillis();
           System.out.println("StirngBuffer消耗的时间为:"+(endTime-startTime)); 
            
     
       }
    }

    运行结果为:

        Stirng消耗的时间为:42185
        StirngBuilder消耗的时间为:0
        StirngBuffer消耗的时间为:0

        相关效率,你懂的~~~ 

       另外StringBuffer和StringBuilder没有重写equals和hashcode方法,它们在存储在java集合框架的时候可能出现问题。

    参考资料:http://www.cnblogs.com/xiohao/p/4271140.html

    可以用来计时:纳秒级别的

    long t3 = System.nanoTime()

    2018年8月25日 09:10:13

    2019年6月26日 09:08:52

    2020年5月1日 10:00:03

  • 相关阅读:
    如何使用SAP Intelligent Robotic Process Automation自动操作Excel
    OpenSAML 使用引导 IV: 安全特性
    Spring Cloud Zuul 网关使用与 OAuth2.0 认证授权服务
    微服务架构集大成者—Spring Cloud (转载)
    Spring Cloud Eureka 服务注册列表显示 IP 配置问题
    使用 Notification API 开启浏览器桌面提醒
    SignalR 中使用 MessagePack 序列化提高 WebSocket 通信性能
    配置 Nginx 的目录浏览功能
    关于 Nginx 配置 WebSocket 400 问题
    Migrate from ASP.NET Core 2.0 to 2.1
  • 原文地址:https://www.cnblogs.com/lukelook/p/7832605.html
Copyright © 2011-2022 走看看