-------android培训、java培训、期待与您交流! ----------
Equals方法:
Object类中的equals方法用于检测一个对象是否等于另外一个对象。在Object类中这个方法判断两个对象是否有相同的引用。如果两个对象有相同的引用,它们一定是想等的。
然而,对于多数类来说,这种判断并没有什么意义。例如,采用这种方式比较两个PrintStream对象是否相等就完全没有意义。然而,经常需要检测两个对象的
相等性,如果两个对象的状态想等,就认为这两个对象是想等的。
下面给出编写一个完美的equals方法的建议:
1) 显示参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量。
2) 检测this与otherObject是否引用同一个对象:
if(this== otherObject) return true;
这条语句只是一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个一个比较类中的域代价要小得多。
3) 检测otherObject是否为null,如果为null,返回false,这项检测是很有必要的。
if(otherObject== null) return false;
4) 比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:
if(getClass()!= otherObject.getClass()) return false;
如果所有的子类都拥有统一的语义,就使用instanceof检测:
if(!(otherObject instanceofClassName)) return false;
5) 将otherObject转换成为相应的类类型变量:
ClassName other = (ClassName)otherObject;
6) 现在开始对所有需要比较的域进行比较了。使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true,否则返回false。
return field1 == other.filed1
&& filed2.equals(other.field2)
&& …
如果在子类中重新定义equals,就要在其中包含调用super.equals(other)。
PS:对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的数组元素是否想等。
从Java SE 5.0开始,为了避免发生类型错误,可以使用@Override对覆盖超类的方法进行标记:
@Override public Boolean equals(Objectother)
如果出现了错误,并且正在定义一个新方法,编译器就会给出错误报告。例如,将下面的声明添加到Employee类中:
@Override public Boolean equals(Employeeother) //ERROR
就会看到一个错误报告,这是因为这个方法并没有覆盖超类Object类中的任何方法。
HashCode方法
散列码(hash code)是由对象导出的一个整型值,散列码是没有规律的。
由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址,看一下下面的这个例子:
String s = “OK”;
StringBuilder sb = new StringBuilder(s);
System.out.println(s.hashCode() + “ ”+ sb.hashCode());
String t = “OK”;
StringBuilder tb = new StringBuilder(t);
System.out.println(t.hashCode() + “ “ +tb.hashCode());
输出如下:
Object | HashCode |
s | 2556 |
sb | 20526976 |
t | 2556 |
tb | 20527144 |
字符串s和t拥有相同的散列码,这是因为字符串的散列码是由内容导出的。而字符串缓冲sb和tb却有不同的散列码,这是因为在StringBuilder类中没有定义hashCode方法,它的散列码是由Object类的默认hashCode方法导出的对象存储地址。
如果重新定义equals方法,就必须重新定义hashCode方法,以便用户将对象插入到散列表中。
HashCode方法应该返回一个整型数值(也可以是负数),并合理地组合实例域的散列码,以便能够让不同对象产生的散列码更加均匀。
Equals域hashCode的定义必须一致,如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。例如,如果用定义Employee.equals比较雇员的ID,那么hashCode方法就需要散列ID,而不是雇员的姓名或存储地址。
下面是Employee类的hashCode方法:
class Employee
{
public int hashCode()
{
return 7 * name.hashCode()
+ 11 * newDouble(salary).hashCode()
+ 13 *hireDay.hashCode();
}
//省略部分代码
}
PS:如果存在数组类型的域,那么使用静态的Arrays.hashCode()方法计算一个散列码,这个散列码有数组元素的散列码组成。
ToString方法
在Object中还有一个重要的方法,就是toString方法,它用于返回表示对象值的字符串。
绝大多数(但不是全部)的toString方法都遵循这样的格式:类的名字、随后是一对用方括号括起来的域值。
最好通过getClass().getName()获得类名的字符串,而不要将类名硬加到toString方法中:
public String toString()
{
return getClass().getName()
+ “[name=” + name
+ “,salary=” + salary
+ “,hireDay=” + hireDay
+ “]”;
}
toString方法也可以供子类调用。
设计子类的程序员也应该定义自己的toString方法,并将子类域的描述添加进去。如果超类使用了getClass().getName(),那么子类只要调用super.toString()就可以了。
随处可见toString方法的主要原因是:只要对象与一个字符串通过操作符“+”连接起来,Java编译器就会自动的调用toString方法,以便获得这个对象的字符串描述。
如果x是任意对象,并调用
System.out.println(x);
println方法就会调用x.toString(),并打印输出得到的字符串。
Object类定义了toString方法,用来打印输出对象所属的类名和散列码。例如,调用System.out.println(System.out),将输出以下内容:
java.io.PrintStream@zf6684
之所以得到这样的结果是因为PrintStream类的设计者没有覆盖toString方法。
令人烦恼的是,数组继承了Object类的toString方法,数组类型将按照旧的格式打印,例如:
int[] luckyNumbers = {2,3,5,7,11,13};
String s = “” + luckyNumbers;
生成字符串“[I@1a46e30]”(前缀[I表明是一个整数型数组]。修正的方法是调用静态方法Arrays.toString()。代码:
String s = Arrays.toString(luckyNumbers);
将生成字符串“[2,3,5,7,11,13]”。
toString是一种非常有用的调试工具。在标准类库中,许多类都定义了toString方法,以便用户能够获得一些有关对象状态的重要信息。
PS:强烈建议为自定义的每一个类增加一个toString方法。这样做不仅自己收益,而且所有使用这个类的程序员也会受益匪浅。