前言回顾
java 中的8种基本数据类型。其中, char 是字符类型,占2个字节16位,默认值是 ‘\u0000’ 。用 char 类型的变量来表示单个字符,用单引号引起来。
多个字符按照一定的顺序组成的列表就叫作字符序列
String
——实现了 CharSequence 接口
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence, Constable,
ConstantDesc{ }
字符串是Java中表示字符序列的类。
字符串是不可变的,一旦创建就不能被修改。
字符串的表示
字符串java.lang.String
可以用来表示多个字符,用""
引起来。字符串是引用数据类
型,默认值是 null
。
//字符串声明
String str = "我是一个字符串"; // 字面量 比较常用
String str1 = new String("我是一个字符串对象"); // 字符串对象
JDK13 新增文本块(预览),在 JDK15 正式在字符串支持文本块
,用 """
表示。
// Text Block 文本块声明
// 注意第一个"""后面不能写文本,需从下一行开始写
String season = """
winter""";
String类
public final class String
是用 final
修饰的类,在 Java 中用 final
修饰的类不能被继承,也就是说 String
没有子类。
** 回顾:**
在 Java 中,关键字 final 表示最终的。可以用来修饰类、方法、变量。
- 被 final 修饰的类,不能被继承
- 被 final 修饰的方法,不能被重写
- 被 final 修饰的变量,不能变。如果是基本数据类型不能重新赋值,如果是引用数据类型不
能改变引用。
字符串具有不可变特性
jdk 1.8及之前版本:
private final char value[]; // 用于存放内容,字符串底层其实就是一个字符数组
private int hash; // 用来存放hash值
jdk1.9及以后版本:
private final byte value[]; // 用于存放内容,1.9之后使用字节数组存储
private int hash; // 用来存放hash值
private byte coder; // 编码方式,0 -LATIN1, 1 - UTF-16
从中我们发现,不管是 jdk 8 及以前还是 jdk 9 及以后版本, value 属性都是 final 的。
就说明,字符串一旦声明就不可变。所以字符串拥有不可变性。
为什么要这么改呢?
主要是为了节约 String 占用的内存,占用内存少引发另外一个好处就是减少 GC 次数。
众所周知,在大多数 Java 程序的堆里, String 占用的空间最大,并且绝大多数 String只有 Latin-1 字符,这些 Latin-1 字符只需要1个字节就够了。 JDK9 之前, JVM 因为 String 使用 char 数组存储,每个 char 占2个字节,所以即使字符串只需要1字节/字符,它也要按照2字节/字符进行分配,浪费了一半的内存空间。
JDK9 是怎么解决这个问题的呢?一个字符串出来的时候判断,它是不是只有 Latin-1 字符,如果是,就按照1字节/字符的规格进行分配内存,如果不是,就按照2字节/字符的规格进行分配( UTF-16 编码),提高了内存使用率。
为什么用 UTF-16 而不用 UTF-8 呢?
UTF-8
实际上是对空间利用效率最高的编码集,它是不定长的,可以最大限度利用内存和网络。但是这种编码集只能用于传输和存储,并不适合拿来做 String 的底层实现。因为 String 有随机访问的方法,所谓随机访问,就是 charAt 、 subString 这种方法。如果字符串中的每个字符占用的内存是不定长的,那么进行随机访问的时候,就需要从头开始数每个字符的长度,才能找到你想要的字符。
使用 UTF-16 编码
将它们表示并存储为每个字符2个字节
。
UTF-16 采用固定长度的编码单元(16位),这使得在访问字符串的任何部分时可以在 O(1) 时间内完成。而 UTF-8 使用可变长度编码单元,因此访问字符串中的字符可能需要 O(n) 时间,其中 n 是字符的位置。
对于 JDK9 和更高版本,默认情况下,如果 String 的行为已更改为对字符串使用紧凑表示形式。 java 命令文档现在这样说
-XX:-CompactStrings
禁用紧凑字符串功能。 默认情况下,启用此选项。 启用此选项后,内部仅包含单字节字符的 Java 字符串将使用 ISO-8859-1 / Latin-1
编码在内部表示并存储为每个字符的单字节字符串。 这将只包含单字节字符的字符串减少了50%的空间。 对于包含至少一个多字节字符的 Java 字符串:使用 UTF-16 编码将它们表示并存储为每个字符2个字节。 禁用紧凑字符串功能会强制使用 UTF-16 编码作为所有 Java 字符串的内部表示。
需要注意的是,在 Java 9 之后,Java 引入了一些新的 API,如 String::codePoints 和String::getBytes 等,它们允许开发人员以不同的字符编码方式(包括 UTF-8)来处理字符串。这些 API 提供了更多的灵活性,以满足不同的需求。