• 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

  • 相关阅读:
    深度学习大牛Yoshua Bengio
    mysql select简单用法
    CF 191 div2
    MySQL 讨厌哪种类型的查询
    Python 中的list小结
    定时器常用的两种工作方式及编程要点
    Linux下which、whereis、locate、find 区别
    Dalvik虚拟机的优化机制
    [leetcode]Partition List
    tomcat:Could not publish to the server. java.lang.IndexOutOfBoundsException
  • 原文地址:https://www.cnblogs.com/lukelook/p/7832605.html
Copyright © 2020-2023  润新知