作者:困了电视剧
专栏:《JavaSE语法与底层详解》
文章分布:这是一篇关于接口的文章,在本篇文章中我会将接口常用的一些实例进行讲解,以及部分方法在重写中的思想。
目录
Comparable和Comparator接口使用
Object类
toString()方法
println中的toString方法
equals()方法
Comparable和Comparator接口使用
现在我们有一个需求,我们定义了一个stu的对象数组,数组中每一个元素是Student类实例化的对象,一个Student对象中又包含姓名,年龄和成绩的属性,代码如下:
public class Javabit_Code {
public static void main(String[] args) {
Student[] stu=new Student[3];
stu[0]=new Student("11",18,11);
stu[1]=new Student("22",18,22);
stu[2]=new Student("33",18,33);
Arrays.sort(stu);
System.out.println();
}
}
class Student{
String name;
int age;
int soccer;
public Student(String name, int age, int soccer) {
this.name = name;
this.age = age;
this.soccer = soccer;
}
public Student(){}
}
现在我们要对这个对象数组中的每一个元素按成绩从小到大进行排序,我们应该怎么做?
在之前数组的文章中我提到了Arrays.sort()这个方法,这个方法中其中一个重载的方法可以将传入的数组按从小到大的顺序进行排列,这里我将这个对象数组传进去,因为我的Student对象中有很多属性,而我又没有指明用哪一个属性进行比较,所以程序此时一定会报错。
果然,程序报错了,然后我们通过报错信息点开发生异常的类。
会发现会跳到这部分源码,此时我们可以看到一个神奇的地方,在这一个方法中,它先把Object[]型的a向下转型转换成了Comparable,然后在进行相关的操作,这个Comparable又是什么呢?
一个只包含compareTo这一个方法的接口,此时逻辑关系捋顺了:对于任意一个类,要想实现比较这一个功能必须实现Comparable接口,即在逻辑上就是,这个类实例化的对象都是“可比较的”,然后重写该接口中的compareTo方法,并在调用的时候使用该类重写的方法进行比较。
现在我们修改一下Student类,让他实现一下Comparable接口
class Student implements Comparable<Student> {
String name;
int age;
int soccer;
public Student(String name, int age, int soccer) {
this.name = name;
this.age = age;
this.soccer = soccer;
}
public Student(){}
public int compareTo(Student student){
return this.soccer-student.soccer;
}
}
现在我们来运行一下
就会发现stu中的对象已经按分数从小到大的顺序排列好了,至于是怎么进行排列的就和sort的底层逻辑有关了,暂时先不说。
比较的问题解决了,但此时我们发现了一个问题,就是在本例中我是想通过分数的大小进行排序,但是如果我的需求变了,变成根据年龄的大小进行排序了怎么办,在重写一个吗?这样不仅麻烦还会造成逻辑上的混乱,况且从逻辑上进行分析Student类本身不像数字,大小那样在逻辑上可以进行比较,Student在逻辑上本身就没有比较这一功能的。
这时候我们怎么办?我们可以设置一些比较器,只要我们想进行需要比较的相关功能,使用对应的比较器就行了,为此我们需要实现Comparator接口,代码如下:
class SoccerComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.soccer-o2.soccer;
}
}
class AgeComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
方法实现在这里一样,只要返回差值即可,我们在进行相应的方法调用时,需要将对应的比较器也进行调用,就比如我现在想用年龄进行一次排序:
完全可以进行排序。
Object类
toString()方法
先看一下toString的源码:
运行一段代码,结果如图:
我们可以看到结果按照源码中的规则输出了出来,但是我们在平常调试的过程中,这种形式的输出用的并不是太多吗,我们更多地是需要将这个对象的各个属性进行输出,此时我们就可以重写toString方法,如图:
然后我们在进行输出:
就会得到我们想要的结果。
println中的toString方法
还是上述栗子,当我们在输出的时候不使用toString方法会怎么样?
我将输出中的student后面的toString删掉,然后发现运行结果和原先一样,即把这个对象的全部属性都给输出了出来。
这是怎么回事?我们打开println的源码看一下
我们可以看到,当参数为Object时,其方法内部调用了String.valueOf方法,我们点开valueOf
发现最终还是调用了toString方法,所以println的底层是调用了toString的方法,所以只要我们重写了Object中的toString方法,就可以用println直接进行输出。
equals()方法
在 Java 中, == 进行比较时:a. 如果 == 左右两侧是基本类型变量,比较的是变量中值是否相同b. 如果 == 左右两侧是引用类型变量,比较的是引用变量地址是否相同c. 如果要比较对象中内容,必须重写 Object 中的 equals 方法,因为 equals 方法默认也是按照地址比较的
现在我们有一个需求:在假设每一个学生的姓名都不同的情况下,我想判断两个学生是不是同一个人,该怎么办?
可以通过引用直接进行比较,但那样就没有体现面向对象的封装性,我们可以重写equals方法,在equals方法中进行比较
@Override
public boolean equals(Object o) {
//地址一样一定是同一个
if (this == o) return true;
//指向null或者类名不一样一定不是同一个
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
此时,方法就重写完成,我们就可以运行equals这个方法来根据学生的名字判断是不是同一个人了。
以上,就是本篇文章的全部内容,如有疏漏欢迎指正。