学习Java少不了对Object的认知,所有类都会继承它的属性,真正的超类。这一个系列,我会对Object中的几个方法,也就是我们自定义类的时候需要重写的几个方法做一个介绍。下面是这一个系列的主要内容:
本系列内容源于对《Effective Java》中文第二版第8条到第12条的学习记录。所有内容的准确性均以原书为准。
1,引言
toString方法在平时是我们用的相对来说最多的,所以它的意义也更加重要,虽然重写它的时候没有约束,但是我们应该利用它达到我们想要的目的,下面就来看一个官方API对其的介绍和建议:
public String toString()
返回该对象的字符串表示。通常,toString
方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。
Object
类的 toString
方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@
”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())
返回:
该对象的字符串表示形式。
从官方的介绍我们可以总结出两点:
- 建议所有子类重写该方法
- 默认的格式是类名+“@”+哈希码值的无符号16进制表示
2,分析
我们下面就对上述的两条进行验证,看看对于Java平台类库中的类是不是都重写toString方法,如果没有,是不是符合第二条的组成规则。
- 验证没有重写的类
我在API中随便找了一个类:
public final class Parameter
extends Object
implements IDLEntity
org/omg/Dynamic/Parameter.java。由 "3.2" 版的 IDL-to-Java 编译器(可移植)从 ../../../../src/share/classes/org/omg/PortableInterceptor/Interceptors.idl 生成,生成时间为 2006 年 6 月 30 日,星期五上午 12:40:09 (GMT-08:00)。
它就没有重写toString方法,测试如下:
可见并不是所有java平台类库中的所有类都重写了toSTring,但是不可否认我们经常使用的比如String,文件相关API,集合相关API等等都是重写了的
- 验证Object中的toString方法默认输出组成
这里为了验证,我创建了两个Object对象,测试结果如下:
于是我们还会有下面两个猜测:
- 如果我们自己重写了hashCode方法还会保持上面的规律吗?
- 如果想保持一致该肿么办?
我们使用如下的Person类测试第一个猜测:
package hfut.edu;
/**
* Date:2018年10月1日 上午11:05:45 Author:why
*/
public class Person {
private int hash=1;
int age;
String name;
String sex;
public Person(int age, String name, String sex) {
super();
this.age = age;
this.name = name;
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if (!(obj instanceof Person))
return false;
Person p = (Person) obj;
return this.age == p.age && this.name.equals(p.name) && this.sex.equals(p.sex);
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
hash=hash*3+this.age;
hash=hash*3+this.name.hashCode();
hash=hash*3+this.sex.hashCode();
return hash;
}
}
测试结果如下:
到这里我们就验证了第一条是否定的。但是我觉得还是有点问题,因为我们观察Object类中的toString方法:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
我们想了一下,它调用的就是我们的hashCode方法转为16进制字符串显示,为什么我们转回10进制就不对了,这里有一个问题就是我们调用了两次hashCode方法,第一次调用的时候就修改了对象的hash参数的值,第二次是在此基础上进行运算的,所以出现了上述的错误,我在上一篇博客中也出现了这样的错误(现在已修改),所以,Person类的hashCode方法应该这样定义:
@Override
public int hashCode() {
// TODO Auto-generated method stub
int hash=1;
hash = hash * 3 + this.age;
hash = hash * 3 + this.name.hashCode();
hash = hash * 3 + this.sex.hashCode();
return hash;
}
同时,删除Person类中的hash成员变量即可,这个时候我们再次测试:
现在就没有任何问题了。
上面也说了,重写toString没有什么约束条约,所以可以随便定义,但是我们必须要明确其用途和意义,这里,我们就可以效仿Java平台类库了,比如File:
/**
* Returns the pathname string of this abstract pathname. This is just the
* string returned by the <code>{@link #getPath}</code> method.
*
* @return The string form of this abstract pathname
*/
public String toString() {
return getPath();
}
比如Calender:
/**
* Return a string representation of this calendar. This method
* is intended to be used only for debugging purposes, and the
* format of the returned string may vary between implementations.
* The returned string may be empty but may not be <code>null</code>.
*
* @return a string representation of this calendar.
*/
@Override
public String toString() {
// NOTE: BuddhistCalendar.toString() interprets the string
// produced by this method so that the Gregorian year number
// is substituted by its B.E. year value. It relies on
// "...,YEAR=<year>,..." or "...,YEAR=?,...".
StringBuilder buffer = new StringBuilder(800);
buffer.append(getClass().getName()).append('[');
appendValue(buffer, "time", isTimeSet, time);
buffer.append(",areFieldsSet=").append(areFieldsSet);
buffer.append(",areAllFieldsSet=").append(areAllFieldsSet);
buffer.append(",lenient=").append(lenient);
buffer.append(",zone=").append(zone);
appendValue(buffer, ",firstDayOfWeek", true, (long) firstDayOfWeek);
appendValue(buffer, ",minimalDaysInFirstWeek", true, (long) minimalDaysInFirstWeek);
for (int i = 0; i < FIELD_COUNT; ++i) {
buffer.append(',');
appendValue(buffer, FIELD_NAME[i], isSet(i), (long) fields[i]);
}
buffer.append(']');
return buffer.toString();
}
可见只要添加好toString方法备注,无论是继承还是扩展都明白你输出的内容即可。到这里,关于toString的介绍就结束了。