java比较方法、浅拷贝、深拷贝
希望本博客对你有所帮助。(能帮助一点点我就非常开心!!!)
Comparable接口
举例:我们这里写一个Student类,里面包含两个成员变量(姓名和年龄)
public class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Test{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
当我们对自己自定义的类进行比较,会发现代码报错!因为这些操作符只能比较基本数据类型(byte、short、int、long、float、double)的数值。
public class Test {
public static void main(String[] args) {
Student student1=new Student("zhangsan ",6);
Student student2=new Student("lisi ",8);
System.out.println(student1>student2);
}
}
因此这里就引出了 Comparable接口,当比较自定义类的成员变量,需要重写Comparable接口中的compareTo方法。
//Student.java
public class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
return this.age-o.age;
}
@Override
public String toString() {
return "Test{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//Test.java
public class Test {
public static void main(String[] args) {
Student student1=new Student("zhangsan ",6);
Student student2=new Student("lisi ",8);
System.out.println(student1.compareTo(student2));//-2
}
}
这个重写的compareTo方法是通过年龄进行比较的,你也可以通过name进行比较,那么compareTo方法的代码应该是:return this.name.compareTo(o.name);(这里通过字符串比较大小是和C语言中是一样的)
这里值得注意的是怎样实现Comparable接口,Comparable<“T”>(<“T”>是泛型,后续会为您详细解释,这里只需要知道即可)当你要比较那个自定义类,“T”就改为你自定义类的类名。如这里比较的是“Student”类,因此Comparable<“Student”>。
以上是单个类的实例比较,那么如果我要实现多个类比较,应该怎么办呢?
这里我们可以通过数组进行多个类的比较
public class Test {
public static void main(String[] args) {
Student[] students=new Student[4];
students[0]=new Student("zhangsan ",6);
students[1]=new Student("lisi ",8);
students[2]=new Student("wangwu ",10);
students[3]=new Student("laoliu ",9);
System.out.println("排序前"+Arrays.toString(students));
Arrays.sort(students);
System.out.println("排序后"+Arrays.toString(students));
}
}
运行结果:
这里的 Arrays.sort()方法也是根据你重写的compareTo方法进行比较排序的。如果你需要逆序比较,只需要把return this.age-o.age;改为return o.age-this.age;
总结:
一个自定义的类中有且只能有重写的一个compareTo方法,不会出现多个compareTo方法。此时,compareTo方法的缺点就体现出来了,局限性太大了(compareTo 方法只支持一种比较方式)!!!因此这里就引出了Comparator 接口允许你为自定义类实现多个比较逻辑。
Comparator 接口
还是以上面的Student为例进行演示。
//NameComparator
import java.util.Comparator;
public class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
这是我重写的Comparator接口中的compare()方法(通过name进行比较)。
public class Test {
public static void main(String[] args) {
Student[] students=new Student[4];
students[0]=new Student("zhangsan ",6);
students[1]=new Student("lisi ",8);
students[2]=new Student("wangwu ",10);
students[3]=new Student("laoliu ",9);
System.out.println("排序前"+Arrays.toString(students));
NameComparator nameComparator=new NameComparator();
Arrays.sort(students,nameComparator);
System.out.println("排序后"+Arrays.toString(students));
}
}
运行结果:
public static void main(String[] args) {
Student[] students=new Student[4];
students[0]=new Student("zhangsan ",6);
students[1]=new Student("lisi ",8);
students[2]=new Student("wangwu ",10);
students[3]=new Student("laoliu ",9);
System.out.println("排序前"+Arrays.toString(students));
NameComparator nameComparator=new NameComparator();
int ret=nameComparator.compare(students[0],students[2]);
System.out.println(ret);
}
这里你还可以通过对象的引用,来调用Comparator接口中的Compare()方法。
//AgeComparator
public class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
//Test.java
public class Test {
public static void main(String[] args) {
Student[] students=new Student[4];
students[0]=new Student("zhangsan ",6);
students[1]=new Student("lisi ",8);
students[2]=new Student("wangwu ",10);
students[3]=new Student("laoliu ",9);
System.out.println("排序前"+Arrays.toString(students));
NameComparator nameComparator=new NameComparator();
AgeComparator ageComparator=new AgeComparator();
Arrays.sort(students,nameComparator);
System.out.println("name排序后"+Arrays.toString(students));
Arrays.sort(students,ageComparator);
System.out.println("age排序后"+Arrays.toString(students));
}
}
运行结果:
通过使用 Comparator,你可以定义多个不同的排序逻辑,而无需修改原始类的源代码。
equals方法
equals() 方法是 Object 类的一个方法,用于比较两个对象的“内容”是否相等,而不仅仅是它们的引用(即内存地址)是否相同。并且重写之后会发现equals()方法返回的是boolean类型。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
拷贝
拷贝的注意事项:
- 先实现Cloneable接口。
public interface Cloneable {
}
Cloneable接口是空接口,作用:标记当前自定义类是可以拷贝的。
2.再重写clone()方法。
这里需要注意两点:一、因为object是所有类的父类,所以这里在实例化的时候需要向下转型;二、重写clone()方法会抛出异常,所以在main函数调用此方法时,也需要抛出异常。
浅拷贝
浅拷贝会创建一个新对象,这个新对象有着原始对象属性值的一份精确拷贝。如果属性是基本数据类型,拷贝的就是基本数据类型的值;如果属性是引用数据类型,拷贝的就是内存地址,因此如果属性指向的对象被修改,那么原始对象和新对象都会受到影响,因为它们指向的是同一个对象。
//Writer.java
class Writer implements Cloneable{
public String author="张三";
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//Book.java
public class Book implements Cloneable {
public String name="查理九世";
public double money=9.9;
public Writer writer=new Writer();
public Book() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
}
//Test.java
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Book book1=new Book();
Book book2=(Book) book1.clone();
System.out.println("修改前"+book1.writer.author);
System.out.println("修改前"+book2.writer.author);
book1.writer.author="李四";
System.out.println("修改后"+book1.writer.author);
System.out.println("修改后"+book2.writer.author);
}
}
运行结果:
深拷贝
深拷贝会创建一个新对象,并且递归地复制对象中引用的其他对象,直到这些对象都变为基本类型为止。这样,原始对象和新对象之间就没有任何关联了,修改新对象的任何属性(包括引用类型的属性)都不会影响到原始对象。
@Override
protected Object clone() throws CloneNotSupportedException {
Book tmp=(Book)super.clone();
tmp.writer=(Writer) this.writer.clone();//调用所写Writer类中的clone()方法,将新对象赋值给tmp对象的writer成员变量;
return tmp;
}
深拷贝就和浅拷贝重写的clone()方法不一样,其他都一样!
运行结果:
总结:深拷贝和浅拷贝是根据代码区实现的!!!
较为容易理解:
如果重新开辟了空间拷贝过去了,那就是深拷贝,
如果没有还是指向原来的空间,那就是浅拷贝。