1、Equals方法
Object类中的equals方法用于检测一个对象是否等于另外一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。从这点上看,将其作为默认操作也是合乎情理的。然而,对于多数类来说,这种判断没有什么意义。例如,采用这种方式比较两个PrintStream对象是否相等就没有完全意义。然而,经常需要检测两个对象状态的相等性,如果两个对象的状态相等,就认为这两个是相等的。
例如,如果两个雇员对象的姓名,薪水和雇用日期都一样,就认为它们是相等的(在实际的雇员数据库中,比较ID更有意义。)
2、equals 方法在非空对象引用上实现相等关系(java语言规范要求equals方法具有的特性):
- 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
- 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
- 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
- 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
- 对于任何非空引用值 x,x.equals(null) 都应返回 false。
Object类的 equals方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值true)。
注意:当此方法被重写时,通常有必要重写 hashCode方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
3、编写一个完美的equals方法的建议:
1)显示参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量。
2)检测this与otherObject是否引用同一个对象:
If(this == otherObject) return true;
这条语句只是一个一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多。
3)检测otherObject是否属于同一个类。如果equals的语义在每个子类中有所改就使用getClass检测:
If(getClass() ! = otherObject.getClass()) return false;
如果所有的子类都拥有统一的语义,就使用instanceof检测:
If(!(otherObjectinstanceof ClassName)) return false;
5)将otherObject转换为相应的类类型变量:
ClassNameother = (ClassName) otherObject
6)现在开始对所有需要比较的域进行比较了。使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true,否则返回false。
Returnfield1 == other.Filed1
&&field2.equals(other.field2)
&&...;
如果在子类中重新定义equals,就要在其中包含调用super.equals(other)。
提示: 对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等。
4、HashCode方法
散列码(hash code)是由对象导出的一个整型值。散列码是没有规律的。如果x和y是两个不同的对象,x.hashCode()与y.hashCode()基本上不会相同。
HashCode方法应该返回一个整形数值(也可以是负数),并合理地组合实例域的散列码,以便能够让各个不同的对象产生的散列码更加均匀。
例如,下面是Employee类的hashCode方法。
Class Employee
{
Public int hashCode()
{
Return 7 * name.hashCode()
+ 11 * newDouble(salary).hashCode()
+ 13 * hireDay.hashCode();
}
......
}
Equals与hashCode的定义必须一致:如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。例如,如果用定义的Employee.equals比较雇员的ID,那么hashCode方法就需要散列ID,而不是雇员的姓名或存储地址。
提示:如果存在数组类型的域,那么可以使用静态的Arrary.hashCode方法计算一个散列码,这个散列码由数组元素的散列码组成。
ToString方法
在Object中海油一个重要的方法,就是toString方法,它用于返回表示对象值的字符串。下面是一个典型的例子。Point类的toString方法将返回下面这样的字符串:
Java。Awt。Point[x=10,y=20]
绝大多数(但不是全部)的toString方法都遵循这样的格式:类的名字,随后是一个对方括号括起来的域值。下面是Employee类中的toString方法的实现:
Public String toString()
{
Return“Employee[name=” + name + “,salary=”+ salary + “,hireDay=” +hireDay + “]”;
}
随处可见toString方法的主要原因是:只要对象与一个字符串通过操作符“+”连接起来,java变异就会自动地调用toString方法,以便获得这个对象的字符串描述。例如,
Pointp = new Point(10, 20);
Stringmessage = “The current position is” + p;
//automaticallyinvokes p.toString()
提示:在调用x.toString()的地方可以用”+x”替代。这条语句将一个空串与x的字符串表示相连接。这里的x就是x.toString()。与toString不同的是,如果x是基本类型,这条语句照样能够执行。
如果x是任意一个对象,并调用
System.out.println(x);
Println方法就会直接地调用x.toString(),并打印输出得到的字符串。
Object类定义了toString方法,用来打印输出对象所属的类名和散列码。例如,调用
System.out.println(System.out)
将输出下列内容:
之所以得到这样的结果是因为PrintStream类的设计者没有覆盖toString方法。
要想打印多维数组(即,数组的数组)则需要调用Arrays.deepToString方法。
toString方法是一种非常有用的调试工具。在标准类库中,许多类都定义了toString方法,以便用户能够获得一些有关对象状态的必要信息。
package Equal;
import java.util.Date;
import java.util.GregorianCalendar;
public classEqualsTest {
/**
* @authorxiaoyuan
* @param args
* 实现Employee类和Manager类的equals、hashcode和toString方法。
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Employeealice1 = newEmployee("Alice Adams", 75000, 1987, 12, 15);
Employeealice2 = alice1;
Employeealice3 = newEmployee("Alice Adams", 75000, 1987, 12, 15);
Employeebob = newEmployee("Bob Brandson", 50000, 1989, 10, 1);
System.out.println("alice1 == alice2: "+ (alice1 == alice2));
System.out.println("alice1 == alice3: "+ (alice1 == alice3));
System.out.println("alice1.equals(alice3): " + alice1.equals(alice3));
System.out.println("alice1.equals(bob): "+ alice1.equals(bob));
System.out.println("bob.toString(): "+ bob);
Managercarl = newManager("Carl Cracker", 80000, 1987, 12, 15);
Managerboss = newManager("Carl Cracker", 80000, 1987, 12, 15);
boss.setBonus(5000);
System.out.println("boss.toString(): "+ boss);
System.out.println("carl.equals(boss): "+ carl.equals(boss));
System.out.println("alice1.hashCode(): "+ alice1.hashCode());
System.out.println("alice3.hashCode()"+ alice3.hashCode());
System.out.println("bob.hashCode()"+ bob.hashCode());
System.out.println("carl.hashCode()"+ carl.hashCode());
}
}
class Employee {
public Employee(String n,double s, int year,int month, int day) {
// TODO Auto-generatedconstructor stub
name = n;
salary = s;
GregorianCalendarcalendar = newGregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName() {
returnname;
}
public double getSalary() {
returnsalary;
}
public Date getHireDay() {
returnhireDay;
}
public void raiseSalary(double byPercent) {
double raise =salary * byPercent / 100;
salary += raise;
}
public boolean equals(ObjectotherObject) {
// a quick test to see if the objects are identical
if (this == otherObject)
return true;
// must return false if the explicit parameter is null
if (otherObject ==null)
return false;
// if the classes don't match,they can't be equal
if (getClass() !=otherObject.getClass())
return false;
// now we know otherObject is a non-null Employee
Employeeother = (Employee) otherObject;
// test whether the fields have identical values
returnname.equals(other.name) &&salary == other.salary
&&hireDay.equals(other.hireDay);
}
public int hashCode() {
return 7 *name.hashCode() + 11 * new Double(salary).hashCode() + 13
*hireDay.hashCode();
}
public String toString() {
return getClass().getName() +"[name="+ name+",salary=" + salary
+",hireDay=" + hireDay+ "]";
}
private Stringname;
private double salary;
private DatehireDay;
}
class Managerextends Employee {
public Manager(String n,double s, int year,int month, int day) {
super(n, s, year, month,day);
bonus = 0;
// TODO Auto-generated constructor stub
}
public double getSalary() {
double baseSalary =super.getSalary();
return baseSalary +bonus;
}
public void setBonus(double b) {
bonus = b;
}
public boolean equals(ObjectotherObject) {
if (!super.equals(otherObject))
return false;
Managerother = (Manager) otherObject;
// super.equals checked that this and other belong to thesame class
returnbonus == other.bonus;
}
public int hashCode() {
return super.hashCode() + 17 * new Double(bonus).hashCode();
}
public String toString() {
return super.toString() + "[bonus="+ bonus+ "]";
}
private double bonus;
}