一、抽象类(Abstract Class)

1. 什么是抽象类?

抽象类是对所有子类公有方法的共性提取,把子类都会实现的、重复的方法,抽取到父类中统一定义,避免代码冗余。

当父类本身不应该被实例化(比如 Animal 只是动物的概念,不应该直接创建 Animal 对象),且只需要定义子类必须实现的方法规范时,就可以把父类定义为抽象类。

2. 抽象类与抽象方法的定义

  • 抽象类:用 abstract 关键字修饰的类,不能被实例化(不能直接 new)。
  • 抽象方法:用 abstract 关键字修饰的方法,没有方法体(连大括号都没有,直接以分号结尾),定义在抽象类中,强制子类必须重写实现。

代码示例

// 抽象父类:动物
abstract class Animal {
    // 抽象方法:run,没有方法体,强制子类实现
    public abstract void run();

    // 抽象类也可以有普通方法(非抽象方法)
    public void sleep() {
        System.out.println("动物都需要睡觉");
    }
}

// 子类:猫,必须实现抽象父类的所有抽象方法
class Cat extends Animal {
    @Override
    public void run() {
        System.out.println("小猫在跑");
    }
}

// 子类:狗,必须实现抽象父类的所有抽象方法
class Dog extends Animal {
    @Override
    public void run() {
        System.out.println("小狗在跑");
    }
}

3. 抽象类的核心特点

  1. 只有抽象类中才能包含抽象方法,普通类中不能定义抽象方法。
  2. 抽象方法必须在子类中重写实现,否则子类也必须声明为抽象类。
  3. 抽象类可以包含普通方法、构造方法、成员变量,但不能被实例化。
  4. 抽象类存在的意义:作为模板父类,强制子类遵循统一的方法规范,同时实现代码复用。
  5. 抽象类可以使用多态:Animal animal = new Cat();,调用方法时会执行子类重写后的实现。
  6. 修饰符冲突规则:
    • finalabstract 不能同时修饰类 / 方法:final 禁止继承 / 重写,abstract 强制继承 / 重写,两者矛盾。
    • privateabstract 不能同时修饰方法:private 方法子类不可见,无法重写。
    • staticabstract 不能同时修饰方法:static 方法属于类,不依赖对象,而抽象方法需要子类实例重写实现。

二、接口(Interface)

1. 为什么需要接口?

Java 不支持类的多继承(一个类只能有一个直接父类),但多继承会带来 “菱形继承” 的歧义问题(比如多个父类有同名方法,子类不知道该调用哪个)。

为了解决 “无法多继承” 和 “多继承歧义” 的矛盾,Java 引入了接口

  • 接口本身不包含具体实现,不会造成代码调用混乱。
  • 一个类可以实现多个接口,从而获得多个接口定义的行为规范。

2. 接口的定义与核心特性

  • 接口用 interface 关键字定义,本身不能被实例化。
  • 接口中的成员变量默认是 public static final(静态常量,必须初始化)。
  • 接口中的方法默认是 public abstract(抽象方法,没有方法体,强制实现类重写)。
  • 接口没有构造方法。
  • 一个类实现接口时,必须重写接口中所有的抽象方法;如果不重写,该类必须声明为抽象类。
  • 接口与接口之间可以多继承,用 extends 关键字。

代码示例

// 接口:定义“可奔跑”的行为规范
interface Runnable {
    // 静态常量(默认 public static final)
    int SPEED = 10;

    // 抽象方法(默认 public abstract)
    void run();
}

// 接口:定义“可叫”的行为规范
interface Barkable {
    void bark();
}

// 实现类:狗,同时实现多个接口
class Dog implements Runnable, Barkable {
    @Override
    public void run() {
        System.out.println("小狗以" + SPEED + "km/h的速度奔跑");
    }

    @Override
    public void bark() {
        System.out.println("小狗汪汪叫");
    }
}

// 接口继承示例:Person接口继承Animal接口
interface Animal {
    void eat();
}

interface Person extends Animal {
    void study();
}

// 实现类:Student,必须实现Person和Animal的所有抽象方法
class Student implements Person {
    @Override
    public void eat() {
        System.out.println("学生需要吃饭");
    }

    @Override
    public void study() {
        System.out.println("学生需要学习");
    }
}

3. 抽象类 vs 接口

对比维度 抽象类(Abstract Class) 接口(Interface)
核心定位 对 “类” 的抽象,提取子类的共性特征(属性 + 行为) 对 “行为” 的抽象,定义类需要具备的行为规范
继承 / 实现 类只能单继承一个抽象类 类可以实现多个接口
成员变量 可以包含普通成员变量 只能包含 public static final 静态常量
方法 可以包含抽象方法、普通方法、构造方法 只能包含抽象方法(JDK8+ 可包含默认方法 / 静态方法)
构造方法 有构造方法,供子类初始化父类使用 没有构造方法

三、对象排序:Comparable 接口

1. 为什么对象需要实现 Comparable 接口?

Arrays.sort() 可以直接对基本数据类型(如 int[]String[])排序,但无法直接对自定义类的对象数组排序。因为程序不知道两个对象之间如何比较大小,需要通过 Comparable 接口定义比较规则。

2. Comparable 接口的使用

  • Comparable 接口位于 java.lang 包中,只有一个抽象方法 compareTo(T o)
  • 自定义类实现 Comparable 接口,并重写 compareTo 方法,定义对象的比较规则。
  • 实现后,Arrays.sort() 就可以直接对该类的对象数组进行排序。

核心规则:compareTo 方法

this.compareTo(o) 的返回值决定排序规则:

  • 返回 正整数:表示 this > o,排序时 this 排在 o 后面(默认升序)。
  • 返回 0:表示 this == o
  • 返回 负整数:表示 this < o,排序时 this 排在 o 前面(默认升序)。

代码示例:按年龄升序排序

import java.util.Arrays;

// 自定义类:Cat,实现Comparable接口
class Cat implements Comparable<Cat> {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{name='" + name + "', age=" + age + "}";
    }

    // 重写compareTo方法,定义按age升序排序
    @Override
    public int compareTo(Cat o) {
        // 升序:this.age - o.age
        return this.age - o.age;

        // 降序:o.age - this.age
        // return o.age - this.age;
    }
}

public class SortTest {
    public static void main(String[] args) {
        Cat[] cats = {
            new Cat("咪咪", 3),
            new Cat("花花", 1),
            new Cat("小白", 2)
        };

        // 排序前
        System.out.println("排序前:" + Arrays.toString(cats));

        // 排序(调用重写后的compareTo方法)
        Arrays.sort(cats);

        // 排序后
        System.out.println("排序后:" + Arrays.toString(cats));
    }
}

四、自定义排序算法(泛型 / 接口实现通用排序)

1. 需求背景

Arrays.sort() 只能处理基本类型和实现了 Comparable 接口的对象。我们可以自己实现一个通用的冒泡排序 / 快速排序算法,支持所有实现了 Comparable 接口的对象数组排序。

2. 核心思路

  1. 方法参数用 Comparable[] 接收,而不是具体的 Cat[]Dog[],保证算法的泛化能力(支持所有实现了 Comparable 的类)。
  2. 利用 Comparable 接口的 compareTo 方法进行对象间的大小比较,替代基本类型的 >/< 运算符。

代码示例:通用冒泡排序

public class MyArrays {
    // 通用冒泡排序:支持所有实现Comparable接口的对象数组
    public static void bubbleSort(Comparable[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }

        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                // 用compareTo方法比较,实现通用逻辑
                if (arr[j].compareTo(arr[j + 1]) > 0) {
                    // 交换位置
                    Comparable temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
}

// 测试:用自定义冒泡排序对Cat对象数组排序
public class MySortTest {
    public static void main(String[] args) {
        Cat[] cats = {
            new Cat("咪咪", 3),
            new Cat("花花", 1),
            new Cat("小白", 2)
        };

        System.out.println("自定义排序前:" + Arrays.toString(cats));
        MyArrays.bubbleSort(cats);
        System.out.println("自定义排序后:" + Arrays.toString(cats));
    }
}

3. 注意

  • 为什么要转成 Comparable[],不直接用 Object[]Object 类中没有 compareTo 方法,如果直接用 Object[] 接收数组,后续无法调用 compareTo 进行比较。而所有实现了 Comparable 接口的类,都可以向上转型为 Comparable 类型,保证算法的通用性。

  • 为什么不直接写死为 Cat[]如果写死为 Cat[],这个排序方法就只能处理 Cat 对象,无法复用在 DogStudent 等其他需要排序的类上。用 Comparable[] 接收,所有实现了该接口的类都可以使用同一个排序方法,大幅提升代码复用性。


五、易错点

  1. 抽象类不能被实例化:直接 new Animal() 会编译报错,只能通过多态创建子类对象。
  2. 接口的方法默认是 public abstract:实现类重写时必须用 public 修饰,否则会降低访问权限。
  3. compareTo 方法的返回值:升序和降序的返回值容易写反,记住核心规则:this.compareTo(o) > 0 时交换,就是升序排序。
  4. 泛化排序的类型转换:自定义排序算法中,不能直接用 Object[] 调用 compareTo,必须转成 Comparable[] 才能调用该方法。

 

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐