美文网首页
关于Java核心类String的几个问题

关于Java核心类String的几个问题

作者: chenzg8080 | 来源:发表于2017-09-19 10:10 被阅读0次

    1. 前言

    最近看到几个有趣的关于Java核心类String的问题。

    String类是如何实现其不可变的特性的,设计成不可变的好处在哪里。

    为什么不推荐使用+号的方式去形成新的字符串,推荐使用StringBuilder或者StringBuffer呢。

    2. String类是如何实现不可变的

    String类的一大特点,就是使用Final类修饰符。

    A class can be declared final if its definition is complete and no subclasses are desired or required.

    Because a final class never has any subclasses, the methods of a final class are never overridden .

    Java SE 7 官方手册中的定义如上,如果你认为这个类已经定义完全并且不需要任何子类的话,可以将这个类声明为Final,Final类中的方法将永远不会被重写。

    在Java中,String是被设计成一个不可变(immutable)类,一旦创建完后,字符串本身是无法通过正常手段被修改的。

    选了substring方法来做一个代表,其他常见的涉及String操作的方法都是类似,如果你操作后的内容会和目前String中的内容不一致的话,那么都是重新创建一个新的String类返还,不会让你去修改内部的内容。

    将String类设计成Final类,能够避免其方法被子类重写,从而破坏了它本身方法的实现,进而破坏了不可变的特性。

    2.1 String类设计成不可变的好处

    我们都不是Java语言的设计者,不知道其为何一定要设计成不可变,试着做一些猜想。

    1.可以实现多个变量引用JVM内存中的同一个字符串实例。见后文String Pool的介绍。

    2.安全性,String类的用途实在太广了,如果可以随意修改的,是不是很恐怖。

    3.性能,String大量运用在哈希的处理中,由于String的不可变性,可以只计算一次哈希值,然后缓存在内部,后续直接取就好了。如果String类是可变的话,在进行哈希处理的时候,需要进行大量的哈希值的重新计算。

    2.2 String Pool

    上文说了,设计成不可变后,可以多个变量引用JVM上同一块地址,可以节省内存空间,相同的字符串不用重复占用Heap区域空间。

    String test1 ="abc";

    String test2 ="abc";

    通常我们平时在使用字符串是,都是通过这种方式使用,那么JVM中的大致存储就是如下图所示。

    两个变量同时引用了String Pool中的abc,如果String类是可变的话,也就不能存在String Pool这样的设计了。 在平时我们还会通过new关键字来生成String,那么新创建的String是否也会和上文中的示例一样共享同一个字符串地址呢。

    String test1 ="abc";

    String test2 ="abc";

    String test3 =newString("abc");

    答案是不会,使用new关键字会在堆区在创建出一个字符串,所以使用new来创建字符串还是很浪费内存的,内存结构如下图所示。

    2.3 不推荐使用+来拼装字符串的原因。

    首先我们来看这一段代码,应该是之前写代码比较常见的。

    String test1 ="abc";

    String test2 ="abc";

    String test3 = test1 + test2;

    test3通过test1和test2拼接而成,我们看一下这个过程中的字节码。

    从以上图我们可以看到,目前的JDK7的做法是,会通过新建StringBuilder的方式来完成这个+号的操作。这是目前的一个底层字节码的实现,那么是不是没有使用StringBuilder或者StringBuffer的必要了呢。还是有的,看下一个例子。

    String test2 ="abc";

    String test3 ="abc";

    for(inti =0; i <5; i++) {

    test3 += test2;

    }

    在上述代码中,我们还是使用+号进行拼接,但这次我们加了一个循环,看一下字节码有什么变化。

    每次循环都会创建一个StringBuilder,在末尾再调用toString返还回去,效率很低。继续看下一个例子,我们直接使用StringBuilder,来做拼接。

    String test2 ="abc";

    // 使用StringBuilder进行拼接

    StringBuilder test4 =newStringBuilder("abc");

    for(inti =0; i <5; i++) {

    test4.append(test2);

    }

    每次循环体中只会调用之前创建的StringBuilder的append方法进行拼接,效率大大提高。

    3. 总结

    本文主要探讨了String类设计为Final修饰和不可变类的原因,以及为何在日常工作中不推荐使用+号进行字符串拼接。

    相关文章

      网友评论

          本文标题:关于Java核心类String的几个问题

          本文链接:https://www.haomeiwen.com/subject/znqwsxtx.html