String类表示字符串,所有类似"abc"形式的字符串(或魔法字符串)都被看作是这个类的实例。String是不可变的,当一个字符串在常量池中被创建时,他的值就不会被改变。
不可变类指的是其实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并且在对象的整个生命周期内固定不变。为了使类不可变,要遵循下面五条规则:
1. 不要提供任何会修改对象状态的方法。
2. 保证类不会被扩展。 一般的做法是让这个类称为 final 的,防止子类化,破坏该类的不可变行为。
3. 使所有的域都是 final 的。
4. 使所有的域都成为私有的。 防止客户端获得访问被域引用的可变对象的权限,并防止客户端直接修改这些对象。
5. 确保对于任何可变性组件的互斥访问。 如果类具有指向可变对象的域,则必须确保该类的客户端无法获得指向这些对象的引用。
public final class String implements java.io.Serializable, Comparable, CharSequence
所在路径:\java\lang\String.java
一、成员变量
为了保证String类是一个不可变类,String类的成员变量多为私有和不可变的。
/** 存储String对象的值 */
private final char value[];
/** hashcode */
private int hash;
/** serialVersionUID */
private static final long serialVersionUID = -6849794470754667710L;
/** 序列化时使用,表明需要序列化的部分 */
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
其中serialPersistentFields在序列化时使用:
For class that implements Serializable interface there are 2 ways to define what specific fields get streamed during the serialization:
1、By default all non-static, non-transient fields that implement Serializable are preserved.
2、By definning ObjectStreamField [] serialPersistentFields and explicitly declaring the specific fields saved.
The 'advantage' is that it does what it says in the Javadoc: defines which fields are serialized. Without it, all non-transient non-static fields are serialized. Your choice.
二、构造器
JDK8的String类一共有16个构造器,其中两个是@Deprecated,一个是私有构造器,剩下的13个是可以调用的。
1、无参构造器
无参构造器直接将""的value赋值给当前类的value。""的value是一个空的char[],其length为0。调用使用了无参构造器的String对象的isEmpty()方法会得到true,调用length()方法会得到0,判断其==null会得到false。
public String() {
this.value = "".value;
}
2、入参为String对象
入参为String对象时,构造器会对其进行直接取值。
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
3、入参为char[]
入参为字符串数组时,构造器调用的是Arrays.copyOf()方法。
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
其中Arrays.copyOf()方法是为了将入参的字符串序列深拷贝到this.valuie中,他的源码为:
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
其中System.arraycopy()方法的源码为:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
String和char[]的相互转换:
https://www.cnblogs.com/rrttp/p/7922202.html
4、入参为char[],偏移量和有效长度
这个方法支持直接传入想要生成的String的母串,通过偏移量和有效长度找出需要赋值给this.value的部分,然后调用Arrays.copyOfRange()方法进行深拷贝。
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
5、其他
当需要将一个Unicode编码序列转换为String时,可以使用以下构造器:
public String(int[] codePoints, int offset, int count)
参考:https://blog.csdn.net/qq_39477410/article/details/82469104
当需要将一个bytes[]转换为String时,可以使用以下构造器:
// 可以传入JDK支持的字符集名称
public String(byte bytes[], int offset, int length, String charsetName)
throws UnsupportedEncodingException
// 也可以传入自定义的解码字符集
public String(byte bytes[], int offset, int length, Charset charset)
此外,还有一些将上述构造器进一步封装的构造器,其本质都是简化入参。另外,String类的构造器同样支持传入StringBuffer和StringBuilder。如果传入的是StringBuffer,构造器会为其加锁。如果传入的是StringBuilder则不会加锁。
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
事实上,StringBuffer和StringBuilder的toString()方法调用的也是String类的构造器,他们最终的底层实现都是Arrays.copyOf()。
// StringBuffer的toString()方法
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
// StringBuilder的toString()方法
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
最后,String类还提供了一个保护类型的构造方法。该方法相比入参为char[]的构造器多了一个share参数,这个参数并没有实际作用,只是用来和其他构造器进行区分。当String类内部调用该构造器时:
- 直接赋值而不是使用Arrays.copyOf()方法可以获得更高的效率;
- 同时共享字符串数组的方式也可以节省内存。
该构造器不能对外暴露的原因是需要保持String类的不可变性。
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}