1、java.lang.Comparable
int compareTo(Object obj): 自然排序接口
返回值类型是int,它的结果的特点:
如果调用compareTo方法的对象 大于()中的实参对象,那么返回正整数
如果调用compareTo方法的对象 小于()中的实参对象,那么返回负整数
如果调用compareTo方法的对象 等于()中的实参对象,那么返回0
什么时候会用到这个接口?
当需要比较两个对象的大小,就可以使用Comparable接口。
需求:想要定义一个数组工具类MyArrays,这个工具类中包含以下一些方法:
(1)可以给任意的int类型的数组排序,从小到大
(2)可以返回任意的int类型的数组的元素列表拼接的字符串结果
(3)可以给任意的double类型的数组排序,从小到大
(4)可以返回任意的double类型的数组的元素列表拼接的字符串结果
因为我们各种基本数据类型的数组之间是互不兼容的,所以如果要支持8种基本数据类型,需要重载8套这样的方法。
(5)可以给任意的对象类型(引用数据类型)的数组排序,从小到大,按照元素类型重写的Comparable接口的compareTo方法比较大小
(6)可以返回任意的对象类型(引用数据类型)的数组的元素列表拼接的字符串结果
(7)可以给任意的对象类型(引用数据类型)的数组排序,“从小到大”,按照Comparator接口的实现类的compare方法比较大小
public class TestInterface {
public static void main(String[] args) {
//String类型实现Comparable接口
String[] array3 = {"helloabc","hello","java","world","chai","atguigu"};
MyArrays.sort(array3);
System.out.println(MyArrays.toString(array3));
System.out.println("--------------------");
Student[] array4 = new Student[3];
array4[0] = new Student("张三",87);
array4[1] = new Student("李四",99);
array4[2] = new Student("王五",85);
MyArrays.sort(array4);
System.out.println(MyArrays.toString(array4));
}
}
class Student implements Comparable{
private String name;
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int compareTo(Object o) {
//this对象,和o对象的两个学生对象比较大小
/* if(this.score > ((Student)o).score){
return 1;
}else if(this.score < ((Student)o).score){
return -1;
}
return 0;*/
return this.score - ((Student)o).score;
/*
this.score > ((Student)o).score,差是正
this.score < ((Student)o).score,差是负
this.score = ((Student)o).score,差是0
*/
}
}
对象数组的比较大小?
(1)对象之间是不支持直接使用">,<,<=,>="运算符比较大小的,怎么办?
(2)形参Object[]数组,它是实参的对象数组不一定是什么类型的,可能是String[],可能是Student[]。。。。
Object[]的数组,可以接收任意的对象数组,但是就不能接收基本数据类型的数组。
(3)因此不知道对方想要按照什么规则比较大小。
例如:String[]想要按照Unicode编码值比较大小
Student[]想要按照成绩score比较大小
解决方案的讨论:
(1)发现Object类中没有“比较大小”的方法 :否决
(2)用instanceof判断,向下转型,比较大小,要考虑的类型太多了写不完:否决
(3)我们可以设计一个接口(标准),让想要使用这个sort(Object[] arr)排序的对象数组的元素类型
去遵循这个标准,实现这个标准。例如:java.lang.Comparable接口。
这点就有点像,电脑想要提供给任意的硬件设备一个连接方式,如果不给出标准的话,
就需要提供n种类型的插口,没头。
所以电脑想要简化自己的插口,就需要制定一个标准,例如:USB标准,自己和其他硬件厂商都遵循这个标准。
谁要用这个USB标准,谁就遵守并且实现(提供硬件和软件的支持),软件支持就是提供驱动程序。
Java制定了一个对象比较大小的标准接口,java.lang.Comparable接口。
然后所有要比较大小的对象类型,都可以实现这个Comparable接口,并且重写它的抽象方法。
这样的话,所有对象比较大小,就有了一个统一的方式。
java.lang.Comparable接口的抽象方法是int compareTo(Object o)。
换句话说,凡是要比较大小的对象类型,都要实现Comparable接口,都重写了compareTo方法,
它的对象就都可以调用compareTo方法。
import java.util.Comparator;
//数组工具类
public class MyArrays {
//(1)可以给任意的int类型的数组排序,从小到大
public static void sort(int[] arr){
for(int i=1; i<arr.length; i++){
for(int j=0; j<arr.length-i; j++){
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
// (2)可以返回任意的int类型的数组的元素列表拼接的字符串结果
public static String toString(int[] arr){
String result = "[";
for(int i=0; i<arr.length; i++){
if(i==0){
result += arr[i];
}else{
result += "," + arr[i];
}
}
result += "]";
return result;
}
// (3)可以给任意的double类型的数组排序,从小到大
public static void sort(double[] arr){
for(int i=1; i<arr.length; i++){
for(int j=0; j<arr.length-i; j++){
if(arr[j]>arr[j+1]){
double temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
//(4)可以返回任意的double类型的数组的元素列表拼接的字符串结果
public static String toString(double[] arr){
String result = "[";
for(int i=0; i<arr.length; i++){
if(i==0){
result += arr[i];
}else{
result += "," + arr[i];
}
}
result += "]";
return result;
}
// (5)可以给任意的对象类型(引用数据类型)的数组排序,“从小到大”,按照元素类型重写的Comparable接口的compareTo方法比较大小
public static void sort(Object[] arr){
for(int i=1; i<arr.length; i++){
for(int j=0; j<arr.length-i; j++){
/* if(arr[j] > arr[j+1]){ //arr[j]和arr[j+1]是引用数据类型,引用数据类型是不能直接比较大小的
if(arr[j] instanceof Student){
Student before = (Student) arr[j];
Student after = (Student) arr[j+1];
if(before.getScore() > after.getScore()) {
Object temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}*/
/*arr[j]和arr[j+1]要比较大小,
arr[j]和arr[j+1]的编译时类型是Object,
需要arr[j]和arr[j+1]的对象的运行时类型实现Comparable接口,
既然它们实现了这个接口,我们就可以把arr[j]向下转型为Comparable接口类型,
向下转型的目的是为了调用compareTo方法,否则就调不了,因为Object类型中没有compareTo方法。
这里没有把arr[j+1]向下转型的原因是compareTo方法的形参是Object类型,可以直接接收arr[j+1]*/
Comparable before = (Comparable) arr[j];
/*如果arr[j],就是这里的before,大于arr[j+1],那么compareTo的返回值结果就是正整数.
compareTo是虚方法,before编译时类型现在是Comparable类型
运行时类型,要看调用sort方法的实参数组的类型,
当是String[]排序时,before的运行时类型是String,compareTo执行的就是String类中compareTo方法。
当是Student[]排序时,before的运行时类型是Student,compareTo执行的就是Student类中compareTo方法。
*/
if(before.compareTo(arr[j+1])>0){
Object temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//(6)可以返回任意的对象类型(引用数据类型)的数组的元素列表拼接的字符串结果
public static String toString(Object[] arr){
String result = "[";
for(int i=0; i<arr.length; i++){
if(i==0){
result += arr[i];
}else{
result += "," + arr[i];
}
}
result += "]";
return result;
}
//(7)可以给任意的对象类型(引用数据类型)的数组排序,“从小到大”,按照Comparator接口的实现类的compare方法比较大小
// 形参Object[] arr的作用是接收一个对象类型(引用数据类型)的数组,多态数组
// 形参Comparator comparator的作用是接收一个照Comparator接口的实现类对象,多态参数
public static void sort(Object[] arr, Comparator comparator){
for(int i=1; i<arr.length; i++){
for(int j=0; j<arr.length-i; j++){
/* if(arr[j] > arr[j+1]){ //arr[j]和arr[j+1]是引用数据类型,引用数据类型是不能直接比较大小的
//这里comparator是一个Comparator接口类型的一个变量,它调用的compare方法是一个抽象方法,
//最终执行的方法体代码,要看 形参comparator接收的实参的类型是什么,找对应类型的compare方法实现*/
if(comparator.compare(arr[j],arr[j+1])>0){
Object temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
2、java.util.Comparator接口
int compare(Object o1, Object o2) 定制排序接口
Comparator接口相当于是对Comparable接口的一个补充。即当我们存在多种排序要求时,默认的排序一般选择Comparable接口的实现,我们称为自然排序。
其他的排序一般就选择Comparator接口的实现,我们称为定制排序。
Comparator接口的抽象方法:int compare(Object o1, Object o2)
当o1对象大于o2对象时,返回正整数
当o1对象小于o2对象时,返回负整数
当o1对象等于o2对象时,返回0
需求:
有一个员工类Employee,它包含id,name,salary,age等。
(1)默认情况下,Employee的对象数组是按照id排序。从小到大。
(2)财务部门,想要把Employee按照薪资高低排一下顺序,(从高到低),薪资相同的,按照name排序。
(3)人事部分,想要把Employee按照年龄大小排序,从小到大
都想要排序,怎么办?
问题:Employee类中实现了Comparable接口,重写了compareTo方法,但是它是按照id排序的,
无法满足我们更多的其他排序要求。如何使用Comparator接口?
单独声明一个类型,实现 Comparator接口。
有几种排序情况,就写几个Comparator接口的实现类即可。这叫定制。
import java.util.Comparator;
public class TestInterface2 {
public static void main(String[] args) {
Employee[] arr = new Employee[5];
arr[0] = new Employee(2,"lisi",14000,24);
arr[1] = new Employee(1,"zhangsan",15000,23);
arr[2] = new Employee(3,"wangwu",11000,22);
arr[3] = new Employee(5,"zhaoliu",15000,25);
arr[4] = new Employee(4,"laowang",18000,22);
/* System.out.println(MyArrays.toString(arr));//显示一行太长,可以自己遍历*/
/*按照id排序从小到大
//想起,我们刚刚写的MyArrays数组工具类,
// 有一个public static void sort(Object[] arr)方法,可以实现对象的排序
//但是要求调用这个sort方法的对象数组的元素类型,必须实现Comparable接口,重写compareTo方法*/
MyArrays.sort(arr);
System.out.println("按照id升序排列的结果:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println("-----------------------");
//财务部门,想要把Employee按照薪资高低排一下顺序,(从高到低),薪资相同的,按照name排序。
MyArrays.sort(arr, new EmployeeSalaryComparator());
System.out.println("按照薪资降序排列的结果:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println("-------------------------");
//(3)人事部分,想要把Employee按照年龄大小排序,从小到大
MyArrays.sort(arr, new EmployeeAgeComparator());
System.out.println("按照年龄升序排列的结果:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
class EmployeeAgeComparator implements Comparator{
@Override
public int compare(Object o1, Object o2) {
Employee e1 = (Employee) o1;
Employee e2 = (Employee) o2;
int result = e1.getAge() - e2.getAge();
return result == 0 ? e1.getName().compareTo(e2.getName()): result;
}
}
class EmployeeSalaryComparator implements Comparator{
@Override
public int compare(Object o1, Object o2) {
Employee e1 = (Employee) o1;
Employee e2 = (Employee) o2;
int result = Double.compare(e1.getSalary(), e2.getSalary());//Double是包装类,它有一个compare方法,比较两个小数
/*e1.getSalary() > e2.getSalary(),result结果是正整数
//e1.getSalary() < e2.getSalary(),result结果是负整数
//e1.getSalary() = e2.getSalary(),result结果是0
if(result==0){
return e1.getName().compareTo(e2.getName());//因为e1.getName()是String类型,String类型是实现了Comparable接口的,有compareTo方法
}else{
return result;
}*/
return result==0 ? e1.getName().compareTo(e2.getName()) : -result;
//这里:后面写 -result,是在调用sort方法时实现降序的效果
}
}
class Employee implements Comparable{
private int id;
private String name;
private double salary;
private int age;
public Employee() {
}
public Employee(int id, String name, double salary, int age) {
this.id = id;
this.name = name;
this.salary = salary;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
return this.id - ((Employee)o).id;
}
}
3、java.lang.Cloneable接口:使用的不是很多,但是因为Object类所有类型的根类型,它里面有一个clone()方法和这个接口有关。
java.lang.Object类
protected Object clone()throws CloneNotSupportedException
当子类没有实现Cloneable接口,就算重写clone方法也会报CloneNotSupportedException异常,
换句话说,子类需要调用这个方法,就要实现Cloneable接口,并重写clone()方法。
Cloneable接口中,没有看到抽象方法,因为这个接口的抽象方法是clone()方法,它已经在根父类Object中声明了,
表示所有类型都有这个方法了,它就不需要在这个接口中声明了,如果子类想要重写的话,
直接重写Object类中的即可,但是子类还得实现Cloneable接口。
public class TestCloneable {
public static void main(String[] args) throws CloneNotSupportedException {
//创建Person的对象
Person p1 = new Person("张三",23);
//clone()是Object类的,那么任意对象都应该有这个方法
Object cloneObj = p1.clone();//如果Person类没有重写clone,那么这样访问报错,因为clone方法的权限修饰符是protected的
System.out.println("p1="+p1);
System.out.println("cloneObj="+cloneObj);
System.out.println(p1 == cloneObj);//false,原对象与副本对象的地址是不同的,因为是两个对象,但是它们的属性内容是一样的
System.out.println(p1.equals(cloneObj));//false,如果Person类没有重写equals方法,效果和==比较是一样,除非重写
}
}
class Person implements Cloneable{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
//这里重写的目的是为了扩大clone方法的可见性范围,把protected变为public
//如果对象的类不支持 Cloneable 接口,则重写 clone 方法的子类也会抛出CloneNotSupportedException异常,以指示无法复制某个实例。
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();//调用父类Object被重写的clone()
}
}