有这样一个需求:有一个类,它有几个域,希望它的toString方法返回它的所有域的值组合成的字符串,域值之间用某种分隔符(比如####)分隔。我是这样实现的:
public class CombineFieldWithDelimiter {
private String field1;
private String field2;
private String field3;
public CombineFieldWithDelimiter() {
}
@Override
public String toString() {
String delimiter ="####";
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(delimiter);
stringBuilder.append(field1);
stringBuilder.append(delimiter);
stringBuilder.append(field2);
stringBuilder.append(delimiter);
stringBuilder.append(field3);
return stringBuilder.toString();
}
public void setField1(String field1) {
this.field1 = field1;
}
public void setField2(String field2) {
this.field2 = field2;
}
public void setField3(String field3) {
this.field3 = field3;
}
public static void main(String[] args) {
CombineFieldWithDelimiter combineFieldWithDelimiter = new CombineFieldWithDelimiter();
System.out.println(combineFieldWithDelimiter.toString());
}
}
当运行这个类的时候,我希望的输出是:
########
而实际上是:
null####null####null
为什么实际的输出和期望的不一样呢?看看StringBuilder的append(String)方法的源码:
public StringBuilder append(String str) {
super.append(str);
return this;
}
继续查看它父类的append(String)方法的源码:
public AbstractStringBuilder append(String str) {
// null字符串当做"null"处理
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
原来如此,当字符串为null时,就被当做”null”处理。因为我知道,如果没有显式的给CombineFieldWithDelimiter 类的各个域赋值,它们都将是null,所以,问题就明了了。为了解决问题,我只需显式的给每个域赋字符串”“就可以了,像下面这样:
public class CombineFieldWithDelimiter {
private String field1 = "";
...
}
如果同学们对虚拟机中对象的创建过程不太了解,这里简单介绍一下。当我们new一个对象时,虚拟机首先去检查要创建对象的类是否已经加载(包括加载、连接和初始化)了,如果没有,就执行相应的类加载过程;然后虚拟机就给对象分配内存并把分配的内存空间都初始化为零值,给内存空间赋零值得操作使得对象的实例字段可以在不赋初始值的情况下也能直接使用,但都是相应的零值,对于对象域来说,就是null,理解了这一点,才算是真正理解本文了;最后是执行对象初始化方法<init>,给对象的域赋上程序员指定的值,例如,在我对上CombineFieldWithDelimiter的代买进行修改后,下面的语句执行后,
CombineFieldWithDelimiter combineFieldWithDelimiter = new CombineFieldWithDelimiter();
field1的值不是null,而是“”了。当执行完成后,一个真正可用的对象才算完全产生出来。