排序接口——Comparable
适用于:给对象数组排序时,如果给自定义类型排序,需要手动实现Comparable接口
用法:
拿以下代码举例(给一个学生类按照名字/年龄/分数进行排序)
首先定义一个Student类:
class Student {
public String name;
public int age;
public double score;
//提供构造方法,使用快捷键Alt+Insert,选择Constructor,将所有属性选中即可构造
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
//重写toString方法,Alt+Insert 选择toString
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
再给定一个学生对象数组students, 对这个对象数组中的元素进行排序
public class TestDemo {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("caocao",16,99.9);
students[1] = new Student("zhangfei",26,19.9);
students[2] = new Student("guanyu",36,29.9);
}
}
如果按平常给数组排序的方法,调用Arrays.sort方法:
Arrays.sort(students);
System.out.println(Arrays.toString(students));
// 运行出错, 抛出异常. Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
这是因为students的数组元素类型Student时自定义类型,这种情况下就需要使用Comparable接口
让我们的 Student 类实现 Comparable 接口, 并实现其中的 compareTo 方法
//<>内的代表需要排序的那个类型
class Student implements Comparable<Student> {
public String name;
public int age;
public double score;
//提供构造方法,使用快捷键Alt+Insert,选择Constructor,将所有属性选中即可构造
public Student (String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
//重写toString方法,Alt+Insert 选择toString
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
//重写compareTo方法
@Override
public int compareTo(Student o) {
//return this.name.compareTo(o.name);
//可以直接调用this.name.comparaTo,是因为源代码已经自动实现了comparaTo接口
//从小到大排序,让当前的age和传进来的age进行比较
return this.age - o.age;
//从大到小排序
//return o.age - this.age;
}
}
这里要重点关注重写的compareTo方法,也可以写成以下形式:
//重写compareTo方法
@Override
public int compareTo(Student o) {
if (this.score > o.score) {
return -1;
} else if (this.score < o.score) {
return 1;
} else {
return 0;
}
}
比较当前对象和参数对象的大小关系.
如果当前对象应排在参数对象之前, 返回小于 0 的数字;
如果当前对象应排在参数对象之后, 返回大于 0 的数字;
如果当前对象和参数对象不分先后, 返回 0;
还要注意:如果在开始实现Comparable接口时没有加<>,
那么在重写compareTo方法时需要先强转类型:
class Student implements Comparable {
...
}
//重写compareTo方法
@Override
public int compareTo(Student o) {
Student s = (Student)o;
if (this.score > o.score) {
return -1;
} else if (this.score < o.score) {
return 1;
} else {
return 0;
}
}
字符串比较大小用的是 compareTo
//重写compareTo方法
@Override
public int compareTo(Student o) {
return this.name.compareTo(o.name);
//可以直接调用this.name.comparaTo,是因为源代码已经自动实现了comparaTo接口
}
克隆接口——Cloneable
由之前所学知识知道,拷贝有浅拷贝和深拷贝之分,这里就不再过多解释。
拿以下例子介绍Cloneable的用法:
红色曲线部分报错的原因是未实现Cloneable接口
下面外面给Person类实现Cloneable接口
这里有个疑问:我们说过实现接口后首先一定要重写接口内的抽象方法,而此处我们没有重写,Idea确也没有报错,这是为什么呢?
即:为什么Cloneable接口他是一个空接口呢?
答案是这样的:
一般把空接口叫做标记接口,
Person implements Cloneable;
如果Person实现了Cloneable接口的话,那么认为:
Person将来是可以被克隆的
1)但是仅仅实现了Cloneable接口,这样还是不能实现克隆的,因为还需重写clone方法
但是
2)此时clone仍然报错的原因是:系统担心我们没有实现Cloneable接口要声明异常或者捕获异常(使用 Alt + Enter快捷键)
3)还要注意将克隆结果强转类型为(Person)(因为person.clone(),引用的person对象 引用的clone()方法的类型是Object,类型不匹配。
所以克隆步骤为:
1、实现Cloneable接口
2、实现clone方法
3、强转类型
4、声明或捕获异常
但是,通过这种方法,只是实现了浅拷贝,只拷贝了数组,但是没有拷贝原数组内的类型
如果我们利用拷贝的person2改变money的值,结果如下:
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
Person person2 = (Person)person.clone();
System.out.println(person.m.money);
System.out.println(person2.m.money);
System.out.println("********************");
person2.m.money = 99.0;
System.out.println(person.m.money);
System.out.println(person2.m.money);
}
要想实现深拷贝的话,即达到以下目的:
则,如果当前类包含了引用类型
Person -> Money
在Person的clone方法内,不仅要克隆自己本身
还需要克隆Money
代码如下,具体细节在注释中:
class Money implements Cloneable{
double money = 12.5;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public String name;
public Money m;
//构造Money
public Person() {
this.m = new Money();
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person per = (Person)super.clone();
per.m = (Money)this.m.clone();
return per;
//Person默认继承Object父类
//因为Object是所有类的父类,向上转型,所以任何类型都可以接收
}
}
public class TestDemo3 {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
Person person2 = (Person)person.clone();
System.out.println(person.m.money);
System.out.println(person2.m.money);
System.out.println("********************");
person2.m.money = 99.0;
System.out.println(person.m.money);
System.out.println(person2.m.money);
}
public static void main1(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.name = "caocao";
//声明异常
Person person2 = (Person)person.clone();
//进行捕获异常
try {
Person person2 = (Person)person.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(person2.name);
}
}
此时运行结果为: