String类
String的基本特性
- 不可变性: String 对象一旦创建就不能被修改,所有看似修改的操作实际上都是创建新的 String 对象
- final类: String 类被声明为 final,不能被继承
- 基于字符数组: 内部使用
final char value[]
存储字符数据(Java9以后改为byte[]
+ 编码标记)
重要源码分析
关键字段
直接上源码:
/*** The value is used for character storage.** @implNote This field is trusted by the VM, and is a subject to* constant folding if String instance is constant. Overwriting this* field after construction will cause problems.** Additionally, it is marked with {@link Stable} to trust the contents* of the array. No other facility in JDK provides this functionality (yet).* {@link Stable} is safe here, because value is never null.*//**
该字段用于字符存储。实现说明:该字段被虚拟机(VM)信任,如果String实例是常量,它会成为常量折叠的优化对象。
在构造后覆盖此字段会导致问题。此外,该字段标记了@Stable注解以信任数组内容。目前JDK中还没有其他设施提供此功能。
在此处使用@Stable是安全的,因为value永远不会为null。
*/@Stable
private final byte[] value;/*** The identifier of the encoding used to encode the bytes in* {@code value}. The supported values in this implementation are** LATIN1* UTF16** @implNote This field is trusted by the VM, and is a subject to* constant folding if String instance is constant. Overwriting this* field after construction will cause problems.*//**
用于编码value字节的编码标识符。本实现中支持的值为:
LATIN1
UTF16实现说明:该字段被虚拟机(VM)信任,如果String实例是常量,它会成为常量折叠的优化对象。
在构造后覆盖此字段会导致问题。
*/private final byte coder;
-
存储机制: 从Java9开始,String内部使用
byte[]
而不是char[]
存储字符数据,这是为了支持紧凑型字符串- Java9之前使用
char[]
(UTF-16编码,每个字符固定2字节) - 实际大部分业务字符串仅含Latin-1字符
byte[]
可以根据内容动态选择编码- Latin-1:单字节存储ASCII字符(0~255)
- UTF-16: 双字节存储扩展字符(如中文)
- 性能对比:
//Java 8 (char[]) "Hello" 存储:10字节 (5 * 2字节)//Java 9+ (byte[] + Latin-1) "Hello" 存储:5字节 + 1字节(coder标记) //coder标记下文会解释
- 性能权衡:
- 访问字符时需要条件判断(检查coder值)
- 内存节省的收益远大于条件判断的损耗
- Java9之前使用
-
关于
value
字段的注解:@Stable:- JDK内部注解
- 表示字段引用及其内容在初始化后永远不变
- 比常规final更强的不变性保证:
- 普通fianl只保证引用不变
- @Stable还保证数组元素不变
-
final修饰符解析
- 修饰目标: 此处修饰的是
byte[] value
的引用(非数组内容) - 保证引用不可变(不可指向其他数组)
- 举例:
final byte[] value = new byte[10]; value = new byte[20]; //编译错误(引用不可变) value[0] = 1; //正确(数组内容可变性不由final决定)
- 与String不可变性的关系
- final是基础保障,但不是充分条件
- 完整的不可变性需要:
- 私有字段(private)
- 不暴露内部数组(如toCharArray()返回副本)
- 所有方法不修改数组内容
- 修饰目标: 此处修饰的是
常用方法实现
equals,substring
equals()方法
源码解析:
/*** Compares this string to the specified object. The result is {@code* true} if and only if the argument is not {@code null} and is a {@code* String} object that represents the same sequence of characters as this* object.** <p>For finer-grained String comparison, refer to* {@link java.text.Collator}.** @param anObject* The object to compare this {@code String} against** @return {@code true} if the given object represents a {@code String}* equivalent to this string, {@code false} otherwise** @see #compareTo(String)* @see #equalsIgnoreCase(String)*//*** 将此字符串与指定对象进行比较。当且仅当参数不为 null 且是表示相同字符序列的 String 对象时,结果为 true。* * 对于更精细的字符串比较,请参考 java.text.Collator。* * @param anObject 要与此字符串比较的对象* * @return 如果给定对象表示与此字符串等效的 String,则返回 true,否则返回 false* * @see #compareTo(String)* @see #equalsIgnoreCase(String)*/public boolean equals(Object anObject) {if (this == anObject) {return true;}return (anObject instanceof String aString)&& (!COMPACT_STRINGS || this.coder == aString.coder)&& StringLatin1.equals(value, aString.value);
}
- 引用相等检查
public boolean equals(Object anObject) {
if (this == anObject) {return true;
}
- 优化作用: 相同对象直接返回,避免后续计算
- 类型检查与模式匹配
return (anObject instanceof String aString)
- Java16引入的模式匹配语法
- 同时检查类型和转换类型
- 将成功转换的对象赋值给新变量
aString
- 紧凑型字符串兼容性检查
&& (!COMPACT_STRINGS || this.coder == aString.coder)
COMPACT_STRINGS
: 静态final布尔值,表示是否启用紧凑字符串- 如果未启用紧凑字符串特性,则跳过编码检查
- 如果启用,则要求两者的
coder(编码器)
相同
- 核心内容比较
&& StringLatin1.equals(value, aString.value);
实际的比较使用StringLatin1.equals()
,先比较长度,再逐字节比较内容
substring()方法
源码解析:
/*** Returns a string that is a substring of this string. The* substring begins with the character at the specified index and* extends to the end of this string. <p>* Examples:* <blockquote><pre>* "unhappy".substring(2) returns "happy"* "Harbison".substring(3) returns "bison"* "emptiness".substring(9) returns "" (an empty string)* </pre></blockquote>** @param beginIndex the beginning index, inclusive.* @return the specified substring.* @throws IndexOutOfBoundsException if* {@code beginIndex} is negative or larger than the
* length of this {@code String} object.*//*** 返回此字符串的子字符串。子字符串从指定索引处的字符开始,* 延伸到该字符串的末尾。* * 示例:* <blockquote><pre>* "unhappy".substring(2) 返回 "happy"* "Harbison".substring(3) 返回 "bison"* "emptiness".substring(9) 返回 "" (空字符串)* </pre></blockquote>** @param beginIndex 开始索引(包含该字符)* @return 指定的子字符串* @throws IndexOutOfBoundsException 如果 beginIndex 为负数或大于此字符串对象的长度*/public String substring(int beginIndex) {return substring(beginIndex, length());
}
- 方法重载
- 这是一个便捷方法,委托给
substring(int beginIndex, int endIndex)
实现 - 默认
endIndex
为字符串长度length
- 参数处理
-
beginIndex
必须满足0 <= beginIndex <= length()
-
endIndex
自动设置为字符串长度
- 边界情况
- 当
beginIndex == length()
时,返回空字符串 - 当
beginIndex == 0
时,返回原字符串的副本
接着来了解substring(int beginIndex, int endIndex)
的实现原理
/*** Returns a string that is a substring of this string. The* substring begins at the specified {@code beginIndex} and* extends to the character at index {@code endIndex - 1}.* Thus the length of the substring is {@code endIndex-beginIndex}.* Examples:* <blockquote><pre>* "hamburger".substring(4, 8) returns "urge"* "smiles".substring(1, 5) returns "mile"* </pre></blockquote>** @param beginIndex the beginning index, inclusive.* @param endIndex the ending index, exclusive.* @return the specified substring.* @throws IndexOutOfBoundsException if the* {@code beginIndex} is negative, or* {@code endIndex} is larger than the length of* this {@code String} object, or* {@code beginIndex} is larger than* {@code endIndex}.*//*** 返回此字符串的子字符串。子字符串从指定的 beginIndex 开始,* 延伸到索引 endIndex - 1 处的字符。因此子字符串的长度为 endIndex - beginIndex。* * 示例:* <blockquote><pre>* "hamburger".substring(4, 8) 返回 "urge"* "smiles".substring(1, 5) 返回 "mile"* </pre></blockquote>** @param beginIndex 开始索引(包含该字符)* @param endIndex 结束索引(不包含该字符)* @return 指定的子字符串* @throws IndexOutOfBoundsException 如果 beginIndex 为负数,或* endIndex 大于此字符串对象的长度,或* beginIndex 大于 endIndex*/public String substring(int beginIndex, int endIndex) {int length = length();checkBoundsBeginEnd(beginIndex, endIndex, length);if (beginIndex == 0 && endIndex == length) {return this;}int subLen = endIndex - beginIndex;return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen): StringUTF16.newString(value, beginIndex, subLen);
}
- 长度获取
int length = length();
先获取当前字符串长度,避免多次调用
- 边界检查
checkBoundsBeginEnd(beginIndex, endIndex, length);
通过checkBoundsBeginEnd()方法
验证:
beginIndex >= 0
endIndex <= length
beginIndex <= endIndex
如果不满足则抛出IndexOutOfBoundsException
- 完整字符串优化
if (beginIndex == 0 && endIndex == length) {return this;
}
请求整个字符串时直接返回原对象
- 字串长度计算
int subLen = endIndex - beginIndex;
- 判断不同编码创建子串
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen) : StringUTF16.newString(value, beginIndex, subLen);
利用了Java9的紧凑型字符串特性,根据不同编码类型选择不同的创建方式
String不可变性的原理
一、不可变性的本质定义
String 对象一旦创建,其内容永远不可更改,所有看似修改的操作都返回新对象。
二、底层实现机制
- 存储结构锁定
1. // Java 8及以前private final char[] value; // final保证引用不变// Java 9+优化
@Stable
private final byte[] value; // 字节存储+稳定性注解
private final byte coder; // 编码标记
- 无修改接口
所有修改操作都创建新对象:
public String concat(String str) {return new String(...);
}
//不提供任何修改内部数组的方法
三、设计保障措施
- 构造防护
public String(char[] value) {
this.value = Arrays.copyOf(value, value.length); // 防御性拷贝
}
- 访问控制
public char[] toCharArray() {
return Arrays.copyOf(value, length()); // 返回副本而非原数组
}
- 反射防护
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
field.set(str, newValue); // 抛出IllegalAccessException
四、关键技术支撑
- 哈希缓存优化
private int hash; // 首次调用hashCode()时计算并缓存public int hashCode() {int h = hash;if (h == 0 && !hashIsZero) {h = calculateHash();hash = h;}return h;
}
- 字符串常量池
String s2 = new String("abc"); // 堆中新对象
s2.intern(); // 返回常量池引用
String s1 = "abc"; // 常量池对象
- 线程安全保证
- 天然线程安全,无需同步
- 可安全发布(Safe Publication)
五、不可变性的核心价值
优势领域 | 具体表现 |
---|---|
安全性 | 防止敏感数据被篡改 |
性能 | 哈希缓存、常量池复用 |
线程安全 | 无需同步自由共享 |
设计简单性 | 消除状态变化复杂性 |
六、典型应用场景
- 作为HashMap的Key
config.put("timeout", 30); // 依赖哈希缓存
Map<String, Object> config = new HashMap<>();
- 类加载机制
Class.forName("com.example.Test"); // 类名字符串不可变
- 网络通信
URL url = new URL("https://example.com"); // 基础地址安全