运维开发网

《Java架构筑基》从Java基础讲起——String类深入理解

运维开发网 https://www.qedev.com 2020-01-26 07:25 出处:网络 作者:未来可期_
一.String问题答疑String字符串是如何设计与实现考量的?String字符串缓存intern()方法,由永久代移到堆中。String的演化,Java9中底层把char数组换成了byte数组,占用更少的空间二.String的创建机理由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查

一. String问题答疑

  • String字符串是如何设计与实现考量的?
  • String字符串缓存 intern()方法,由永久代移到堆中。
  • String 的演化,Java 9 中底层把 char 数组换成了 byte 数组,占用更少的空间

二. String的创建机理

由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串池的,而是直接在堆区或栈区创建一个新的对象,也不会把对象放入池中。上述原则只适用于通过直接量给String对象引用赋值的情况。博客

举例:String str1 = "123"; //通过直接量赋值方式,放入字符串常量池
String str2 = new String(“123”);//通过new方式赋值方式,不放入字符串常量池

注意:String提供了inter()方法。调用该方法时,如果常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并且返回此池中对象的引用。

三. StringBuffer/StringBuilder

StringBuffer和StringBuilder都实现了AbstractStringBuilder抽象类,拥有几乎一致对外提供的调用接口;其底层在内存中的存储方式与String相同,都是以一个有序的字符序列(char类型的数组)进行存储,不同点是StringBuffer/StringBuilder对象的值是可以改变的,并且值改变以后,对象引用不会发生改变;两者对象在构造过程中,首先按照默认大小申请一个字符数组,由于会不断加入新数据,当超过默认大小后,会创建一个更大的数组,并将原先的数组内容复制过来,再丢弃旧的数组。因此,对于较大对象的扩容会涉及大量的内存复制操作,如果能够预先评估大小,可提升性能。

四. String类的考点分析

  • 通过 String 和相关类,考察基本的线程安全设计与实现,各种基础编程实践。
  • 考察 JVM 对象缓存机制的理解以及如何良好地使用。
  • 考察 JVM 优化 Java 代码的一些技巧。
  • String 相关类的演进,比如 Java 9 中实现的巨大...

五. String技术点深入分析

5.1 String类是典型的Immutable类

是典型的 Immutable 类,被声明成为 final class,所有属性也都是 final 的。也由于它的不可变,类似拼接、裁剪字符串等动作,都会产生新的 String 对象。

5.2 字符串设计和实现考量

  • String 是 Immutable 类的典型实现,原生的保证了基础线程安全,因为你无法对它内部数据进行任何修改,这种便利甚至体现在拷贝构造函数中,由于不可变,Immutable 对象在拷贝时不需要额外复制数据。
  • 为了实现修改字符序列的目的,StringBuffer 和 StringBuilder 底层都是利用可修改的(char,JDK 9 以后是 byte)数组,二者都继承了 AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了 synchronized。
  • 这个内部数组应该创建成多大的呢?如果太小,拼接的时候可能要重新创建足够大的数组;如果太大,又会浪费空间。目前的实现是,构建时初始字符串长度加 16(这意味着,如果没有构建对象时输入最初的字符串,那么初始值就是 16)。我们如果确定拼接会发生非常多次,而且大概是可预计的,那么就可以指定合适的大小,避免很多次扩容的开销。扩容会产生多重开销,因为要抛弃原有数组,创建新的(可以简单认为是倍数)数组,还要进行arraycopy。

5.3 字符串缓存

  • String 在 Java 6 以后提供了 intern()方法,目的是提示 JVM 把相应字符串缓存起来,以备重复使用。在我们创建字符串对象并调用 intern() 方法的时候,如果已经有缓存的字符串,就会返回缓存里的实例,否则将其缓存起来。
  • 在后续版本中,这个缓存被放置在堆中,这样就极大避免了永久代占满的问题,甚至永久代在 JDK 8 中被 MetaSpace(元数据区)替代了。而且,默认缓存大小也在不断地扩大中,从最初的 1009,到 7u40 以后被修改为 60013。

六. String不可变的好处

6.1 可以缓存 hash 值 博客

因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

6.2 String Pool 的需要

如果一个String对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。

6.3 安全性

String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。

6.4 线程安全

String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

0

精彩评论

暂无评论...
验证码 换一张
取 消