以下是一个实现给对象数组排序的程序:
class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "[" + this.name + ":" + this.score + "]";
}
}
再给定一个学生对象数组, 对这个对象数组中的元素进行排序(按分数降序)。
Student[] students = new Student[] {
new Student("张三", 95),
new Student("李四", 96),
new Student("王五", 97),
new Student("赵六", 92),
};
按照我们之前的理解, 数组我们有一个现成的 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
仔细思考, 不难发现, 和普通的整数不一样, 两个整数是可以直接比较的, 大小关系明确,而两个学生对象的大小关系怎么确定? 需要我们额外指定。
可以让 Student 类实现 Comparable 接口, 并实现其中的 compareTo 方法。
class Student implements Comparable {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "[" + this.name + ":" + this.score + "]";
}
@Override
public int compareTo(Object o) {
Student s = (Student)o;
if (this.score > s.score) {
return -1;
} else if (this.score < s.score) {
return 1;
} else {
return 0;
}
}
}
然后比较当前对象和参数对象的大小关系(按分数来算)。
如果当前对象应排在参数对象之前, 返回小于 0 的数字。
如果当前对象应排在参数对象之后, 返回大于 0 的数字。
如果当前对象和参数对象不分先后, 返回 0。
再次执行程序, 结果就符合预期了。
// 执行结果
[[王五:97], [李四:96], [张三:95], [赵六:92]]
注意事项: 对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备 compareTo 这样的能力。通过重写 compareTo 方法的方式, 就可以定义比较规则。
为了进一步加深对接口的理解, 我们可以尝试自己实现一个 sort 方法来完成刚才的排序过程(使用冒泡排序)。
public static void sort(Comparable[] array) {
for (int bound = 0; bound < array.length; bound++) {
for (int cur = array.length - 1; cur > bound; cur--) {
if (array[cur - 1].compareTo(array[cur]) > 0) {
// 说明顺序不符合要求, 交换两个变量的位置
Comparable tmp = array[cur - 1];
array[cur - 1] = array[cur];
array[cur] = tmp;
}
}
}
}
再次执行代码。
sort(students);
System.out.println(Arrays.toString(students));
// 执行结果
[[王五:97], [李四:96], [张三:95], [赵六:92]]
Java 中内置了一些很有用的接口, Clonable 就是其中之一。Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”, 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常。
如下:
class Animal implements Cloneable {
private String name;
@Override
public Animal clone() {
Animal o = null;
try {
o = (Animal)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
Animal animal2 = animal.clone();
System.out.println(animal == animal2);
}
}
// 输出结果
// false
有两个概念:浅拷贝和深拷贝。
Cloneable 拷贝出的对象是一份浅拷贝。
观察以下代码:
class Money {
public double m = 99.99;
}
class Student implements Cloneable{
String name;
int age;
public Money money = new Money();
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class demo {
public static void main(String[] args) throws CloneNotSupportedException {
Student stduent1 = new Student();
Student stduent2 = (Student) Student.clone();
System.out.println("通过stduent2修改前的结果");
System.out.println(stduent1.money.m);
System.out.println(stduent2.money.m);
stduent2.money.m = 13.6;
System.out.println("通过stduent2修改后的结果");
System.out.println(stduent1.money.m);
System.out.println(stduent2.money.m);
}
}
// 执行结果
通过stduent2修改前的结果
99.99
99.99
通过stduent2修改后的结果
13.6
13.6
如上代码,我们可以看到,通过clone,我们只是拷贝了Student对象。但是Student对象中的Money对象,并没有拷贝。通过stduent2这个引用修改了m的值后,stduent1这个引用访问m的时候,值也发生了改变。其原因就是stduent2拷贝stduent1后,其成员变量money与stduent1的money为同一个地址,这里就是发生了浅拷贝。
如图:
如何避免这种情况?这就需要使用深拷贝,代码如下:
class Money implements Cloneable{
public double m=25.0;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student implements Cloneable {
public String name;
public int age;
public Money money=new Money();
@Override //深拷贝,将引用的对象(student1)中的对象也拷贝到目标对象中
protected Object clone() throws CloneNotSupportedException {
Student temp=(Student) super.clone();
temp.money=(Money)this.money.clone();
return temp;
}
}
public class demo {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student();
Student student2 = (Student) student1.clone();
System.out.println("通过stduent2修改前的结果");
System.out.println(student1.money.m);
System.out.println(student2.money.m);
student2.money.m = 13.6;
System.out.println("通过stduent2修改后的结果");
System.out.println(student1.money.m);
System.out.println(student2.money.m);
}
}
//执行结果:
通过stduent2修改前的结果
25.0
25.0
通过stduent2修改后的结果
25.0
13.6