String和StringBuffer还有StringBuilder之前的区别和特性这里不详细展开,可以自行google。本文主要是讲一些在看源码时的一些发现。
线程安全原理
源码分析一下为什么StringBuffer是线程安全的,而StringBuilder不是。原以为是有缓存或者可重入锁机制,后来发现是在方法层面用了synchronized修饰符。例如经常用的下列方法:
public synchronized StringBuffer append(StringBuffer sb) {
toStringCache = null;
super.append(sb);
return this;
}
性能优化
但是StringBuffer在toString()
方法中有个优化点,就是StringBuffer中有个toStringCache
的缓存数组,它使得当调用过一次toString()
后,如果没有修改过StringBuffer中即将要输出的字符值,再次toString()
时不需要重新从属性value中根据当前字符数据长度将数据拷贝出来,直接用toStringCache
生成String对象。
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
所以多次调用toString()
会比StringBuilder具有更好的性能。因为下列方法只调用一次。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
意外发现
在看String类的源码时,发现一个和序列化相关的成员变量:
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
通过注释可以知道,对于String类型的序列化在流处理中是个特例。那为什么要创建一个长度为零的数据呢?
首先看一下其他类的serialPersistentFields
属性是如何赋值的,下图是ConcurrentHashMap
中的serialPersistentFields
属性:
/** For serialization compatibility. */
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("segments", Segment[].class),
new ObjectStreamField("segmentMask", Integer.TYPE),
new ObjectStreamField("segmentShift", Integer.TYPE)
};
serialPersistentFields是什么
简单说就是一个类想申明它在序列化时一定要序列化哪些成员变量的列表。下面我们拿ObjectOutputStream
举例,在调用其writeObject(Object obj)
方法时,首先会进入一个判断,判断是否使用覆写逻辑,如果子类有自己特殊的序列化逻辑的话,可以将成员变量enableOverride
置为true
,然后便可覆写protected void writeObjectOverride(Object obj)
方法实现特殊逻辑。我们这里主要看writeObject0(obj, false)
。
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
中间省略了一些逻辑,直接看ObjectStreamClass
这个类,下面这个方法是获取需要序列化属性的逻辑,getDeclaredSerialFields()
是获取序列化对象声明的一定要序列化出来的属性列表的方法,如果没有特殊声明则调用getDefaultSerialFields()
获取默认的序列化属性。
private static ObjectStreamField[] getSerialFields(Class<?> cl)
throws InvalidClassException
{
ObjectStreamField[] fields;
if (Serializable.class.isAssignableFrom(cl) &&
!Externalizable.class.isAssignableFrom(cl) &&
!Proxy.isProxyClass(cl) &&
!cl.isInterface())
{
if ((fields = getDeclaredSerialFields(cl)) == null) {
fields = getDefaultSerialFields(cl);
}
Arrays.sort(fields);
} else {
fields = NO_FIELDS;
}
return fields;
}
最终某个类需要被序列化的成员变量的列表被放在某个ObjectStreamClass
实例的成员变量fields
中:
/** serializable fields */
private ObjectStreamField[] fields;
getDeclaredSerialFields()
详情如下,其中"serialPersistentFields"便是我们之前在String
类中看到的成员变量:
private static ObjectStreamField[] getDeclaredSerialFields(Class<?> cl)
throws InvalidClassException
{
ObjectStreamField[] serialPersistentFields = null;
try {
Field f = cl.getDeclaredField("serialPersistentFields");
int mask = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
if ((f.getModifiers() & mask) == mask) {
f.setAccessible(true);
serialPersistentFields = (ObjectStreamField[]) f.get(null);
}
} catch (Exception ex) {
}
if (serialPersistentFields == null) {
return null;
} else if (serialPersistentFields.length == 0) {
return NO_FIELDS;
}
...
...
}
可以看到,如果是String
类,他会返回一个NO_FIELDS
变量,其实质也是new ObjectStreamField[0]
。因为不是null
所以返回之后上面介绍的getSerialFields(Class<?> cl)
方法也不会走getDefaultSerialFields()
逻辑,那么问题来了,String
当中的字符串数据是如何序列化出来的呢?我们接着分析writeObject0(obj, false)
方法逻辑:
/**
* Underlying writeObject/writeUnshared implementation.
*/
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
...
...
...
desc = ObjectStreamClass.lookup(cl, true);
...
...
...
// remaining cases
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else
...
...
...
从上图可以看到,String
类型的逻辑是独立的,并且不需要ObjectStreamClass
帮助,所以可以解释为什么String
的成员变量serialPersistentFields
可以赋值一个空的数组。
试验数据
测试方法
public static void main(String[] args) throws IOException {
String aa = "123s";
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xxx"))) {
oos.writeObject(aa);
} catch (Exception e) {
}
System.out.println(aa);
}
断点
ObjectOutputStream.java:1134
ObjectOutputStream.java:1172
ObjectStreamClass.java:1730