Java 接口和抽象类的共同点和区别

在Java中,接口(Interface)抽象类(Abstract Class) 都是用来提供类的模板、定义公共的行为规范的机制。尽管它们的目的类似,都可以被子类实现或继承,从而提供一定的功能扩展,但它们在使用场景、功能特性以及语法方面存在一些关键的区别。

1. 共同点

  • 都不能被实例化:接口和抽象类都不能直接创建对象。你不能通过 new 来实例化它们。
  • 都用于定义规范:接口和抽象类都用于定义类应该遵循的规范(方法)。子类可以通过实现接口或继承抽象类来实现这些规范。
  • 可以包含抽象方法:接口和抽象类都可以包含抽象方法(没有方法体的方法)。这些方法必须由子类实现。
  • 都支持多态:接口和抽象类都可以被用作多态的目标,可以通过接口类型或抽象类类型的引用指向具体类的对象。

2. 区别

特性 接口(Interface)抽象类(Abstract Class)
方法 只能有抽象方法(Java 8 引入了默认方法和静态方法)。 可以包含抽象方法,也可以包含普通方法(有方法体)。
属性 所有字段默认是 public static final,即常量。 可以包含实例变量(可以有不同的访问修饰符)。
多重继承 可以实现多个接口(支持多重继承)。 只能继承一个抽象类(不支持多重继承)。
构造器 没有构造器。 可以有构造器。
默认访问修饰符 默认是 public 默认是 protected
实现方式 类通过 implements 关键字来实现接口。 类通过 extends 关键字来继承抽象类。
适用场景 适用于定义行为规范,可以由多个不相关的类来实现。 适用于提供一个基础类的功能,允许部分方法有默认实现。

3. 接口(Interface)

接口通常用于定义一组公共的、标准的行为规范,任何类都可以通过实现接口来遵循这些规范。接口适合用于不相关的类之间共享公共行为,比如一个动物类与一个电子设备类都可以实现 Playable 接口。

代码示例(接口)

// 定义接口
interface Playable {
    void play();  // 接口中的方法默认是 public abstract 的
}

class Dog implements Playable {
    @Override
    public void play() {
        System.out.println("Dog is playing");
    }
}

class Robot implements Playable {
    @Override
    public void play() {
        System.out.println("Robot is playing");
    }
}

public class InterfaceExample {
    public static void main(String[] args) {
        Playable dog = new Dog();
        Playable robot = new Robot();
        
        dog.play();  // 输出: Dog is playing
        robot.play(); // 输出: Robot is playing
    }
}

说明

  • Playable 是一个接口,定义了 play() 方法。
  • DogRobot 类分别实现了 Playable 接口,并提供了自己的 play() 方法实现。
  • 接口的好处在于它可以被多个不相关的类实现,体现了 多态性解耦性

4. 抽象类(Abstract Class)

抽象类用于提供一个共享的基类,其他类可以继承并扩展它。抽象类允许在类中定义部分方法的实现,并保留一些方法作为抽象方法,供子类实现。抽象类适合用于提供通用的功能实现,同时也要求子类提供特定的实现。

代码示例(抽象类)

// 定义抽象类
abstract class Animal {
    private String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    // 抽象方法,必须由子类实现
    abstract void sound();
    
    // 普通方法,子类可以继承
    public void eat() {
        System.out.println(name + " is eating.");
    }
}

class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    void sound() {
        System.out.println("Cat meows");
    }
}

public class AbstractClassExample {
    public static void main(String[] args) {
        Animal dog = new Dog("Buddy");
        Animal cat = new Cat("Kitty");

        dog.sound();  // 输出: Dog barks
        dog.eat();    // 输出: Buddy is eating.

        cat.sound();  // 输出: Cat meows
        cat.eat();    // 输出: Kitty is eating.
    }
}

说明

  • Animal 是一个抽象类,定义了一个抽象方法 sound() 和一个普通方法 eat()
  • DogCat 类继承了 Animal 类,并实现了 sound() 方法。
  • 抽象类的好处在于它提供了共享的行为(如 eat()),并允许子类实现特定的行为(如 sound())。

5. 接口与抽象类的选择

特性/用途 接口(Interface)抽象类(Abstract Class)
继承方式 一个类可以实现多个接口 一个类只能继承一个抽象类
默认方法实现 从 Java 8 开始,接口可以有默认方法实现(default)。 抽象类可以有方法的具体实现
使用场景 用于定义多个不相关类的公共行为,适合多重继承的情况。 用于提供共享的基础类,适合有部分实现的场景。
访问控制符 默认所有方法为 public,字段为 public static final 可以有任何访问修饰符(privateprotectedpublic)。
构造器 无法定义构造方法。 可以有构造方法

选择实例

  1. 如果需要定义多个类之间共享的行为,并且不关心它们是否相关,使用接口。例如,你有多个类 CarBicycleScooter,它们可能都实现 Drivable 接口。

  2. 如果需要提供一个基础类,供多个子类继承,并且可以有部分方法的实现,使用抽象类。例如,你想定义一个基类 Shape,并为所有形状提供 area() 方法的默认实现,同时要求每个形状类实现 draw() 方法。

6. 总结

特性 接口(Interface)抽象类(Abstract Class)
方法 只能包含抽象方法或默认方法、静态方法 可以包含抽象方法和非抽象方法
字段 所有字段默认是 public static final 常量 可以包含实例变量,访问控制符可以是 privateprotectedpublic
构造器 没有构造器 可以有构造器
继承方式 一个类可以实现多个接口 一个类只能继承一个抽象类
适用场景 用于不同类之间共享行为规范,适合多重继承的需求 用于创建共享功能的基类,适合类之间有共同的特性时

接口和抽象类各自有其优势,选择使用哪个,主要取决于具体需求。如果类之间有公共行为需要实现,并且可能会涉及多重继承,则接口是更好的选择;如果有一些共享的行为需要提供,同时需要约束子类的行为,抽象类更为合适。

Java碎碎念 文章被收录于专栏

来一杯咖啡,聊聊Java的碎碎念呀

全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务