一. 针对对象值是否相等的比较
== 和 equals 的区别
当我们提到比较值的时候,大多数人都会想到 == ,因为在一般情况下,人们对于比较的概念中,数字比较的应用场景出现频率是最多的.
首先我们创建一个类,之后新建这个类的对象来进行比较验证.
class Student {
public String name;
public int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
}
==
我们通常都用 == 来比较数字的大小,那如果用他来比较对象会是什么情况,看下面一个代码.
Student student1 = new Student("小明", 95);
Student student2 = new Student("小明", 95);
if (student1 == student2) {
System.out.printf("相等");
} else {
System.out.println("不相等");
}
这段代码中我们创建了包含实例相同的两个对象,都是小明,95分.在运行之前我们也许会想它是相同的,但实则不然,结果为不相等.下面我们再提供一段相似的代码.
Student student1 = new Student("小明", 95);
Student student2 = student1;
if (student1 == student2) {
System.out.println("相等");
} else {
System.out.println("不相等");
}
这段代码的结果为相等.为什么?
第一段代码中我们 new 了两个对象,但其实 == 在比较对象时,比较的是对象的身份(地址),每新建一个对象就会开辟一块内存, student1引用一个对象,而 student2 也引用一个内容一样但是地址不同的对象.
equals
上面提到了 == 的应用场景,但是我们所需要的是比较两个不同对象的内容是否相等,这里则需要对类中的 equals 方法进行覆写,来针对对象的特定实例域来进行比较,判断他们是否相等.
public class Student {
public String name;
public int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
Student other = (Student)obj;
return this.score == other.score
&& this.name.equals(other.name);
}
}
equals 必须要比较的几点:
- 如果引用了同一个对象,返回 true
- 如果传入参数为 null,由于调用者不可能为 null,返回 false
- 如果这两个对象的类型不一样(getClass()可以获得对象的类型),返回false
- 最后要完成两个对象实例域的比较,这里比较成绩和姓名是否相同,(因为姓名是 String 类型,所以需要使用String类型的 equals 来进行比较,这里的equals 和我们覆写的equals 方法有所区别),返回 true,否则返回 false.
下面我们创建两个实例相同的对象和一个不同的对象来进行比较.
Student student1 = new Student("小明", 98);
Student student2 = new Student("小明", 98);
Student student3 = new Student("小红", 95);
System.out.println(student1.equals(student2));
System.out.println(student1.equals(student3));
输出结果为
true
false
二. 针对对象值大于小于和等于的比较(自然顺序)
在比较了是否相等以后,我们也需要对学生的成绩来进行排名,但是每次新建的一个对象都是由姓名和得分这两个实例域构成的,所以我们需要在学生这个类中实现 Comparable 接口,来覆写该接口中的 compareTo 方法.这里我们要让学生类实现这个接口,并且覆写接口中的方法.
class Student implements Comparable<Student>{
public String name;
public int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public int compareTo(Student o) {
if (o == null) {
//这里设定任何对象都是大于null的
return 1;
}
return this.score - o.score;
}
}
观察这个覆写的 compareTo 方法,这里规定如果传进来的对象参数是 null , 则说明调用该方法的对象大于对象o.两个对象的分数实例域进行比较,如果调用方法的对象成绩大于被比较对象o,则返回正数,反之就返回一个负数,相等就返回0.
下面我们来对这个覆写方法制定的规则进行验证.
Student student1 = new Student("小明", 98);
Student student2 = new Student("小红", 95);
if (student1.compareTo(student2) > 0) {
System.out.println("student1的分数比student2的分数高" );
} else if (student1.compareTo(student2) < 0) {
System.out.println("student2的分数比student1的分数高" );
} else {
System.out.println("student1的分数与student2的分数相等");
}
这段代码的输出结果为 student1的分数比student2的分数高.使用这样一个覆写 Comparable 接口的 compareTo 方法,能够很方便的让我们针对对象特定实例域来比较对象的大小.
三. 针对对象值大于小于和等于的比较(比较器)
刚才我们覆写的方法比较方式为一个一个对象引用调用方法,与方法传进来的参数进行比较.但如果我们想要使用方法来对两个传入的对象参数进行比较呢?
这时我们可以再创建一个类来做比较器,同时这个类要实现 Comparator 对象,再在这个比较器类中覆写 compare 方法,这个方法的参数是两个,再将内部的大小规则进行设定,就可以实现我们想要的效果.
public class ScoreComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
return o1.score - o2.score;
}
}
这个覆写方法中传入了两个引用参数,相等就返回0, o1 的分数高就返回正数,反之返回负数.
下面来对这个覆写方法进行测试,我们还是使用刚才创建出来的两个学生对象为例来进行比较.
Student student1 = new Student("小明", 98);
Student student2 = new Student("小红", 95);
//创建比较器对象用来调用compare方法
ScoreComparator scoreComparator = new ScoreComparator();
if (scoreComparator.compare(student1,student2) > 0) {
System.out.println("student1的分数比student2的分数高" );
} else if (scoreComparator.compare(student1,student2) < 0) {
System.out.println("student2的分数比student1的分数高" );
} else {
System.out.println("student1的分数与student2的分数相等");
}
这段代码运行的结果和刚才一样,都是student1的分数比student2的分数高,符合我们制定的比较规则.由于我们需要调用这个方法,所以要先创建一个比较器的类,这个类中覆写的 compare 方法也是针对 Student 学生类的分数来进行比较的,在使用的时候必须要创建一个比较器对象才能调用 compare 方法.