文章目录
前言
在面向对象编程(OOP)中,接口(Interface)和抽象类(Abstract Class)是两个非常重要的概念,它们用于定义类的行为并促进代码的复用和模块化。本文将重点讨论接口和抽象类的区别,并通过一个示例展示如何在Java中实现这些概念,以及如何在博客系统中应用sort方法。
一、接口(Interface)
1.特点
- 接口使用
interface
关键字定义。 - 接口中的所有成员变量默认是
public static final
修饰的,即公有的静态常量。 - 接口中的所有方法默认是
public abstract
修饰的,即抽象方法。 - 接口不能被实例化,因为没有构造器。
- 接口中的方法全部是抽象方法,与抽象类不同。
- 接口可以互相继承,实现代码的复用。
示例
package 接口;
public interface Eat {
void eat();
}
public interface Run {
void run();
}
二、抽象类(Abstract Class)
1.特点
- 由
abstract
修饰的类叫做抽象类,由abstract
修饰的方法叫做抽象方法。 abstract
修饰的抽象方法,不在抽象类当中去实现,更多的作为子类必须实现的方法的定义。- 抽象类中可以有抽象方法(
abstract
修饰),也可以有普通方法。 - 抽象类不能被实例化,因为没有构造器。
final
和abstract
不能同时使用,因为final
表示类不能被继承,而abstract
表示类必须被继承。- 抽象方法不能使用
static
,因为static
是针对类层次,抽象方法是针对对象层次的,所以不能一起使用。 - 子类继承抽象类后,如果不想实现抽象类中的抽象方法,那么子类必须也是抽象类。
- 抽象类一定是父类。
示例
package 接口;
public abstract class Animal {
public abstract void jump();
public abstract void drunk();
public void eat() {
System.out.println("动物在吃东西...");
}
}
2.类的实现
一个类可以实现多个接口,但只能继承一个抽象类。以下是Cow类的实现,它继承了Animal
抽象类并实现了Eat
和Run
接口。
package 接口;
public class Cow extends Animal implements Eat, Run {
@Override
public void jump() {
System.out.println("牛在跳...");
}
@Override
public void drunk() {
System.out.println("牛在喝水...");
}
@Override
public void eat() {
System.out.println("牛在吃草...");
}
@Override
public void run() {
System.out.println("牛在跑...");
}
public void flay() {
System.out.println("牛在飞...");
}
}
三、接口和抽象类的区别
1. 定义与修饰符
- 抽象类:使用
abstract
修饰的类称为抽象类。抽象类中可以包含抽象方法(使用abstract
修饰的方法)和普通方法。 - 接口:使用
interface
关键字定义。接口中的方法默认是public abstract
修饰的抽象方法,接口中的成员变量默认是public static final
修饰的常量。
2. 方法的实现
- 抽象类:抽象类中可以包含抽象方法(没有方法体)和普通方法(有方法体)。子类继承抽象类时,必须实现所有的抽象方法,除非子类也是抽象类。
- 接口:接口中的所有方法默认都是抽象方法,没有方法体。实现接口的类必须实现接口中的所有方法,除非该类是抽象类。
3. 构造器
- 抽象类:抽象类可以有构造器,但不能被实例化。构造器主要用于子类实例化时调用。
- 接口:接口没有构造器,不能被实例化。
4. 成员变量
- 抽象类:抽象类中可以定义普通成员变量和静态变量。
- 接口:接口中的成员变量默认是
public static final
修饰的常量,且必须初始化。
5. 继承与实现
- 抽象类:一个类只能继承一个抽象类(单继承),子类继承抽象类后,可以选择实现抽象方法或继续将子类声明为抽象类。
- 接口:一个类可以实现多个接口(多实现),接口之间也可以互相继承(多继承)。
6. 与 final
和 static
的关系
- 抽象类:抽象方法不能使用
static
修饰,因为抽象方法是针对对象层次的,而static
方法是针对类层次的。final
不能与abstract
同时使用,因为final
修饰的类不能被继承,final
修饰的方法不能被重写。 - 接口:接口中的方法不能使用
final
修饰,因为接口的方法必须由实现类来实现。接口中的方法默认是public abstract
的,不能是static
的。
7. 实例化
- 抽象类:抽象类不能被实例化,只能通过子类来实例化。
- 接口:接口不能被实例化,只能通过实现类来实例化。
四、Sort 方法:基于 Comparable 接口的自定义排序
1.实现Comparable接口进行排序
以下是一个完整的代码示例,展示了如何通过实现 Comparable 接口对 Person 对象进行排序
package 接口;
public class Person implements Comparable<Person> {
public String name;
public int age;
public double height;
public Person(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
@Override
public int compareTo(Person o) {
// 按 age 从小到大排序
// return this.age - o.age;
// 按 age 从大到小排序
// return o.age - this.age;
// 按 height 从小到大排序
return (int) (this.height - o.height);
}
}
在 Person 类中,我们实现了 Comparable 接口,并重写了 compareTo 方法。通过修改 compareTo 方法的返回值,可以实现不同的排序规则:
- 按 age 从小到大排序:return this.age - o.age;
- 按 age 从大到小排序:return o.age - this.age;
- 按 height 从小到大排序:return (int) (this.height - o.height);
2.自定义快速排序
虽然Java标准库提供了Arrays.sort()方法,但为了深入理解排序机制,我们可以自己实现一个排序算法,比如快速排序。
package 接口;
public class Arrays2 {
/**
* 对实现了 Comparable 接口的数组进行排序
*
* @param o 待排序的数组
*/
public static void sort(Comparable[] o) {
quickSort(o, 0, o.length - 1);
}
/**
* 快速排序算法
*
* @param arr 待排序的数组
* @param left 左边界
* @param right 右边界
*/
public static void quickSort(Comparable[] arr, int left, int right) {
if (left >= right) {
return;
}
int i = left;
int j = right;
// 定义基准值
Comparable base = arr[left];
while (i != j) {
// 从右向左找小于基准值的元素
while (arr[j].compareTo(base) >= 0 && i < j) {
j--;
}
// 从左向右找大于基准值的元素
while (arr[i].compareTo(base) <= 0 && i < j) {
i++;
}
// 交换元素
Comparable temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
// 将基准值放到正确的位置
arr[left] = arr[i];
arr[i] = base;
// 递归排序左半部分
quickSort(arr, left, i - 1);
// 递归排序右半部分
quickSort(arr, i + 1, right);
}
}
在 Test类中,我们创建了一个 Person 数组,并分别使用 Arrays.sort 和自定义的 Arrays2.sort 进行排序。通过输出结果,可以验证排序的正确性。
package 接口;
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Person p1 = new Person("小黑", 18, 189.5);
Person p2 = new Person("小白", 22, 185.5);
Person p3 = new Person("小虎", 20, 184.5);
Person p4 = new Person("小何", 24, 185.5);
// 创建 Person 数组
Person[] arrPersons = new Person[]{p1, p2, p3, p4};
// 使用 Arrays.sort 进行排序
Arrays.sort(arrPersons);
System.out.println("Arrays.sort 排序结果:" + Arrays.toString(arrPersons));
// 使用自定义的 Arrays2.sort 进行排序
Arrays2.sort(arrPersons);
System.out.println("Arrays2.sort 排序结果:" + Arrays.toString(arrPersons));
}
}
结果
Arrays.sort 排序结果:[Person{name='小虎', age=20, height=184.5}, Person{name='小白', age=22, height=185.5}, Person{name='小何', age=24, height=185.5}, Person{name='小黑', age=18, height=189.5}]
Arrays2.sort 排序结果:[Person{name='小虎', age=20, height=184.5}, Person{name='小白', age=22, height=185.5}, Person{name='小何', age=24, height=185.5}, Person{name='小黑', age=18, height=189.5}]
总结
接口和抽象类在Java中扮演着不同的角色,用于实现代码复用和模块化设计。通过理解它们的特性和使用场景,开发者可以更加灵活地设计系统,提高代码的可维护性和可扩展性。
通过实现 Comparable 接口,我们可以轻松地为自定义类定义排序规则。同时,通过自定义排序算法(如快速排序),我们可以更深入地理解排序的原理和实现方式。
希望本文能帮助你更好地理解 Java 中的排序机制!如果有任何问题,欢迎留言讨论。