目标:总结Comparable接口以及compareTo方法、comparator接口中compare方法比较器、toString方法、equals方法、hashCode方法、Cloneable接口以及深浅拷贝
比较对象中内容的大小【Comparable接口以及compareTo方法】
例如:学生类:成员有姓名、年龄、分数
要求:分别按照姓名、年龄、分数排序
初步编写:
学生类:
package demo2;public class Student {public String name;public int age;public int score;public Student(String name, int age, int score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}
测试类:
package demo2;
import java.util.Arrays;
public class TestStudent {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student(“王王王”,21,98);
students[1] = new Student(“帅帅帅”,22,66);
students[2] = new Student(“哈哈哈”,23,99);
Arrays.sort(students);System.out.println(Arrays.toString(students));
}
}
运行结果:抛出异常

分析:Arrays.sort(students);不知道怎么比较大小
根据报错提示:


作用:取到数组的某一个值,把这个值转换为一个Comparable接口类型,然后调用compareTo方法

因此在Student类中实现Comparable接口,并重写compareTo方法。
package demo2;public class Student implements Comparable{public String name;public int age;public int score;@Overridepublic int compareTo(Object o) {//向下转型,因为Object是所有类的父类,将Object父类转成Studnet子类Student s = (Student) o;if (this.age > s.age) {return 1;}else if (this.age < s.age) {return -1;}else {return 0;}}public Student(String name, int age, int score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}
运行结果:

分析:此时按照年龄从小到大排序。
System.out.println(students[0].compareTo(students[1]));
让0下标的学生去调用compareTo方法去和1下标的学生进行比较,所以在compareTo方法中,this代表0下标的学生,传入的Object o,以及强制类型转换成Student s代表的是1下标的学生。

假如根据姓名比较,重写的compareTo方法相当于写了一个比较规则,从而sort也有用了
public int compareTo(Object o) {Student s = (Student) o;if (this.name.compareTo(s.name) > 0) {return 1;}else if (this.name.compareTo(s.name) < 0) {return -1;}else {return 0;}}
运行结果:

分析:此处的name是String类型,String类型比较大小不能通过数学符号(>和<等)比较,要通过String类中的compareTo方法(String类也实现了Comparable接口,则必定也实现了compareTo方法),equals只能比较字符串相不相同(并且equals返回值的类型是boolean类型)
自己实现sort【采用冒泡排序】
public static void sort(Comparable[] array) {for (int i = 0; i < array.length - 1; i++) {for (int j = 0; j < array.length - 1 -i; j++) {if (array[j].compareTo(array[j+1]) > 0) {Comparable t = array[j];array[j] = array[j+1];array[j+1] = t;}}}}
注意:形参是Comparable接口类型,在if语句中的比较规则用到实现接口中的compareTo方法。
但是,目前这样写,用Comparable接口不好,因为代码不够灵活,一旦写死了拿年龄比较,以后默认都是拿年龄比较,那我要想拿名字或者分数比较就不行了。

package demo4;import java.util.Comparator;public class AgeComparator implements Comparator {@Overridepublic int compare(Student o1, Student o2) {return o1.age - o2.age;}
}
实例化新建类的对象,并作为sort的第二个参数
public static void main(String[] args) {Student[] students = new Student[3];students[0] = new Student("王王王",23,98);students[1] = new Student("帅帅帅",22,66);students[2] = new Student("哈哈哈",24,99);//既然是一个类,就在这里面实例化这个对象AgeComparator ageComparator = new AgeComparator();//把他传给sort的第二个参数Arrays.sort(students,ageComparator);System.out.println(Arrays.toString(students));}
分析: AgeComparator ageComparator = new AgeComparator(); 叫做比较器,这样实现比较就比较灵活。
比如想通过分数比较,可以新建一个分数的比较器:
package demo4;import java.util.Comparator;public class ScoreComparator implements Comparator {@Overridepublic int compare(Student o1, Student o2) {return o1.score - o2.score;}
}
ScoreComparator scoreComparator = new ScoreComparator();Arrays.sort(students,scoreComparator);
通过姓名比较:
package demo4;import java.util.Comparator;public class NameCompartor implements Comparator {@Overridepublic int compare(Student o1, Student o2) {return (o1.name.compareTo(o2.name));}
}
NameCompartor nameCompartor = new NameCompartor();Arrays.sort(students,nameCompartor);
以上就是非常灵活的比较器,想怎么写就怎么写。


@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
①如果没有重写toString方法,运行结果:

②如果重写了toString方法,运行结果:

public boolean equals(Object obj) {return (this == obj);}
如果要比较对象中内容是否相同,必须重写Object中的equals方法,因为equals方法默认是按照地址比较的。
在创建2个对象,分别为(姓名和年龄都一样):
姓名:小王,年龄22;姓名:小王,年龄22
public static void main(String[] args) {Student student1 = new Student("小王",22);Student student2 = new Student("小王",22);System.out.println(student1.equals(student2));}
①没有重写equals,运行结果:

分析:因为2个对象的姓名和年龄都相同,在主观现实世界中,我们会认为这2个对象是同一个对象,但是程序的运行结果却是flase,因此我们要重写Objec类中的equals方法。
②重写equals,运行结果:
@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null) {return false;}if (! (o instanceof Student)) {return false;}Student s = (Student) o;if (this.name == s.name && this.age == s.age) {return true;}return false;}
运行结果:

编译器自动重写的equals方法:
@Overridepublic 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);}
System.out.println(student1);System.out.println(student2);
①没有重写hashCode方法的时候,运行结果:

②重写hashCode方法的运行结果:
@Overridepublic int hashCode() {return Objects.hash(name, age);}

分析:通过name和age去返回哈希地址,因为student1和student2的name和age都相同,因此打印出来的地址也是相同的。
总结:hashcode方法用来确定对象在内存中存储的位置是否相同

@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
public static void main(String[] args) throws CloneNotSupportedException {Student student1 = new Student("小帅");Student student2 = (Student) student1.clone();System.out.println(student1);System.out.println(student2);}
整体代码:
package demo5;class Student implements Cloneable{public String name;public Student(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}public class Test {//学习自定义类型克隆拷贝public static void main(String[] args) throws CloneNotSupportedException {Student student1 = new Student("小帅");Student student2 = (Student) student1.clone();System.out.println(student1);System.out.println(student2);}
}
运行结果:

分析:目前是可以对自定义类型进行拷贝了,但是拷贝分为浅拷贝和深拷贝。

class Money {public double money = 12.5;
}
组合:每个学生都有钱,在Student类中实例化Money对象
class Student implements Cloneable{public String name;public Money m = new Money();public Student(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", m=" + m +'}';}
}
测试:
public class Test {//学习自定义类型克隆拷贝public static void main(String[] args) throws CloneNotSupportedException {Student student1 = new Student("小帅");Student student2 = (Student) student1.clone();System.out.println(student1.m.money);System.out.println(student2.m.money);student2.m.money = 99;System.out.println("修改拷贝对象的money之后的打印结果");System.out.println(student1.m.money);System.out.println(student2.m.money);}
}
运行结果:

分析:修改拷贝对象student2的值money,原来的student1的值也发生了改变,则是浅拷贝,因为只对student1拷贝,并没有把money也拷贝一份。

class Money implements Cloneable{public double money = 12.5;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
②Student类重写clone方法
@Overrideprotected Object clone() throws CloneNotSupportedException {Student student = (Student) super.clone(); //得到一个浅拷贝对象student.m = (Money) this.m.clone(); //浅拷贝的对象的m要再次拷贝,从而实现深拷贝//this 谁调用这个方法就指谁,这里指student1return student;}

分析:此时实现了深拷贝,Student student = (Student) super.clone();只是克隆了student对象, student.m = (Money) this.m.clone();克隆了student对象里面的money对象。

所以如要要实现深拷贝,当前对象的每一个对象都要克隆。
package demo6;class Dog {}public class Test {public static void func(Object o) {System.out.println("wow");}public static void main(String[] args) {func(new Dog());Dog dog = new Dog();func(dog);}
}
所有类的对象都可以使用Object的引用进行接受public static void func(Object o)。
当没有给其他引用的时候,这些都是匿名对象,形如: func(new Dog());即为匿名对象,缺点是每次使用都需要new,使用场景是只用一次的时候。
上一篇:如何下载赞美诗网的歌曲