文章目录
- Comparable
- Comparator
- Cloneable
- 浅拷贝
- 深拷贝
Comparable
当我们需要对一个自己写的类进行排序(Collections.sort
和Arrays.sort
)的时候,,就要使用到Comparable
接口。
该接口中有一个compareTo
方法,该方法其实就是一比较规则。
public interface Comparable<T> {
public int compareTo(T o);
}
然后比较当前对象和参数对象的大小关系(按分数来算).
- 如果当前对象应排在参数对象之前, 返回小于 0 的数字;
- 如果当前对象应排在参数对象之后, 返回大于 0 的数字;
- 如果当前对象和参数对象不分先后, 返回 0
所以如果自己的类需要进行排序的时候,就需要实现Comparable
该接口,并重写compareTo
方法,该法就可以通过指定字段进行排序,比如年龄。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Student implements Comparable<Student> {
int id;
String name;
int age;
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
// 谁调用的compareTo,谁就是this
@Override
public int compareTo(Student o) {
return this.age -o.age;
}
}
public class TestDemo {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student(2,"张三",20));
students.add(new Student(3,"李四",18));
students.add(new Student(1,"王五",15));
System.out.println(students);
Collections.sort(students);
System.out.println(students);
}
}
运行结果
[Student{id=2, name='张三', age=20}, Student{id=3, name='李四', age=18}, Student{id=1, name='王五', age=15}]
[Student{id=1, name='王五', age=15}, Student{id=3, name='李四', age=18}, Student{id=2, name='张三', age=20}]
该接口对类的侵入性比较强,但是如果这个比较规则已经被很多人使用,你突然想换个比较规则,如果修改比较规则必然会造成损失,所以这个方方法对类的侵入性太强了,并不是特别好。
Comparator
刚刚我们说 Comparable 对类的侵入性太强了,但有另外一个接口它十分灵活。就是Comparator 。
代码示例:
我们写了两个类,分别实现了 Comparator 这个接口,一个是用年龄比较,一个是用姓名比较。我们叫做比较器.
import java.util.Comparator;
public class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
import java.util.Comparator;
public class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
再来看原来的代码,我们的Student并没有实现任何接口,也没有重写compareTo 方法。
只是实例化了刚刚上面两个比较器,在用sort排序时,传过去了两个参数,一个是要排序的集合,一个是我们写的比较器对象。
class Student{
int id;
String name;
int age;
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class TestDemo {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student(3,"李四",18));
students.add(new Student(1,"王五",15));
students.add(new Student(2,"张三",20));
System.out.println(students);
//按姓名排序
NameComparator nameComparator = new NameComparator();
Collections.sort(students,nameComparator);
System.out.println(students);
// 按年龄排序
AgeComparator ageComparator = new AgeComparator();
Collections.sort(students,ageComparator);
System.out.println(students);
}
}
运行结果
[Student{id=3, name='李四', age=18}, Student{id=1, name='王五', age=15}, Student{id=2, name='张三', age=20}]
[Student{id=2, name='张三', age=20}, Student{id=3, name='李四', age=18}, Student{id=1, name='王五', age=15}]
[Student{id=1, name='王五', age=15}, Student{id=3, name='李四', age=18}, Student{id=2, name='张三', age=20}]
Comparator
接口,如果要进行比较只需要根据自己的需要写一个比较器就好了,并不像第一个接口那样直接写死了。
Cloneable
Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法, 必须要先实现Clonable接口
- 实现Clonable接口
- 记住要处理异常
- 重写 Object 的 clone 方法
class Person implements Cloneable {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class CloneableDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Person std1 = new Person("张三",18);
Person std2 = (Person) p1.clone();
System.out.println(std1);
System.out.println(std2);
}
}
运行结果
Person{name='张三', age=18}
Person{name='张三', age=18}
此时的内存布局
如果一个类里的成员变量是另外一个类,使用clone
对其进行拷贝
浅拷贝
class Test {
int val;
public Test(int val) {
this.val = val;
}
@Override
public String toString() {
return "Test{" +
"val=" + val +
'}';
}
}
class Person implements Cloneable {
String name;
int age;
Test t;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", t=" + t +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class CloneableDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Person std1 = new Person("张三",18);
std1.t = new Test(100);
Person std2 = (Person) p1.clone();
std2.t.val = 200;
System.out.println(std1);
System.out.println(std2);
}
}
运行结果
Person{name='张三', age=18, t=Test{val=200}}
Person{name='张三', age=18, t=Test{val=200}}
当我们对拷贝后的对象也就是p2里的Test示例进行修改时,p1也发生了改变。此时就是一个典型的浅拷贝
此时的内存布局,这个拷贝就是将一个对象原样克隆了一份,它其时两个对象里的Test示例对象还是指向的同一个 对象
深拷贝
要想实现深拷贝,就得修改clone
方法,再让其包含的Test类也要实现Cloneable
接口重写clone接口,让Person对象在拷贝的同时把Test示例也给拷贝,这样就实现深拷贝。
class Test implements Cloneable {
int val;
public Test(int val) {
this.val = val;
}
@Override
public String toString() {
return "Test{" +
"val=" + val +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable {
String name;
int age;
Test t;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", t=" + t +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.t = (Test) this.t.clone();
return person;
}
}
public class CloneableDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person("张三",18);
p1.t = new Test(100);
Person p2 = (Person) p1.clone();
p2.t.val = 200;
System.out.println(p1);
System.out.println(p2);
}
}
运行结果
Person{name='张三', age=18, t=Test{val=100}}
Person{name='张三', age=18, t=Test{val=200}}
此时的内存布局