zoukankan      html  css  js  c++  java
  • Java基础 String,StringBuilder,StringBuffer三者的区别

    总结

    String 和 StringBuffer、StringBuilder 的区别

    • 在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

    StringBuffer 和 StringBuilder 最大的区别

    • 在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

    ----------------------------------------------------------

    !!!必看!!! 常见的关于String、StringBuffer的面试题

    下面是一些常见的关于String、StringBuffer的一些面试笔试题,若有不正之处,请谅解和批评指正。

    1. 下面这段代码的输出结果是什么?

      String a = "hello2";   String b = "hello" + 2;   System.out.println((a == b));

      输出结果为:true。原因很简单,"hello"+2在编译期间就已经被优化成"hello2",因此在运行期间,变量a和变量b指向的是同一个对象。

    2.下面这段代码的输出结果是什么?

      String a = "hello2";    String b = "hello";       String c = b + 2;       System.out.println((a == c));

      输出结果为:false。由于有符号引用的存在,所以  String c = b + 2;不会在编译期间被优化,不会把b+2当做字面常量来处理的,因此这种方式生成的对象c事实上是保存在堆上的,a保存在运行时常量池。因此a和c指向的并不是同一个对象。javap -c得到的内容:

      

    3.下面这段代码的输出结果是什么?

      String a = "hello2";     final String b = "hello";       String c = b + 2;       System.out.println((a == c));

      输出结果为:true。对于被final修饰的变量,会在class文件常量池中保存一个副本,也就是说不会通过连接而进行访问,对final变量的访问在编译期间都会直接被替代为真实的值。那么String c = b + 2;在编译期间就会被优化成:String c = "hello" + 2; 下图是javap -c的内容:

      

    4.下面这段代码输出结果为:

    public class Main {
        public static void main(String[] args) {
            String a = "hello2";
            final String b = getHello();
            String c = b + 2;
            System.out.println((a == c));
        }
         
        public static String getHello() {
            return "hello";
        }
    }

    输出结果为false。这里面虽然将b用final修饰了,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定,因此a和c指向的不是同一个对象。

    5.下面这段代码的输出结果是什么?

    public class Main {
        public static void main(String[] args) {
            String a = "hello";
            String b =  new String("hello");
            String c =  new String("hello");
            String d = b.intern();
             
            System.out.println(a==b);
            System.out.println(b==c);
            System.out.println(b==d);
            System.out.println(a==d);
        }
    }

    输出结果为(JDK版本 JDK6):

      

      这里面涉及到的是String.intern方法的使用。在String类中,intern方法是一个本地方法,在JAVA SE6之前,intern方法会在运行时常量池中查找是否存在内容相同的字符串,如果存在则返回指向该字符串的引用,如果不存在,则会将该字符串入池,并返回一个指向该字符串的引用。因此,a和d指向的是同一个对象。

    6.String str = new String("abc")创建了多少个对象?

      这个问题在很多书籍上都有说到比如《Java程序员面试宝典》,包括很多国内大公司笔试面试题都会遇到,大部分网上流传的以及一些面试书籍上都说是2个对象,这种说法是片面的。

      如果有不懂得地方可以参考这篇帖子:

      http://rednaxelafx.iteye.com/blog/774673/

      首先必须弄清楚创建对象的含义,创建是什么时候创建的?这段代码在运行期间会创建2个对象么?毫无疑问不可能,用javap -c反编译即可得到JVM执行的字节码内容:

      

      很显然,new只调用了一次,也就是说只创建了一个对象。

      而这道题目让人混淆的地方就是这里,这段代码在运行期间确实只创建了一个对象,即在堆上创建了"abc"对象。而为什么大家都在说是2个对象呢,这里面要澄清一个概念  该段代码执行过程和类的加载过程是有区别的。在类加载的过程中,确实在运行时常量池中创建了一个"abc"对象,而在代码执行过程中确实只创建了一个String对象。

      因此,这个问题如果换成 String str = new String("abc")涉及到几个String对象?合理的解释是2个。

      个人觉得在面试的时候如果遇到这个问题,可以向面试官询问清楚”是这段代码执行过程中创建了多少个对象还是涉及到多少个对象“再根据具体的来进行回答。

    8. str2中的“love”+"java"会被优化么?答案:会 (JDK 1.8)

    public class Main {
        public static void main(String[] args) {
            String str1 = "I";
            String str2 = str1 + "love" + "java";   
        }
    }

    -----------------------------------------------------------

    一.你了解String类吗?

    想要了解一个类,最好的办法就是看这个类的实现源代码,String类的实现在 \jdk1.6.0_14\src\java\lang\String.java   文件中。打开这个类文件就会发现String类是被final修饰的:

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence
    {
        /** The value is used for character storage. */
        private final char value[];
     
        /** The offset is the first index of the storage that is used. */
        private final int offset;
     
        /** The count is the number of characters in the String. */
        private final int count;
     
        /** Cache the hash code for the string */
        private int hash; // Default to 0
     
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L;
     
        ......
     
    }

    从上面可以看出几点:

      1)String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。

      2)上面列举出了String类中所有的成员属性,从上面可以看出String类其实是通过char数组来保存字符串的。

      3)无论是sub操作、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。

      在这里要永远记住一点:

      “对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。

    二.深入理解String、StringBuffer、StringBuilder

    1.String str="hello world"和String str=new String("hello world")的区别

      想必大家对上面2个语句都不陌生,在平时写代码的过程中也经常遇到,那么它们到底有什么区别和联系呢?下面先看几个例子:

    public class Main {
             
        public static void main(String[] args) {
            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);
            System.out.println(str1==str3);
            System.out.println(str2==str4);
        }
    }

    这段代码的输出结果为

      

    为什么会出现这样的结果?下面解释一下原因:

    1)在前面一篇讲解关于JVM内存机制的一篇博文中提到 ,在class文件中有一部分 来存储编译期间生成的 字面常量以及符号引用,这部分叫做class文件常量池,在运行期间对应着方法区的运行时常量池。

    2)因此在上述代码中,String str1 = "hello world";和String str3 = "hello world"; 都在编译期间生成了 字面常量和符号引用,运行期间字面常量"hello world"被存储在运行时常量池(当然只保存了一份)。通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。

    3)总所周知,通new关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过new来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。

    三.不同场景下三个类的性能测试

    一些验证实例代码, 在这里查看:https://www.cnblogs.com/dolphin0520/p/3778589.html

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

      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。

  • 相关阅读:
    VS2008编写MFC程序--使用opencv2.4()
    November 02nd, 2017 Week 44th Thursday
    November 01st, 2017 Week 44th Wednesday
    October 31st, 2017 Week 44th Tuesday
    October 30th, 2017 Week 44th Monday
    October 29th, 2017 Week 44th Sunday
    October 28th, 2017 Week 43rd Saturday
    October 27th, 2017 Week 43rd Friday
    October 26th, 2017 Week 43rd Thursday
    October 25th, 2017 Week 43rd Wednesday
  • 原文地址:https://www.cnblogs.com/frankcui/p/10678245.html
Copyright © 2011-2022 走看看