Java提高学习之Object(2)
Equality
问:euqals()函数是用来做什么的?
答:equals()函数可以用来检查一个对象与调用这个equals()的这个对象是否相等。
问:为什么不用“==”运算符来判断两个对象是否相等呢?
答:虽然“==”运算符可以比较两个数据是否相等,但是要来比较对象的话,恐怕达不到预期的结果。就是说,“==”通过是否引用了同一个对象来判断两个对象是否相等,这被称为“引用相等”。这个运算符不能通过比较两个对象的内容来判断它们是不是逻辑上的相等。
问:使用Object类的equals()方法可以用来做什么样的对比?
答:Object类默认的eqauls()函数进行比较的依据是:调用它的对象和传入的对象的引用是否相等。也就是说,默认的equals()进行的是引用比较。如果两个引用是相同的,equals()函数返回true;否则,返回false.
问:覆盖equals()函数的时候要遵守那些规则?
答:覆盖equals()函数的时候需要遵守的规则在Oracle官方的文档中都有申明:
- 自反性:对于任意非空的引用值x,x.equals(x)返回值为真。
- 对称性:对于任意非空的引用值x和y,x.equals(y)必须和y.equals(x)返回相同的结果。
- 传递性:对于任意的非空引用值x,y和z,如果x.equals(y)返回真,y.equals(z)返回真,那么x.equals(z)也必须返回真。
- 一致性:对于任意非空的引用值x和y,无论调用x.equals(y)多少次,都要返回相同的结果。在比较的过程中,对象中的数据不能被修改。
- 对于任意的非空引用值x,x.equals(null)必须返回假。
问:能提供一个正确覆盖equals()的示例吗?
答:当然,请看代码清单8。
代码清单8:对两个对象进行逻辑比较
class Employee
{
private String name;
private int age; Employee(String name, int age)
{
this.name = name;
this.age = age;
} @Override
public boolean equals(Object o)
{
if (!(o instanceof Employee))
return false; Employee e = (Employee) o;
return e.getName().equals(name) && e.getAge() == age;
} String getName()
{
return name;
} int getAge()
{
return age;
}
} public class EqualityDemo
{
public static void main(String[] args)
{
Employee e1 = new Employee("John Doe", );
Employee e2 = new Employee("Jane Doe", );
Employee e3 = new Employee("John Doe", );
Employee e4 = new Employee("John Doe", +);
// 验证自反性。
System.out.printf("Demonstrating reflexivity...%n%n");
System.out.printf("e1.equals(e1): %b%n", e1.equals(e1));
// 验证对称性。
System.out.printf("%nDemonstrating symmetry...%n%n");
System.out.printf("e1.equals(e2): %b%n", e1.equals(e2));
System.out.printf("e2.equals(e1): %b%n", e2.equals(e1));
System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));
System.out.printf("e3.equals(e1): %b%n", e3.equals(e1));
System.out.printf("e2.equals(e3): %b%n", e2.equals(e3));
System.out.printf("e3.equals(e2): %b%n", e3.equals(e2));
// 验证传递性。
System.out.printf("%nDemonstrating transitivity...%n%n");
System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));
System.out.printf("e3.equals(e4): %b%n", e3.equals(e4));
System.out.printf("e1.equals(e4): %b%n", e1.equals(e4));
// 验证一致性。
System.out.printf("%nDemonstrating consistency...%n%n");
for (int i = ; i < ; i++)
{
System.out.printf("e1.equals(e2): %b%n", e1.equals(e2));
System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));
}
// 验证传入非空集合时,返回值为false。
System.out.printf("%nDemonstrating null check...%n%n");
System.out.printf("e1.equals(null): %b%n", e1.equals(null));
}
}
代码清单8声明了一个包含名字、年龄成员变量的Employee对象。这个对象覆盖了equals()函数来对Employee对象进行适当的对比。
ps:覆盖hashCode()函数
当覆盖equals()函数的时候,就相当于覆盖了hashCode()函数,我将在下篇文章讨论hashCode()的时候详细说明。
equals()函数首先要检查传入的确实是一个Employee对象。如果不是,返回false。这个检查是靠instanceof运算来判断的,当传入null值的时候,同样也返回false。因此,遵守了“对于任意的非空引用值x,x.equals(null)必须返回假”这个规则。
这样,我们就保证了传入的对象是Employee类型。因为之前的instanceof判断保证了传入值必须是Employee类型的对象,所以在这里我们就不必担心抛出ClassCastException异常了。接下来,equals()方法对两个对象的name和age的值进行了比较。
编译(javac EqualityDemo.java)并运行(java EqualityDemo)代码清单8,你可以看到以下输出结果:
Demonstrating reflexivity... e1.equals(e1): true Demonstrating symmetry... e1.equals(e2): false
e2.equals(e1): false
e1.equals(e3): true
e3.equals(e1): true
e2.equals(e3): false
e3.equals(e2): false Demonstrating transitivity... e1.equals(e3): true
e3.equals(e4): true
e1.equals(e4): true Demonstrating consistency... e1.equals(e2): false
e1.equals(e3): true
e1.equals(e2): false
e1.equals(e3): true
e1.equals(e2): false
e1.equals(e3): true
e1.equals(e2): false
e1.equals(e3): true
e1.equals(e2): false
e1.equals(e3): true Demonstrating null check... e1.equals(null): false
equals()和继承
当Employee类被继承的时候,代码清单8就存在一些问题。例如,SaleRep类继承了Employee类,这个类中也有基于字符串类型的变量,equals()可以对其进行比较。假设你创建的Employee对象和SaleRep对象都有相同的“名字”和“年龄”。但是,SaleRep中还是添加了一些内容,假设你在Employee对象中调用equals()方法并且传入了一个SaleRep对象。由于SaleRep对象继承了Employee,也是一种Employee的对象,instanceof判断会通过,并且执行equals()方法来判断名字和年龄。因为这两个对象有完全相同的名字和年龄,所以equals()方法返回true。如果拿SaleRep对象中Employee的部分来和Employee比较的话,返回true值是正确的,但是,如果拿整个SaleRep对象来和Employee对象比较,返回true值就不妥了。
现在假设在SaleRep对象中调用equals()方法并将Employee传入。因为Employee不是SaleRep类型的对象(否则的话,你可以访问Employee对象中不存在的Region域,这会导致虚拟机崩溃),无法通过instanceof判断,equals()方法返回false。综上,equals()在一种判断中为true却在另一判断中为false,违背了“对称性原则”。
Joshua Bloch在《Effective Java Programming Language Guide》第七版中指出:我们无法扩展可被实例化的类(例如Employee)并向其中增加一个域(如Region域),而同时维持equals()方法的对称性。尽管也有办法来维持对称性,但代价是破坏传递性。Bloch指出解决这个问题需要在继承上支持组合:不是让SaleRep来扩展Employee,SaleRep应该引用一个私有的Employee值。
问:可以使用equals()函数来判断两个数组是否相等吗?
答:可以调用equals()函数来比较数组的引用是否相等。但是,由于在数组对象中无法覆盖equals(),所以只能对数组的引用进行比较,因为不是很常用。参见代码清单9。
代码清单9:尝试通过equals()函数来比较两个数组
public class EqualityDemo
{
public static void main(String[] args)
{
int x[] = { , , };
int y[] = { , , }; System.out.printf("x.equals(x): %b%n", x.equals(x));
System.out.printf("x.equals(y): %b%n", x.equals(y));
}
}
代码清单9的main()函数中声明了一对类型与内容完全相等的数组。然后尝试对第一个数组和它自己、第一个数组和第二个数组分别进行比较。由于equals()对数组来说比较的仅仅是引用,而不比较内容,所以x.equals(x)返回true(因为自反性——一个对象与它自己相等),但是x.equals(y)返回false。编译(javac EqualityDemo.java) 并运行(java EqualityDemo)代码清单9,你将会看到以下输出结果:
|
1
2
|
x.equals(x): truex.equals(y): false |
如果你想要比较的是两个数组的内容,也不要绝望。 可以使用java.util.Arrays 类中声明的 static boolean deepEquals(Object[] a1, Object[] a2) 方法来实现。代码清单10演示了这个方法。
代码清单10:通过deepEquals()函数来比较两个数组
import java.util.Arrays; public class EqualityDemo
{
public static void main(String[] args)
{
Integer x[] = { , , };
Integer y[] = { , , };
Integer z[] = { , , }; System.out.printf("x.equals(x): %b%n", Arrays.deepEquals(x, x));
System.out.printf("x.equals(y): %b%n", Arrays.deepEquals(x, y));
System.out.printf("x.equals(z): %b%n", Arrays.deepEquals(x, z));
}
}
由于deepEquals()方法要求传入的数组元素必须是对象,所以之前在代码清单9中的元素类型要从int[]改为Integer[]。Java语言的自动封装特性会把integer常量转换成Integer对象存放在数组中。接下来要将数组传入到deepEquals()就是小事一桩了。
编译(javac EqualityDemo.java)并运行(java EqualityDemo)代码清单10,你将看到以下输出结果。
|
1
2
3
|
x.equals(x): truex.equals(y): truex.equals(z): false |
用deepEquals()方法比较的相等是“深度”的相等:这要求每个元素对象所包含的的成员、对象相等。成员对象如果还包含了对象,也要相等,以此类推,才算是“相等”(另外,两个空的数组引用也是“深度”的相等,因此Arrays.deepEquals(null, null)返回true)。
Java提高学习之Object(2)的更多相关文章
- Java提高学习之Object类详解(1)
转自:http://www.importnew.com/10304.html 问:什么是Object类? 答:Object类存储在java.lang包中,是所有java类(Object类除外)的终极父 ...
- Java提高学习之Object(5)
字符串形式的表现 Q1:toString() 方法实现了什么功能?A1:toString() 方法将根据调用它的对象返回其对象的字符串形式,通常用于debug. Q2:当 toString() 方法没 ...
- Java提高学习之Object(4)
哈希码 问: hashCode()方法是用来做什么的? 答: hashCode()方法返回给调用者此对象的哈希码(其值由一个hash函数计算得来).这个方法通常用在基于hash的集合类中,像java. ...
- Java提高学习之Object(3)
终止 问: finalize()方法是用来做什么的? 答: finalize()方法可以被子类对象所覆盖,然后作为一个终结者,当GC被调用的时候完成最后的清理工作(例如释放系统资源之类).这就是终止. ...
- java基础学习总结——Object类
一.Object类介绍
- 转载-java基础学习汇总
共2页: 1 2 下一页 Java制作证书的工具keytool用法总结 孤傲苍狼 2014-06-24 11:03 阅读:25751 评论:3 Java基础学习总结——Java对象的序列化和 ...
- Java提高篇(三二)-----List总结
前面LZ已经充分介绍了有关于List接口的大部分知识,如ArrayList.LinkedList.Vector.Stack,通过这几个知识点可以对List接口有了比较深的了解了.只有通过归纳总结的知识 ...
- java提高篇(四)-----理解java的三大特性之多态
面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法. 继承 ...
- Java提高篇(三二)-----List总结
前面LZ已经充分介绍了有关于List接口的大部分知识,如ArrayList.LinkedList.Vector.Stack,通过这几个知识点能够对List接口有了比較深的了解了.仅仅有通过归纳总结的知 ...
随机推荐
- Hibernate征途(三)之CRUD
上篇博客<Hibernate征途(二)之基础与核心>介绍了Hibernate的基础内容和核心内容,这篇博客简单实践一下.第一篇博客也说过Hibernate是一种JDBC的简化方案,既然是和 ...
- Node.js服务的重启与监控
开始:Node +服务文件名 停止:直接快捷键Ctrl+C 监控:1.安装 npm install supervisor -g, 2.supervisor app.js 参考:http://blog. ...
- C#中的多线程-入门
概述与概念C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行.一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多 ...
- Kernel Packet Traveling Diagram(图片,关于iptables)
转自:view-source:http://www.docum.org/docum.org/kptd/ Network -----------+----------- | +------------- ...
- javascript定时器(上)
(一).setInterval 间隔性 function show(){ alert(‘a’); } setInterval(show,1000); 每隔1000毫秒(1秒)执行一次show这个函数: ...
- SQL Server2012 创建定时作业——图文并茂,教你轻松快速创建
SQL Server2012 如何创建定时作业?有兴趣的可以来看下,图文并茂哦! 1.首先我们打开SQL Server代理,里面选择作业 2.右键作业,选择新建作业 3.填写一些相应的内容,名称和说明 ...
- yii2 安装过程中的问题及解决方法
一.php版本要求5.4+,如果使用wamp组合包,建议更换 二.各种模块的支持,一般只要修改php.ini文件,去掉相应模块前的注释即可. 注意,Intl extension模块的支持需要将 ...
- 从头开发MUDLIB
跟Akuma一起从头打造mudlib--[第一讲] 第一讲:让它跑起来注:每一讲我都会上传一个相符的lib,有些文件是旧的,有些是新的,我尽可能在lib里写清楚注释.更详细的内容则在每讲的正文里写. ...
- PHP学习笔记-数组(1)
1-1 数组定义 1.什么是数组? 所谓数组,就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为下标.组 ...
- elasticsearch 索引 类型 id
zjtest7-frontend:/usr/local/logstash-2.3.4/config# cat logstash_indexer01.conf input { redi ...