写点什么

String、StringBuiler 和 StringBuffer 面试那些事

用户头像
Java旅程
关注
发布于: 1 小时前
String、StringBuiler和StringBuffer面试那些事

String 应该是面试中被考烂的问题,但是不知道为什么还总是有面试官喜欢问。下面就从面试常问的几个问题出发,来深入理解下 String。

String、StringBuiler 和 StringBuffer 有什么区别?

这个问题主要想问的是你对这三个类的理解,这里可以不用说原理,诱导面试官继续问下去.


  1. String 类是不可变的,而 StringBuiler 和 StringBuffer 类是可变的;

  2. StringBuiler 是线程不安全的,StringBuffer 是线程安全的。


到这里,你已经给自己挖了一些坑了,填了这些坑不出意外面试的节奏也已经掌握到你的手里了。

为什么说 String 类是不可变的?

public final class String    implements java.io.Serializable, Comparable<String>, CharSequence {    /** The value is used for character storage. */    private final char value[];    ...}
复制代码


  1. String 类是 final 类,也即意味着 String 类不能被继承,并且它的成员方法都默认为 final 方法;

  2. 用于保存值的数组也是被 private final 修饰的,说明它也是不可变的。


由于这两个原因,一旦 String 被创建那就肯定是不可变的。

那为什么下面的字符串 s 还是改变了?

    public static void main(String[] args) {        String s = "abc";        s += "12345";
System.out.println(s); // abc12345 }
复制代码


对上面的代码使用 javap 命令进行反编译:


  public static void main(java.lang.String[]);    Code:       0: ldc           #2                  // String abc       2: astore_1       3: new           #3                  // class java/lang/StringBuilder       6: dup       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V      10: aload_1      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;      14: ldc           #6                  // String 12345      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;      22: astore_1      23: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;      26: aload_1      27: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V      30: return
复制代码


对于+这种方式的拼接的底层其实是使用了 StringBuiler,先初始化一个 StringBuilder 对象,然后使用 append()方法拼接,最后使用 toString()方法得到结果。相当于如下代码:


    public static void main(String[] args) {        String s = "abc";        StringBuilder sb = new StringBuilder();        sb.append(s);        sb.append("12345");        s = sb.toString();        System.out.println(s);    }
复制代码


查看StringBuilder.toString()方法,你会发现重新创建了一个 String 对象,所以其实是将新的引用指向了 s,而不是 s 本身发生了改变。

那么将 String 类设计成不可变的意义是什么?

安全性:String 被许多 Java 类用作参数,比如 URI、反射、文件等,这就需要字符串不可变来保证多线程安全,而不用再写复杂的代码来保证。

JVM 如何优化字符串频繁创建的问题?

字符串对象需要消耗大量资源,因此 JVM 为了提高性能和减少内存的开销,提出了使用字符串常量池进行优化。在创建 String 对象时,判断字符串是否在字符串常量池中,如果在,则直接返回常量池中的引用。


由此,可以看出下面这个结果是 true:


public static void main(String[] args) {    String s1 = "abc";    String s2 = "abc";
System.out.println(s1 == s2); // true}
复制代码


扩展,下面的代码共创建几个实例?


String s1 = new String("abc");String s2 = "abc";String s3 = new String("abc");
复制代码


答案是 3 个,可以通过 jvisualvm 查看



通过 new String()的方式会创建 1 个或 2 个,而使用字符串创建的方式则可能是 0 个。第一行创建两个对象,堆中创建了一个 String 对象,字符串常量池创建了一个。第二行发现常量池中已经有"abc"这个字符串对象实例,直接返回。第三行,在堆中又创建一个 String 对象。

StringBuffer 如何保证线程安全?

StringBuffer的方法上都被加上了synchronized关键字。关于synchronized会再说到。

总结

String 是日常开发中最常用到的类,设计者也更加慎重。所以学习这些基础的类,也能有所收获,这也是面试官乐此不疲的原因吧。关于 String 其实还有很多内容可以说,比如空指针异常,在日常项目中很多空指针异常都来源于字符串操作上,所以在有些开发规范中也强制要求使用 StringUtils 工具类来操作。

提问

你觉得 String 还有什么可以优化的地方?


成长在于一点一滴的积累,而我在路上。欢迎大家关注和点赞。



发布于: 1 小时前阅读数: 5
用户头像

Java旅程

关注

还未添加个人签名 2018.07.18 加入

还未添加个人简介

评论

发布
暂无评论
String、StringBuiler和StringBuffer面试那些事