源码实现

它是典型的 Immutable 类,被声明成为 final class,所有属性也都是 final 的。也由于它的不可变性,类似拼接、裁剪字符串等动作,都会 产生新的 String 对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能有明显影响。

image.png

StringBuffer/StringBuilder 的区别

为了实现修改字符序列的目的,StringBuffer 和 StringBuilder 底层都是利用可修改的(char,JDK 9 以后是 byte)数组,二者都继承了 AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了 synchronized。
image.png
image.png

内部数组长度

构建时初始字符串长度加 16(这意味着,如果没有构建对象时输入最初的字符串,那么初始值就是 16)

image.png image.png

== 与 equals

image.png

字符串常量池

位于方法区,详情可参见(不同 Java 版本有差异?没仔细证实

🎤 JVM 运行时内存结构

由于 String 在 Java 世界中使用过于频繁,Java 为了避免在一个系统中产生大量的 String 对象,引入了字符串常量池

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

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

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

Java 中的 String 长度最大是多少

详情参见 :Java 中 String 的最大长度

这个问题要分两个阶段看,分别是 编译期运行期。不同的时期限制不一样。

编译器

我们在编写源代码的时候,经常会使用 String s = “abc” 这种方式去声明字符串。这种用字面量声明的字符串在class字节码文件中是以 CONSTANT_urf8_info 格式存储的。

CONSTANT_urf8_info 结构如下:
carbon (4).png

其中 u2 length 表示这个表中字节数组的最大长度,也就是说 bytes 的最大长度是 2 的 16 次方:65536。因此一个字符串最大长度也就是 u2 所能代表的最大值 65536 个,但是需要使用 2 个字节来保存 null 值,因此一个字符串的最大长度为 65536 - 2 = 65534 个字节 。

注意: 上述解释中说的是字符串最大长度为 65534 个 字节,并不代表一个字符串中就可以保存 65534 个字符。因为在 utf-8 编码下,一个数字和一个英文字母占一个字节,但是一个汉字却可以占用 2~4 个字节。因此如果使用字面量的方式声明中文字符串的长度会远远小于 65534。

运行期

上面提到的这种 String 长度的限制是编译期的限制,也就是使用String s= “”; 这种字面量方式定义的时候才会有的限制。

那么,String 在运行期有没有限制呢,答案是 有的。先看下 String.java 的源码:
image.png

Java 9 之前使用的是 cahr 数组

那么其最大长度便受到数组长度的限制。