《Java核心技术 卷I》学习笔记18:Object类


Object类是Java中所有类的始祖,在Java中每个类都扩展了 Object类。在定义类时,如果没有明确地指出超类, Object类就被认为是这个类的超类。下面介绍 Object类的一些基本内容。

1 Object类型的变量

可以使用Object类型的变量引用任何类型的对象。但Object类型的变量只能用于作为各种值的一个泛型容器,要想对其中的内容进行具体操作,还需要清楚对象的原始类型,并进行相应的强制类型转换。例如:

Object obj = new Employee("Harry", 35000);
Employee e = (Employee) obj;

所有的数组类型,不管是对象数组还是基本类型的数组都扩展了Object类。例如,下列操作是合法的:

Employee[] staff = new Employee[10];
Object obj = staff;
obj = new int[10];

2 equals方法

Object类中的equals方法用于检测一个对象是否等于另一个对象。Object类中实现的equals方法将确定两个对象引用是否相等。例如:

Employee a = new Employee("Harry", 35000);
Employee b = a;
Employee c = new Employee("Harry", 35000);
Employee d = new Employee("Peter", 30000);

Object object = a;
System.out.println(object.equals(b)); // true
System.out.println(object.equals(c)); // false
System.out.println(object.equals(d)); // false

通常情况下,需要基于状态检测对象的相等性,如果两个对象有相同的状态,才认为这两个对象是相等的。Object类中实现的equals方法显然不能满足这种需求。为此需要自己编写equals方法的实现。例如,Employee类的equals方法实现如下:

public boolean equals(Object otherObject)
{
	// 判断两个对象引用是否相同
	if (this == otherObject) return true;

	// 当 otherObject 为 null 时,返回 false
	if(otherObject == null) return false;

	// 如果类型不一样,二者不可能相等
	if(getClass() != otherObject.getClass())
		return false;

	// 至此,otherObject 一定是一个非 null 的 Employee 对象
	Employee other = (Employee) otherObject;

	// 判断二者的状态是否相同
	return Objects.equals(name, other.name)
		&& salary == other.salary
		&& Objects.equals(hireDay, other.hireDay);
}

对上面的实现做如下说明:

  1. getClass方法返回一个对象所属的类。
  2. 为了防备namehireDay可能为null的情况,需要使用Objects.equals方法。如果两个参数都为nullObjects.equals(a, b)返回true;如果其中一个参数为null,则返回false;如果两个参数都不为null,则调用a.equals(b)

在子类中定义equals方法时,首先调用超类的equals,如果检测失败,对象就不可能相等。如果超类中的字段都相等,就需要比较子类中的实例字段。例如,Manager类的equals方法可以定义如下:

public boolean equals(Object otherObject)
{
	if (!super.equals(otherObject)) return false;

	Manager other = (Manager) otherObject;
	return bonus == other.bonus;
}

Java语言规范要求equals方法具有下面的特性:

  1. 自反性:对于任何非空引用xx.equals(x)应该返回true
  2. 对称性:对于任何引用xy,当且仅当y.equals(x)返回true时,x.equals(y)返回true。也就是说,y.equals(x)x.equals(y)返回的结果相同。
  3. 传递性:对于任何引用xyz,如果x.equals(y)返回truey.equals(z)返回truex.equals(z)也应该返回true
  4. 一致性:如果xy引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。
  5. 对于任意非空引用xx.equals(null)应该返回false

下面给出编写一个完美的equals方法的建议:

  1. 显式参数命名为otherObject,稍后需要将它强制转换成另一个名为other的变量。
  2. 检测thisotherObject是否相等:
    if (this == otherObject) return true;
  3. 检测otherObject是否为null,如果为null,返回false
    if (otherObject == null) return false;
  4. 比较thisotherObject的类。如果equals的语义可以在子类中改变,就使用getClass检测:
    if (getClass() != otherObject.getClass()) return false;
    如果所有的子类都有相同的相等性语义,可以使用instanceof检测:
    if (!(otherObject instanceof ClassName)) return false;
  5. otherObject强制转换为相应类类型的变量:
    ClassName other = (ClassName) otherObject;
  6. 根据相等性概念的要求来比较字段。使用==比较基本类型字段,使用Objects.equals比较对象字段。对于基本类型的数组字段,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等。

如果在子类中重新定义equals,就要在其中包含一个super.squals(otherObject)调用。

对于上面提到的相等性语义,做出如下说明。在EmployeeManager的例子中,只要对应的字段相等,就认为两个对象相等。如果两个Manager对象的姓名、薪水和雇用日期均相等,而奖金不相等,就认为它们是不相同的,因此要使用getClass检测。但是,假设使用员工的ID作为相等性检测标准,并且这个相等性概念适用于所有的子类,就可以使用instanceof检测,这样可以在不同子类的对象之间进行相等性比较。

3 hashCode方法

散列码(hash code)是由对象导出的一个整数值。散列码是没有规律的,如果xy是两个不同的对象,x.hashCode()y.hashCode()基本上不会相同。

由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值由对象的存储地址得出。如果在类中没有定义hashCode方法,将采用Object类中的默认hashCode方法,从对象的存储地址得出散列码。

String类的hashCode方法实现如下:

int hash = 0;
for (int i = 0; i < length(); i++)
{
	hash = 31 * hash + charAt(i);
}

可见字符串的散列码是由内容导出的。

hashCode方法应该返回一个整数(可以是负数)。要合理地组合实例字段的散列码,以便能够让不同对象产生的散列码分布更加均匀。对于对象字段,使用null安全的Objects.hashCode(Object)方法,当参数为null时返回0,否则返回对参数调用hashCode的结果;对于基本类型字段,使用包装器的静态hashCode方法;对于数组字段,使用静态的Arrays.hashCode方法。例如,Employee类的hashCode方法可以实现如下:

public int hashCode()
{
	return 7 * Objects.hashCode(name)
		+ 11 * Double.hashCode(salary)
		+ 13 * Objects.hashCode(hireDay);
}

其中系数7、11、13是自己设定的,对象字段namehireDay使用Objects.hashCode(Object)方法,double类型的salary属性使用Double.hashCode方法。

需要组合多个散列值时,可以调用Objects.hash并将所有字段作为参数。这个方法会对各个参数调用Objects.hashCode,并组合这些散列值。例如,上面的Employee类的hashCode方法可以写为:

public int hashCode()
{
	return Objects.hash(name, salary, hireDay);
}

equals方法与hashCode方法的定义必须兼容,如果x.equals(y)返回true,那么x.hashCode()y.hashCode()就必须返回相同的值。如果重新定义了equals方法,就必须为用户可能插入散列表的对象重新定义hashCode方法。

4 toString方法

Object类中的toString方法会返回表示对象值的一个字符串。例如,Point类的toString方法会返回形如这样的字符串:java.awt.Point[x=10,y=20]

绝大多数的toString方法遵循这样的格式:类名,随后是一对方括号括起来的字段值。例如,Employee类的toString方法可以实现如下:

public String toString()
{
	return getClass().getName()
		+ "[name=" + name
		+ ",salary=" + salary
		+ ",hireDay=" + hireDay
		+ "]";
}

getClass方法返回包含对象信息的类对象,getName方法返回字符串形式的类名。

设计子类时应该单独定义子类的toString方法,并加入子类的字段。如果超类使用了getClass().getName(),子类只需要调用super.toString()再加上子类字段就可以了。例如,Manager类的toString方法可以实现如下:

public String toString()
{
	return super.toString()
		+ "[bonus=" + bonus
		+ "]";
}

只要对象与一个字符串通过操作符+连接起来,Java编译器就会自动调用toString方法来获得这个对象的字符串描述。例如:

Point p = new Point(10, 20);
String message = "The current position is " + p;

如果x是一个任意对象,调用System.out.println(x)时就会自动调用x.toString(),并输出得到的字符串。

Object类中定义的默认的toString方法可以得到对象的类名和散列码。例如,调用System.out.println(System.out),会输出java.io.PrintStream@2f6684。之所以得到这样的结果,是因为PrintStream类没有覆盖toString方法。

toString方法是一种非常有用的调试工具。在标准类库中,许多类都定义了toString方法,以便用户能够获得一些有关对象状态的有用信息。建议为自定义的每一个类添加toString方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值