接口和抽象类的区别与使用场景

本文探讨了Java中接口与抽象类的基本概念、声明、应用场景和区别。抽象类不能实例化,用于定义一组抽象方法,子类必须实现这些方法。接口则包含变量和抽象方法,支持多继承,提供了定义模块间通信契约的机制。在选择使用抽象类还是接口时,通常考虑是否需要默认实现以及是否需要多继承。举例来说,门的open(), close()行为适合抽象类,而报警功能更适合设计为接口。" 111965010,10551649,数据库连接池详解:原理、运行机制与最佳实践,"['数据库连接池', 'C#编程', '数据库管理', '性能优化', '并发编程']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:
前几天面试的是面试官问了一个问题

接口和抽象类了解吧?
说一下 他们的分别用在什么场景下(就是让说一下我该如何选择使用接口还是抽象类)

我回答的不忍直视。
我事后问了一个同学,你给我来了一句那要看业务
我不知道看业务吗,我想知道的是啥业务用接口啥业务用抽象类

吐槽完毕开始正题

Java基础之接口与抽象类的区别

Java中接口和抽象类的定义语法分别为interface与abstract关键字。

抽象类: 在Java中被abstract关键字修饰的类称为抽象类,被abstract关键字修饰的方法称为抽象方法,抽象方法只有方法的声明,没有方法体。抽象类的特点:

a、抽象类不能被实例化只能被继承;

b、包含抽象方法的一定是抽象类,但是抽象类不一定含有抽象方法;

c、抽象类中的抽象方法的修饰符只能为public或者protected,默认为public;

d、一个子类继承一个抽象类,则子类必须实现父类抽象方法,否则子类也必须定义为抽象类;

e、抽象类可以包含属性、方法、构造方法,但是构造方法不能用于实例化,主要用途是被子类调用。

接口:Java中接口使用interface关键字修饰,特点为:

a、接口可以包含变量、方法;变量被隐士指定为public static final,方法被隐士指定为public abstract(JDK1.8之前);

b、接口支持多继承,即一个接口可以extends多个接口,间接的解决了Java中类的单继承问题;

c、一个类可以实现多个接口;

d、JDK1.8中对接口增加了新的特性:(1)、默认方法(default method):JDK 1.8允许给接口添加非抽象的方法实现,但必须使用default关键字修饰;定义了default的方法可以不被实现子类所实现,但只能被实现子类的对象调用;如果子类实现了多个接口,并且这些接口包含一样的默认方法,则子类必须重写默认方法;(2)、静态方法(static method):JDK 1.8中允许使用static关键字修饰一个方法,并提供实现,称为接口静态方法。接口静态方法只能通过接口调用(接口名.静态方法名)。

1.基本概念和声明

1.1抽象类

抽象类是特殊的类,只是不能被实例化,天生就是要被继承的;除此以外,具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们。另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖,如果不覆盖,则其派生类必须覆盖它们
抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。

一个简单的声明

public abstract class Door {  //定义抽象类
    private String brand;//品牌
    
    private double price;//价格

    public abstract void open();

    public abstract void close();

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样,同样可以拥有成员变量和普通的成员方法。注意,抽象类和普通类的主要有三点区别:
  1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。

2)抽象类不能用来创建对象;

3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。

1.2接口

接口,英文称作interface,在软件工程中,接口泛指供别人调用的方法或者函数。从这里,我们可以体会到Java语言设计者的初衷,它是对行为的抽象。在Java中,定一个接口的形式如下:

public interface Alarm {
    public static final String  effect ="提醒/警告";//相当于常量
    void alarm();
    default void alarmMode()
    {
        System.out.println("叫");
    };
}

接口中可以含有 变量和方法。但是要注意,接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误),并且接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法(特别提醒1.8后接口也有可以非抽象方法了
但是要用 default修饰的方法
)。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量。

1.3 应用场景介绍

什么时候使用抽象类和接口?

  • 如果拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
    对用一类事物共有的方法可以定义成抽象方法

    场景:
    如 AbstractQueuedSynchronize 这个抽象类
    继承这个类的时候你不用考虑线程的插入排队和休眠
    只需要考虑获取的实现就行
    
    如果你是定义成接口的话
    所有的 方法都需要你去自己实现
    (那些CAS的方法和线程等待的方法是可以共用的)
    
  • 如果想实现多重继承,那么必须使用接口。由于Java不支持多继承,即一个类 只能有一个超类。但是,可以实现多个接口,因此可以使用接口来解决它。

     有以下一个场景
     我想声明一个接口或者抽象类 但是要继承这个类子类的之类已经有父类
     则个时候就不能在声明抽象类了(就算声明了你也用不了) 
    
  • 如果基本功能在不断改变,那么就需要使用抽象类,达到解耦目的。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。

    这种一般是在项目初期才会出现吧 
    因为开闭原则:软件实体应当对扩展开放,对修改关闭。
    (在理想的状态下,当我们需要为一个软件系统增加新功能时,
    只需要从原来的系统派生出一些新类就可以,不需要修改原来的任何一行代码。)
    

接口更多的是在系统架构设计方面发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用。例如,模板方法设计模式就是抽象类的一个典型应用,假设某个项目的所有HTTP请求都要用相同的方式进行权限判断、访问日志记录和异常处理,那么就可以定义一个抽象的基类,让所有的controller都继承这个抽象基类,在抽象基类的service方法中实现上述功能,在各个子类中只是完成各自的业务逻辑代码,伪代码如下:

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 

public abstract class BaseServlet extends HttpServlet {
   public final void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

 // 记录访问日志
 // 进行权限判断
} 

 protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
 // 注意访问权限定义成protected,显得既专业,又严谨,因为它是专门给子类用的
 } 

 class MyServlet1 extends BaseServlet {
 protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
 // 本Servlet只处理的具体业务逻辑代码
 }

 }

基类方法中间的某段代码不确定,留给子类去实现。

1.4 举例说明

下面看一个网上流传最广泛的例子——门和警报——门都有open()和close( )两个动作,通过抽象类和接口来定义这个抽象概念。

public abstract class Door {  //定义抽象类
    private String brand;//品牌

    private double price;//价格

    public Door() {

    }

    public Door(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }

    public abstract void open();//抽象开门方法

    public abstract void close();//抽象关门方法

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

但是现在如果需要门具有报警alarm()的功能,那么该如何实现?下面提供两种思路:

  • 将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;
  • 将这三个功能都放在接口里面,需要用到报警功能的类就实现接口中的open()和close(),也许这个类根本就不具备open()和close()功能,比如火灾报警器。

    从这里可以看出,Door的open()、 close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm)属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。

public interface Alarm {
    public static final String  effect ="提醒/警告";
    void alarm();
    default void alarmMode()
    {
        System.out.println("叫");
    };
}
public abstract class Door {  //定义抽象类
    private String brand;//品牌

    private double price;//价格

    public Door() {

    }

    public Door(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }

    public abstract void open();//抽象开门方法

    public abstract void close();//抽象关门方法

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

public class SecurityDoor extends Door implements Alarm {

    public SecurityDoor() {

    }

    public SecurityDoor(String brand, double price) {
        super(brand, price);
    }

    @Override
    public void open() {
        System.out.println("64位加密密码开启");
    }

    @Override
    public void close() {
        System.out.println("三层防盗依次关闭");
    }

    @Override
    public void alarm() {
        System.out.println("默认开启报警");
    }
}

1.5 常见问题
以下来自网图
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶一菩提魁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值