JAVA 接口、抽象类的关系和用处 详细解析

接口 - Java教程 - 廖雪峰的官方网站

一个 抽象类 如果实现了一个接口,可以只选择实现接口中的 部分方法(所有的方法都要有,可以一部分已经写具体,另一部分继续保留抽象),原因在于:

  • 抽象类本身是 不完整的类,它可以有未实现的方法(即抽象方法),因此可以选择不完全实现接口。
  • 由继承该抽象类的具体子类去完成未实现的方法。

这也是抽象类的一个强大功能,它在实现接口时,提供了一个“中间层次”,部分实现接口的行为,为具体的子类提供基础。

这里有两个箭头指向同一个类(例如 AbstractList),是因为:

  1. 接口(如 List)定义了行为规范:接口是用来定义类应该具有的功能和行为,例如 List 定义了与列表相关的方法(如 add(), get() 等),但不提供具体实现。
  2. 抽象类(如 AbstractList)提供了部分实现:抽象类用于实现接口的一部分行为,同时为具体类(如 ArrayListLinkedList)提供可以复用的代码。

AbstractListList 的区别

  • List 接口

    • 是一个完全抽象的接口,只定义了列表操作的规范。
    • 方法如 add(E element), get(int index), remove(int index) 等都只是方法声明,没有实现。
  • AbstractList 抽象类

    • 是一个抽象类,实现了 List 接口的大部分通用功能。
    • 目的是让具体实现类(如 ArrayListLinkedList)复用这些功能,只需实现特定的方法即可。例如,AbstractList 中实现了 addAll(),具体类无需再写这部分代码。
示例代码

假设你要实现一个自定义的列表,直接实现 List 和继承 AbstractList 的区别如下:

直接实现 List 接口

如果从零实现 List 接口,你需要定义接口中所有的方法(包括很多通用方法,比如 size()addAll())。

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class CustomList<E> implements List<E> {
    private Object[] elements = new Object[10];
    private int size = 0;

    @Override
    public boolean add(E e) {
        if (size == elements.length) {
            Object[] newElements = new Object[size * 2];
            System.arraycopy(elements, 0, newElements, 0, size);
            elements = newElements;
        }
        elements[size++] = e;
        return true;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Index: " + index);
        }
        return (E) elements[index];
    }

    // 还需实现 List 中所有的方法,如 remove()、iterator() 等,工作量很大。
}
继承 AbstractList 抽象类

通过继承 AbstractList,你只需实现一些关键方法,剩下的方法由 AbstractList 提供默认实现。

import java.util.AbstractList;

public class CustomList<E> extends AbstractList<E> {
    private Object[] elements = new Object[10];
    private int size = 0;

    @Override
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Index: " + index);
        }
        return (E) elements[index];
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean add(E e) {
        if (size == elements.length) {
            Object[] newElements = new Object[size * 2];
            System.arraycopy(elements, 0, newElements, 0, size);
            elements = newElements;
        }
        elements[size++] = e;
        return true;
    }

    // 不需要手动实现 addAll() 等通用方法,AbstractList 已提供默认实现。
}
运行示例
public class Main {
    public static void main(String[] args) {
        CustomList<String> list = new CustomList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        System.out.println(list.get(1)); // 输出: B
        System.out.println(list.size()); // 输出: 3
    }
}

为什么 Java 集合框架中要设计接口和抽象类的这种关系?

1. 灵活性:接口用于定义行为规范

接口(如 List)允许不同的实现方式,适配多种需求,例如:

  • ArrayList:基于数组实现的列表,适合随机访问操作。
  • LinkedList:基于链表实现的列表,适合插入和删除操作。
  • 自定义列表:可以实现特定的逻辑,比如线程安全或固定容量。
2. 代码复用:抽象类减少重复代码

抽象类(如 AbstractList)避免了在每个实现类中重复编写通用逻辑。例如:

  • size() 的计算逻辑。
  • 批量添加方法(addAll())的实现。
  • 迭代器(iterator())的通用实现。

通过这种设计,新实现类只需关注特定的细节。

3. 多层次抽象设计

如下图中的设计:

  • 接口层: 定义行为规范(如 List, Collection)。
  • 抽象类层: 提供部分实现(如 AbstractList, AbstractCollection)。
  • 具体类层: 提供特定实现(如 ArrayList, LinkedList)。

这种多层次设计提供了灵活性和代码复用的平衡。


问题 1:调用的时候执行的是接口的方法还是抽象类的方法?

调用的是 对象的实际实现类中的方法,而不是接口或抽象类本身。尽管我们通过 List 这样的接口来引用一个对象,但具体执行的代码取决于 对象的具体实现类

假设我们写了一段代码:

List<String> list = new ArrayList<>();
list.add("Hello");
  1. 编译时看接口,运行时看实现类:

    • list 的编译时类型是 List,所以编译器只会允许你调用 List 接口中声明的方法,比如 add()remove() 等。
    • list 的运行时类型是 ArrayList,所以具体执行的 add() 方法是 ArrayList 类中定义的实现。
  2. 接口 vs 抽象类:

    • List 是接口,定义了 add() 的方法规范。
    • AbstractList 是一个抽象类,部分实现了 List 的规范,并提供了通用实现。
    • 但是:在 ArrayList 中,它直接继承了 AbstractList,并可能覆写了某些方法,所以最终调用的是 ArrayList 的实现。
       

为什么我们总是见到 List,而没有见过 AbstractList

  • AbstractList 是设计细节:

    • AbstractList 是为具体实现类(如 ArrayListLinkedList)服务的,目的是 减少代码重复
    • 它为实现类提供了一些通用功能,比如:
      • 默认实现 addAll() 方法。
      • 默认实现 iterator() 方法。
    • 但是,AbstractList 是抽象的,不能直接使用,所以开发者不会直接实例化或引用它。
  • 面向接口编程的原则:我们习惯通过接口(如 List)去引用对象,这是面向接口编程的核心思想。



default 方法

    • 接口 中,default 方法允许有具体的实现,提供一个方法体。
    • 抽象类 中不需要使用 default 关键字,因为抽象类本身可以包含普通的具体方法(带方法体)和抽象方法(没有方法体)。
  1. 为什么 default 方法存在于接口

    • 原本接口中的方法必须全部是抽象的,这意味着接口升级时(比如增加新方法),所有实现这个接口的类都必须修改,去实现新增的方法。
    • 为了兼容老代码,同时给接口增加新功能,Java 8 引入了 default 方法。default 方法是为了 在接口中提供默认实现,而不破坏已有的实现类
  2. 抽象类和接口在具体方法上的区别

    • 抽象类的普通方法天然支持具体实现,不需要额外关键字。
    • 接口中的 default 方法则是接口为了支持具体实现而引入的额外能力。

具体对比:抽象类和接口中的具体方法

特点抽象类中的具体方法接口中的 default 方法
是否需要关键字不需要,直接定义普通方法即可需要使用 default 关键字
是否可以有具体实现是的,普通方法都可以有实现是的,default 方法允许提供具体实现
是否可以被覆写可以,子类可以选择覆写抽象类中的普通方法可以,子类可以选择覆写接口中的 default 方法
是否强制实现不是,子类可以选择继承普通方法的实现或覆写它不是,默认继承接口中的 default 方法

default 方法的实际意义

1. 向接口新增方法时的兼容性问题

假设你有一个接口 MyInterface 和两个实现类:

interface MyInterface {
    void methodA();
}

class ClassA implements MyInterface {
    @Override
    public void methodA() {
        System.out.println("ClassA: methodA");
    }
}

class ClassB implements MyInterface {
    @Override
    public void methodA() {
        System.out.println("ClassB: methodA");
    }
}

如果你需要给 MyInterface 添加一个新方法 methodB,所有的实现类(ClassAClassB)都必须实现这个方法,否则代码无法编译。

2. 使用 default 方法解决兼容问题

在这种情况下,可以用 default 方法为新方法提供一个默认实现,从而避免修改所有实现类:

interface MyInterface {
    void methodA();

    // 新增一个 default 方法
    default void methodB() {
        System.out.println("Default implementation of methodB");
    }
}

class ClassA implements MyInterface {
    @Override
    public void methodA() {
        System.out.println("ClassA: methodA");
    }
}

class ClassB implements MyInterface {
    @Override
    public void methodA() {
        System.out.println("ClassB: methodA");
    }
}
运行示例
public class Main {
    public static void main(String[] args) {
        MyInterface objA = new ClassA();
        objA.methodA(); // 输出: ClassA: methodA
        objA.methodB(); // 输出: Default implementation of methodB

        MyInterface objB = new ClassB();
        objB.methodA(); // 输出: ClassB: methodA
        objB.methodB(); // 输出: Default implementation of methodB
    }
}

如果某个实现类需要对 default 方法提供自定义实现,可以覆写它:

class ClassB implements MyInterface {
    @Override
    public void methodA() {
        System.out.println("ClassB: methodA");
    }

    @Override
    public void methodB() {
        System.out.println("ClassB: Custom implementation of methodB");
    }
}

运行后:

MyInterface objB = new ClassB();
objB.methodB(); // 输出: ClassB: Custom implementation of methodB

结合数据库任务的实际场景

在你的数据库任务中,default 方法可以为某些操作提供通用实现。例如:

接口定义
public interface DBOperations {
    boolean createTable(String tableName, List<String> columns);

    default boolean dropTable(String tableName) {
        System.out.println("[OK] Dropped table: " + tableName);
        return true;
    }
}
实现类

具体类可以选择覆写或继承接口中的 default 方法或者覆写


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962323.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

游戏引擎 Unity - Unity 启动(下载 Unity Editor、生成 Unity Personal Edition 许可证)

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之用户注册

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;项目基本介绍 &#x1f6a6;项…

视频多模态模型——视频版ViT

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细解读多模态论文《ViViT: A Video Vision Transformer》&#xff0c;2021由google 提出用于视频处理的视觉 Transformer 模型&#xff0c;在视频多模态领域有…

DeepSeek本地部署(windows)

一、下载并安装Ollama 1.下载Ollama Ollama官网:Ollama 点击"Download",会跳转至下载页面。 点击"Download for Windows"。会跳转Github进行下载,如下载速度过慢,可在浏览器安装GitHub加速插件。 2.安装Ollama 双击下载的安装文件,点击"Inst…

1 HDFS

1 HDFS 1. HDFS概述2. HDFS架构3. HDFS的特性4. HDFS 的命令行使用5. hdfs的高级使用命令6. HDFS 的 block 块和副本机制6.1 抽象为block块的好处6.2 块缓存6.3 hdfs的文件权限验证6.4 hdfs的副本因子 7. HDFS 文件写入过程&#xff08;非常重要&#xff09;7.1 网络拓扑概念7.…

全国31省空间权重矩阵(地理相邻空间、公路铁路地理距离空间、经济空间)权重矩阵数据-社科数据

中国31个省份空间权重矩阵-社科数据https://download.csdn.net/download/paofuluolijiang/90028597 https://download.csdn.net/download/paofuluolijiang/90028597 空间权重矩阵是反映个体在空间中依赖关系的矩阵&#xff0c;本数据计算全国31个省三种标准化处理的空间权重矩…

Flask框架基础入门教程_ezflaskapp

pip install flaskFlask 快速入门小应用 学东西&#xff0c;得先知道我们用这个东西&#xff0c;能做出来一个什么东西。 一个最小的基于flask 的应用可能看上去像下面这个样子&#xff1a; from flask import Flask app Flask(__name__)app.route(/) def hello_world():ret…

机器学习笔记——特征工程

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本笔记介绍机器学习中常见的特征工程方法、正则化方法和简要介绍强化学习。 文章目录 特征工程&#xff08;Fzeature Engineering&#xff09;1. 特征提取&#xff…

Cursor火出圈,未来程序员还有出路吗?

大家好&#xff0c;我是凡人。 今天我表弟家邻居的阿姨&#xff0c;托他问问我目前程序员还有前景吗&#xff0c;希望我根据十几年的经验给出点建议&#xff0c;看看程序员这条路未来能不能走。 一下子不知道该怎么回复他了&#xff0c;如果是三年前问我&#xff0c;肯定毫不…

如何移植ftp服务器到arm板子?

很多厂家提供的sdk&#xff0c;一般都不自带ftp服务器功能&#xff0c; 需要要发人员自己移植ftp服务器程序。 本文手把手教大家如何移植ftp server到arm板子。 环境 sdk&#xff1a;复旦微 Buildroot 2018.02.31. 解压 $ mkdir ~/vsftpd $ cp vsftpd-3.0.2.tar.gz ~/vs…

第5章 公共事件

HarmonyOS通过公共事件服务为应用程序提供订阅、发布、退订公共事件的能力。 5.1 公共事件概述 在应用里面&#xff0c;往往会有事件。比如&#xff0c;朋友给我手机发了一条信息&#xff0c;未读信息会在手机的通知栏给出提示。 5.1.1 公共事件的分类 公共事件&#xff08…

STM32 对射式红外传感器配置

这次用的是STM32F103的开发板&#xff08;这里面的exti.c文件没有how to use this driver 配置说明&#xff09; 对射式红外传感器 由一个红外发光二极管和NPN光电三极管组成&#xff0c;M3固定安装孔&#xff0c;有输出状态指示灯&#xff0c;输出高电平灯灭&#xff0c;输出…

【数据结构】(2)时间、空间复杂度

一、衡量算法好坏的指标 时间复杂度衡量算法的运行速度&#xff0c;空间复杂度衡量算法所需的额外空间。这些指标&#xff0c;是某场景中选择使用哪种数据结构和算法的依据。如今&#xff0c;计算机的存储器已经变得容易获得&#xff0c;所以不再太关注空间复杂度。 二、渐进表…

FBX SDK的使用:基础知识

Windows环境配置 FBX SDK安装后&#xff0c;目录下有三个文件夹&#xff1a; include 头文件lib 编译的二进制库&#xff0c;根据你项目的配置去包含相应的库samples 官方使用案列 动态链接 libfbxsdk.dll, libfbxsdk.lib是动态库&#xff0c;需要在配置属性->C/C->预…

Ansible自动化运维实战--yaml的使用和配置(7/8)

文章目录 一、YAML 基本语法1.1. 缩进1.2. 注释1.3. 列表1.4. 字典 二、Ansible 中 YAML 的应用2.1. Ansible 剧本&#xff08;Playbooks&#xff09;2.2. 变量定义2.3. 角色&#xff08;Roles&#xff09;2.4. Inventory 文件2.5. 数据类型2.6. 引用变量 在 Ansible 里&#x…

springboot集成钉钉,发送钉钉日报

目录 1.说明 2.示例 3.总结 1.说明 学习地图 - 钉钉开放平台 在钉钉开放文档中可以查看有关日志相关的api&#xff0c;主要用到以下几个api&#xff1a; ①获取模板详情 ②获取用户发送日志的概要信息 ③获取日志接收人员列表 ④创建日志 发送日志时需要根据模板规定日志…

Node.js下载安装及环境配置教程 (详细版)

Node.js&#xff1a;是一个基于 Chrome V8 引擎的 JavaScript 运行时&#xff0c;用于构建可扩展的网络应用程序。Node.js 使用事件驱动、非阻塞 I/O 模型&#xff0c;使其非常适合构建实时应用程序。 Node.js 提供了一种轻量、高效、可扩展的方式来构建网络应用程序&#xff0…

ProfiNet转CANopen应用于汽车总装生产线输送设备ProfiNet与草棚CANopen质量检测系统

ProfiNet转CANopen协议转换网关模块&#xff0c;广泛应用于汽车行业。可替代NT 100-RE-CO和AB7658/7307产品功能 项目概述 在汽车总装生产线的末尾环节&#xff0c;汽车总装生产线输送设备起着关键的搬运作用&#xff0c;其基于 ProfiNet 协议运行&#xff0c;精准控制车辆在各…

「全网最细 + 实战源码案例」设计模式——桥接模式

核心思想 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;将抽象部分与其实现部分分离&#xff0c;使它们可以独立变化。降低代码耦合度&#xff0c;避免类爆炸&#xff0c;提高代码的可扩展性。 结构 1. Implementation&#xff08;实现类…

Attention--人工智能领域的核心技术

1. Attention 的全称与基本概念 在人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;领域&#xff0c;Attention 机制的全称是 Attention Mechanism&#xff08;注意力机制&#xff09;。它是一种能够动态分配计算资源&#xff0c;使模型在处理输入数据…