1..String字符串类型的数据结构
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
先来了解一下final关键字的作用:
(1)修饰变量:当修饰基本数据类型时,一旦赋予初值就不能再改变;当修饰引用类型时,初始化后不能指向一个新的对象,但是可以改变引用指向的对象内容
(2)修饰方法:被final修饰的方法表示该方法不能被子类重写,即子类不能对final方法进行覆盖。这种设计可以用于确保某些特定的行为不会被改变。
(3)修饰类:被final修饰的类表示该类不能被继承,即不能有子类。这种设计通常用于某些特定的类,例如String类就是一个final类,不能被继承。
不可变的原因:
(1)字符串的本质是字符串数组,在源代码中,value使用final关键字修饰,即当value赋予初值就不能被改变。
(2)String类使用final关键字修饰,也就是说该类不允许继承。避免出现定义一个子类继承重写String的方法,将子类设计成可变对象这种情况的出现。因为在java中,有父类引用指向子类对象这种用法,可能会导致String看起来是可变的。
2.每次修改,都会产生一个新的字符串
每次对String对象进行修改(连接、截取等操作),都会产生一个新的字符串对象,而不是在原字符串对象上进行修改。
这种设计是为了确保字符串对象的线程安全性和不可变性,同时也有利于字符串的缓存和重复利用。下面举一个例子来解释这个过程:
public class demo1 {
public static void main(String[] args) {
String str="但使龙城飞将在";
String newStr=str.substring(0,4);
System.out.println(newStr);
}
}
输出结果:
但使龙城
在这个例子中,str字符串对象的值是“但使龙城飞将在”,调用字符串截取方法substring方法后会产生一个新的字符串对象,值为“但使龙城”,而原始的str字符串对象并没有被修改。这种不可变性体现在源码中:
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
在源代码中,
(1)进行参数检查,确保beginIndex和endIndex在合理范围内。
(2)计算子字符串的长度subLen,即endIndex - beginIndex.
(3)如果 beginIndex
为 0 且 endIndex
为原始字符串的长度,则直接返回原始字符串本身;否则,使用 new String
创建一个新的 String 对象,其值为从 beginIndex
到 endIndex-1
的子字符串。