Java 抽象类 vs 接口:相同点与不同点

🎯 快速对比表

特性 抽象类 接口
关键字 abstract class interface
实例化 ❌ 不能 ❌ 不能
继承/实现 单继承 多实现
方法类型 抽象 + 具体 默认只有抽象(Java 8前)
构造器 ✅ 有 ❌ 无
变量 任意类型 只能是常量
访问修饰符 任意 默认 public
设计目的 代码复用 + 规范 定义契约/能力

🔍 相同点

1. 都不能被实例化

java 复制代码
// 抽象类不能实例化
abstract class AbstractClass {
    abstract void method();
}

// 接口不能实例化
interface Interface {
    void method();
}

public class CannotInstantiate {
    public static void main(String[] args) {
        // ❌ 两者都不能直接new
        // AbstractClass obj1 = new AbstractClass();  // 编译错误
        // Interface obj2 = new Interface();         // 编译错误
    }
}

2. 都可以有抽象方法

java 复制代码
abstract class AnimalClass {
    // 抽象类可以有抽象方法
    public abstract void makeSound();
}

interface AnimalInterface {
    // 接口方法默认是抽象的
    void makeSound();  // 等价于 public abstract void makeSound()
}

// 子类/实现类必须实现抽象方法
class Dog extends AnimalClass {
    @Override
    public void makeSound() {
        System.out.println("汪汪");
    }
}

class Cat implements AnimalInterface {
    @Override
    public void makeSound() {
        System.out.println("喵喵");
    }
}

3. 都支持多态

java 复制代码
abstract class Shape {
    abstract void draw();
}

interface Drawable {
    void draw();
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("画圆形");
    }
}

class Square implements Drawable {
    @Override
    public void draw() {
        System.out.println("画正方形");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        // 抽象类的多态
        Shape shape = new Circle();
        shape.draw();  // 画圆形
        
        // 接口的多态
        Drawable drawable = new Square();
        drawable.draw();  // 画正方形
        
        // 都可以用数组统一管理
        Shape[] shapes = {new Circle()};
        Drawable[] drawables = {new Square()};
    }
}

⚡ 不同点

1. 继承 vs 实现(核心区别)

java 复制代码
// 单继承:一个类只能继承一个抽象类
abstract class Vehicle {
    abstract void move();
}

abstract class Engine {
    abstract void start();
}

class Car extends Vehicle {
    // ✅ 正确:只能继承一个抽象类
    @Override
    void move() {
        System.out.println("汽车行驶");
    }
}

/*
class HybridCar extends Vehicle, Engine {  
    // ❌ 错误:不能继承多个抽象类
}
*/

// 多实现:一个类可以实现多个接口
interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

class Duck implements Flyable, Swimmable {
    // ✅ 正确:可以实现多个接口
    @Override
    public void fly() {
        System.out.println("鸭子飞");
    }
    
    @Override
    public void swim() {
        System.out.println("鸭子游");
    }
}

// 组合:可以继承一个类 + 实现多个接口
class AmphibiousVehicle extends Vehicle implements Flyable, Swimmable {
    @Override
    void move() {
        System.out.println("两栖车移动");
    }
    
    @Override
    public void fly() {
        System.out.println("两栖车飞行模式");
    }
    
    @Override
    public void swim() {
        System.out.println("两栖车游泳模式");
    }
}

2. 方法实现能力(Java 8+)

java 复制代码
abstract class AbstractExample {
    // 1. 可以有抽象方法
    public abstract void abstractMethod();
    
    // 2. 可以有具体方法(一直都可以)
    public void concreteMethod() {
        System.out.println("抽象类的具体方法");
    }
    
    // 3. 可以有构造器
    public AbstractExample() {
        System.out.println("抽象类构造器");
    }
    
    // 4. 可以有静态方法
    public static void staticMethod() {
        System.out.println("抽象类静态方法");
    }
    
    // 5. 可以有final方法
    public final void finalMethod() {
        System.out.println("抽象类final方法");
    }
    
    // 6. 可以有private方法
    private void privateMethod() {
        System.out.println("抽象类私有方法");
    }
}

interface InterfaceExample {
    // 1. 抽象方法(Java 8前唯一允许的方法)
    void abstractMethod();
    
    // 2. 默认方法(Java 8+)
    default void defaultMethod() {
        System.out.println("接口的默认方法");
        privateMethod();  // 调用私有方法
    }
    
    // 3. 静态方法(Java 8+)
    static void staticMethod() {
        System.out.println("接口的静态方法");
    }
    
    // 4. 私有方法(Java 9+)
    private void privateMethod() {
        System.out.println("接口的私有方法");
    }
    
    // 5. 私有静态方法(Java 9+)
    private static void privateStaticMethod() {
        System.out.println("接口的私有静态方法");
    }
    
    // ❌ 不能有构造器
    // InterfaceExample() { }  // 编译错误
    
    // ❌ 不能有final方法
    // final void finalMethod();  // 编译错误
}

class Implementation implements InterfaceExample {
    @Override
    public void abstractMethod() {
        System.out.println("实现抽象方法");
    }
}

3. 变量/字段的区别

java 复制代码
abstract class AbstractVars {
    // 1. 可以有实例变量
    protected String instanceVar = "实例变量";
    
    // 2. 可以有非常量
    private int counter = 0;
    
    // 3. 可以有静态变量
    public static String staticVar = "静态变量";
    
    // 4. 可以有final变量
    public final String finalVar = "final变量";
    
    // 5. 访问修饰符可以是任意的
    private String privateVar = "私有变量";
    protected String protectedVar = "受保护变量";
    String defaultVar = "默认变量";
    public String publicVar = "公共变量";
}

interface InterfaceVars {
    // 1. 所有变量默认是 public static final
    String CONSTANT = "常量";
    
    // 等价于:
    // public static final String CONSTANT = "常量";
    
    // ❌ 不能有实例变量
    // String instanceVar;  // 编译错误
    
    // ❌ 不能有非final变量
    // int counter = 0;     // 实际上是常量
    
    // 2. 只能是public
    // private String privateVar = "错误";  // 编译错误
    // protected String protectedVar = "错误";  // 编译错误
}

public class VariablesDemo {
    public static void main(String[] args) {
        System.out.println("抽象类变量: " + AbstractVars.staticVar);
        System.out.println("接口常量: " + InterfaceVars.CONSTANT);
        
        // 接口常量不能修改
        // InterfaceVars.CONSTANT = "新值";  // 编译错误
    }
}

4. 构造器的区别

java 复制代码
abstract class AbstractWithConstructor {
    private String name;
    
    // 抽象类可以有构造器
    public AbstractWithConstructor(String name) {
        this.name = name;
        System.out.println("抽象类构造器调用: " + name);
    }
    
    public abstract void doSomething();
    
    public String getName() {
        return name;
    }
}

class ConcreteClass extends AbstractWithConstructor {
    // 子类必须调用父类构造器
    public ConcreteClass(String name) {
        super(name);  // 必须调用
    }
    
    @Override
    public void doSomething() {
        System.out.println(getName() + "在做事情");
    }
}

interface NoConstructor {
    // ❌ 接口不能有构造器
    // NoConstructor() { }  // 编译错误
    
    void method();
}

public class ConstructorDemo {
    public static void main(String[] args) {
        ConcreteClass obj = new ConcreteClass("测试");
        obj.doSomething();
    }
}

5. 访问修饰符的区别

java 复制代码
abstract class AccessModifiers {
    // 所有访问修饰符都可以用
    private void privateMethod() {}
    void defaultMethod() {}
    protected void protectedMethod() {}
    public void publicMethod() {}
    
    // 抽象方法不能是private
    // private abstract void privateAbstract();  // 编译错误
    
    protected abstract void protectedAbstract();
    public abstract void publicAbstract();
}

interface InterfaceAccess {
    // 接口方法默认是public
    void method();  // 等价于 public abstract void method()
    
    // Java 9+ 可以有private方法
    private void privateMethod() {
        System.out.println("接口私有方法");
    }
    
    // 不能是protected或默认
    // protected void protectedMethod();  // 编译错误
    // void defaultMethod();              // 编译错误(Java 8前)
    
    // Java 8+ 默认方法可以是public
    default void defaultPublicMethod() {
        System.out.println("默认公共方法");
    }
}

💡 何时使用抽象类 vs 接口

1. 使用抽象类的情况

java 复制代码
// 场景1:多个相关类共享代码
abstract class Database {
    // 共享的连接逻辑
    protected Connection connect(String url) {
        System.out.println("连接到: " + url);
        return new Connection();
    }
    
    // 共享的关闭逻辑
    protected void close(Connection conn) {
        System.out.println("关闭连接");
    }
    
    // 模板方法
    public final void executeQuery(String sql) {
        Connection conn = connect(getUrl());
        try {
            execute(sql, conn);
        } finally {
            close(conn);
        }
    }
    
    // 子类必须实现
    protected abstract String getUrl();
    protected abstract void execute(String sql, Connection conn);
}

class MySQLDatabase extends Database {
    @Override
    protected String getUrl() {
        return "jdbc:mysql://localhost:3306/db";
    }
    
    @Override
    protected void execute(String sql, Connection conn) {
        System.out.println("执行MySQL查询: " + sql);
    }
}

class OracleDatabase extends Database {
    @Override
    protected String getUrl() {
        return "jdbc:oracle:thin:@localhost:1521:orcl";
    }
    
    @Override
    protected void execute(String sql, Connection conn) {
        System.out.println("执行Oracle查询: " + sql);
    }
}

2. 使用接口的情况

java 复制代码
// 场景1:定义能力/契约
interface Comparable<T> {
    int compareTo(T other);
}

interface Serializable {
    // 标记接口
}

interface Cloneable {
    // 标记接口
}

// 场景2:多重继承
interface Worker {
    void work();
}

interface Student {
    void study();
}

interface Athlete {
    void train();
}

class CollegeStudent implements Student, Worker, Athlete {
    @Override
    public void work() {
        System.out.println("兼职工作");
    }
    
    @Override
    public void study() {
        System.out.println("学习课程");
    }
    
    @Override
    public void train() {
        System.out.println("体育训练");
    }
}

// 场景3:回调机制
interface ClickListener {
    void onClick();
}

interface TextChangedListener {
    void onTextChanged(String newText);
}

class Button {
    private ClickListener listener;
    
    public void setClickListener(ClickListener listener) {
        this.listener = listener;
    }
    
    public void click() {
        if (listener != null) {
            listener.onClick();
        }
    }
}

3. 结合使用(常见模式)

java 复制代码
// 抽象类提供骨架实现
abstract class AbstractList<E> implements List<E> {
    // 提供通用实现
    public boolean add(E e) {
        add(size(), e);
        return true;
    }
    
    // 抽象方法由子类实现
    public abstract void add(int index, E element);
    public abstract E get(int index);
    public abstract int size();
}

// 接口定义契约
interface List<E> {
    boolean add(E e);
    void add(int index, E element);
    E get(int index);
    int size();
}

// 具体实现
class ArrayList<E> extends AbstractList<E> {
    private Object[] elements = new Object[10];
    private int size = 0;
    
    @Override
    public void add(int index, E element) {
        // 实现细节
        System.out.println("ArrayList添加元素");
    }
    
    @Override
    public E get(int index) {
        // 实现细节
        return null;
    }
    
    @Override
    public int size() {
        return size;
    }
}

public class CombinedUsage {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        System.out.println("大小: " + list.size());
    }
}

📝 现代Java中的变化(Java 8+)

接口的增强

java 复制代码
// Java 8 前:接口只能有抽象方法
interface OldInterface {
    void method();  // 只能是抽象方法
}

// Java 8+:接口可以有多样方法
interface ModernInterface {
    // 1. 抽象方法
    void abstractMethod();
    
    // 2. 默认方法
    default void defaultMethod() {
        System.out.println("默认实现");
        privateMethod();
    }
    
    // 3. 静态方法
    static void staticMethod() {
        System.out.println("静态方法");
        privateStaticMethod();
    }
    
    // 4. 私有方法(Java 9+)
    private void privateMethod() {
        System.out.println("私有实例方法");
    }
    
    // 5. 私有静态方法(Java 9+)
    private static void privateStaticMethod() {
        System.out.println("私有静态方法");
    }
}

// 现在接口 ≈ 抽象类,但仍有区别:
// 1. 接口仍然不能有构造器
// 2. 接口仍然不能有实例变量
// 3. 接口仍然支持多重继承

🎓 记忆口诀

markdown 复制代码
相同点:
1. 都不能new(不能实例化)
2. 都能有抽象方法
3. 都支持多态

不同点:
抽象类:
- 单继承
- 有构造器
- 有具体方法(一直都有)
- 有各种变量
- 设计:是什么(is-a)

接口:
- 多实现
- 无构造器
- 默认方法(Java 8+)
- 只有常量
- 设计:能什么(has-a/can-do)

💡 实用建议

选择指南

markdown 复制代码
问自己这些问题:

1. 需要多重继承吗?
   - 是 → 用接口
   - 否 → 继续

2. 需要共享代码吗?
   - 是 → 用抽象类
   - 否 → 用接口

3. 相关类有紧密关系吗?
   - 是 → 用抽象类
   - 否 → 用接口

4. 只是定义契约吗?
   - 是 → 用接口
   - 否 → 继续

5. 需要控制子类构造吗?
   - 是 → 用抽象类
   - 否 → 用接口

最佳实践

java 复制代码
// 优先使用接口(更加灵活)
public interface Repository<T> {
    void save(T entity);
    T findById(int id);
    List<T> findAll();
}

// 需要共享代码时使用抽象类
public abstract class AbstractRepository<T> implements Repository<T> {
    protected DatabaseConnection connection;
    
    public AbstractRepository(DatabaseConnection connection) {
        this.connection = connection;
    }
    
    // 共享的保存逻辑
    @Override
    public void save(T entity) {
        validate(entity);
        persist(entity);
        logSave(entity);
    }
    
    protected abstract void persist(T entity);
    
    private void validate(T entity) {
        // 验证逻辑
    }
    
    private void logSave(T entity) {
        // 日志逻辑
    }
}

// 具体实现
public class UserRepository extends AbstractRepository<User> {
    public UserRepository(DatabaseConnection connection) {
        super(connection);
    }
    
    @Override
    protected void persist(User entity) {
        // 具体持久化逻辑
    }
    
    @Override
    public User findById(int id) {
        // 具体查找逻辑
        return null;
    }
    
    @Override
    public List<User> findAll() {
        // 具体查找逻辑
        return new ArrayList<>();
    }
}

记住:现代Java开发中,优先考虑使用接口,只有在确实需要共享代码时才使用抽象类。接口提供了更大的灵活性和可扩展性!

相关推荐
expect7g41 分钟前
Paimon Branch --- 流批一体化之二
大数据·后端·flink
幌才_loong42 分钟前
.NET 8 实时推送魔法:SSE 让数据 “主动跑” 到客户端
后端
00后程序员1 小时前
如何解决浏览器HTTPS不安全连接警告及SSL证书问题
后端
00后程序员1 小时前
苹果App上架审核延迟7工作日无反应:如何通过App Store Connect和邮件询问进度
后端
DS小龙哥1 小时前
基于物联网设计的蜂箱智能监测系统设计
后端
QZQ541881 小时前
C++编译期计算
后端
饕餮争锋1 小时前
Spring内置的Bean作用域介绍
java·后端·spring
CryptoRzz1 小时前
美股 (US) 与 墨西哥 (Mexico) 股票数据接口集成指南
后端
张人大 Renda Zhang1 小时前
Java 虚拟线程 Virtual Thread:让“每请求一线程”在高并发时代复活
java·jvm·后端·spring·架构·web·虚拟线程