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的碎碎念呀

全部评论

相关推荐

04-14 16:54
已编辑
大连理工大学 Java
懂车帝 客户端一面 顺序是乱的,录音没录上,较长的问题忘记了很多整体感受:感觉自己像这个猴子🐒1.对于客户端有什么了解吗?如果不了解就不倾向聊,就聊后端了解过android hybrid flutter 嘛2.http和https的区别3.加密算法有什么了解的吗,具体讲一下这边拷打线程进程是真难受啊,有几个场景忘记了3.线程之间怎么交互4.进程和该进程下的线程怎么交互,如何进行数据交互脑子懵了,紧张死,直接说的Redis多线程可见,存储到Redis 中,此时面试官拷打是线程与进程交互,你了解Redis 的内存底层吗?5.你了解过什么设计模式6单例模式介绍7.单例模式是否线程安全8.单例模式用来干什么9.责任链模式不考虑业务,a b c d e顺序是否能改变10.责任链中插入一个f,怎么操作?只需要修改指针吗?11.模版方法模式是干什么的12.线程池怎么用的?你用来干什么?13.线程池怎么实现动态扩容,比如说此时有几个线程数量,突然来了了几十个任务,你怎么扩容14.线程安全的工具具体讲一下15.String StringBuilder StringBuffer的区别讲一下16.StringBuffer内部的实现?忘记了具体问题,只记得这个问题深挖底层源码,大脑宕机,拷打的我浑身难受17.具体说一下接口和抽象类的区别18.接口中只能定义抽象方法吗?19.抽象类的作用是什么20.接口的你主要用来干什么?21.继续让说接口和抽象类的区别,把八股背出来,想不出来别的了22.后续又拷打了一会接口和抽象类,脑子懵了,具体问题忘了23.token cookie session具体讲一下24.对这三个又拷打了一下底层,应用场景,问题太长了,脑子懵了,忘记了25.hashtable和hashmap怎么实现的,讲一下底层26.怎么将hashmap转换为线程安全的27.concurrenthashmap的底层28.后面的有一些忘记了,面试官后面有点想快点结束了,感觉寄了29.算法题 将字符串中的单词进行反转 acm模式,自己导包30.算法题遍历可以解决,有什么优化的方法吗?31.反问了学习建议和客户端的业务 32.对于这次面试的感受,面试官挺好的,但本人太菜了,仍需沉淀,对于八股应该要多深挖一下,紧张,大脑宕机
查看28道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务