当前位置: 首页 > article >正文

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;@Overridepublic 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;}@Overridepublic int size() {return size;}@Overridepublic 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;@Overridepublic E get(int index) {if (index < 0 || index >= size) {throw new IndexOutOfBoundsException("Index: " + index);}return (E) elements[index];}@Overridepublic int size() {return size;}@Overridepublic 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)); // 输出: BSystem.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 {@Overridepublic void methodA() {System.out.println("ClassA: methodA");}
}class ClassB implements MyInterface {@Overridepublic 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 {@Overridepublic void methodA() {System.out.println("ClassA: methodA");}
}class ClassB implements MyInterface {@Overridepublic void methodA() {System.out.println("ClassB: methodA");}
}
运行示例
public class Main {public static void main(String[] args) {MyInterface objA = new ClassA();objA.methodA(); // 输出: ClassA: methodAobjA.methodB(); // 输出: Default implementation of methodBMyInterface objB = new ClassB();objB.methodA(); // 输出: ClassB: methodAobjB.methodB(); // 输出: Default implementation of methodB}
}

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

class ClassB implements MyInterface {@Overridepublic void methodA() {System.out.println("ClassB: methodA");}@Overridepublic 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 方法或者覆写


相关文章:

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

接口 - Java教程 - 廖雪峰的官方网站 一个 抽象类 如果实现了一个接口&#xff0c;可以只选择实现接口中的 部分方法&#xff08;所有的方法都要有&#xff0c;可以一部分已经写具体&#xff0c;另一部分继续保留抽象&#xff09;&#xff0c;原因在于&#xff1a; 抽象类本身…...

数据结构与算法再探(六)动态规划

目录 动态规划 (Dynamic Programming, DP) 动态规划的基本思想 动态规划的核心概念 动态规划的实现步骤 动态规划实例 1、爬楼梯 c 递归&#xff08;超时&#xff09;需要使用记忆化递归 循环 2、打家劫舍 3、最小路径和 4、完全平方数 5、最长公共子序列 6、0-1背…...

使用PC版本剪映制作照片MV

目录 制作MV模板时长调整拖动边缘缩短法分割删除法变速法整体调整法 制作MV 导入音乐 导入歌词 点击歌词 和片头可以修改字体&#xff1a; 还可以给字幕添加动画效果&#xff1a; 导入照片&#xff0c;自动创建照片轨&#xff1a; 修改片头字幕&#xff1a;增加两条字幕轨&…...

Python爬虫获取custom-1688自定义API操作接口

一、引言 在电子商务领域&#xff0c;1688作为国内领先的B2B平台&#xff0c;提供了丰富的API接口&#xff0c;允许开发者获取商品信息、店铺信息等。其中&#xff0c;custom接口允许开发者进行自定义操作&#xff0c;获取特定的数据。本文将详细介绍如何使用Python调用1688的…...

Autogen_core: Reflection

目录 代码代码逻辑解释&#xff1a;数据类定义&#xff1a;CoderAgent 类&#xff1a;ReviewerAgent 类&#xff1a;主程序&#xff1a; 完成的功能&#xff1a; 代码 from dataclasses import dataclassdataclass class CodeWritingTask:task: strdataclass class CodeWritin…...

GitHub 仓库的 Archived 功能详解:中英双语

GitHub 仓库的 Archived 功能详解 一、什么是 GitHub 仓库的 “Archived” 功能&#xff1f; 在 GitHub 上&#xff0c;“Archived” 是一个专门用于标记仓库状态的功能。当仓库被归档后&#xff0c;它变为只读模式&#xff0c;所有的功能如提交代码、创建 issue 和 pull req…...

.NET Core缓存

目录 缓存的概念 客户端响应缓存 cache-control 服务器端响应缓存 内存缓存&#xff08;In-memory cache&#xff09; 用法 GetOrCreateAsync 缓存过期时间策略 缓存的过期时间 解决方法&#xff1a; 两种过期时间策略&#xff1a; 绝对过期时间 滑动过期时间 两…...

Ubuntu 20.04安装Protocol Buffers 2.5.0

个人博客地址&#xff1a;Ubuntu 20.04安装Protocol Buffers 2.5.0 | 一张假钞的真实世界 安装过程 Protocol Buffers 2.5.0源码下载&#xff1a;https://github.com/protocolbuffers/protobuf/tree/v2.5.0。下载并解压。 将autogen.sh文件中以下内容&#xff1a; curl htt…...

【贪心算法】洛谷P1090 合并果子 / [USACO06NOV] Fence Repair G

2025 - 01 - 21 - 第 45 篇 【洛谷】贪心算法题单 -【 贪心算法】 - 【学习笔记】 作者(Author): 郑龙浩 / 仟濹(CSND账号名) 洛谷 P1090[NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G 【贪心算法】 文章目录 洛谷 P1090[NOIP2004 提高组] 合并果子 / [USACO06…...

14.模型,纹理,着色器

模型、纹理和着色器是计算机图形学中的三个核心概念&#xff0c;用通俗易懂的方式来解释&#xff1a; 1. 模型&#xff1a;3D物体的骨架 通俗解释&#xff1a; 模型就像3D物体的骨架&#xff0c;定义了物体的形状和结构。 比如&#xff0c;一个房子的模型包括墙、屋顶、窗户等…...

【微服务与分布式实践】探索 Dubbo

核心组件 服务注册与发现原理 服务提供者启动时&#xff0c;会将其服务信息&#xff08;如服务名、版本、所在节点的网络地址等&#xff09;注册到注册中心。服务消费者则可以从注册中心发现可用的服务提供者列表&#xff0c;并与之通信。注册中心会存储服务的信息&#xff0c…...

Scale AI 创始人兼 CEO采访

Scale AI 创始人兼 CEO 亚历山大王&#xff08;Alexander Wang&#xff09;首次亮相节目接受采访。他的公司专注于为人工智能工具提供准确标注的数据。早在 2022 年&#xff0c;王成为世界上最年轻的白手起家亿万富翁。 美国在全球人工智能竞赛中的地位&#xff0c;以及它与中…...

Java 大视界 -- Java 大数据在生物信息学中的应用与挑战(67)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

NeuIPS 2024 | CoT推理的新突破:推理边界框架(RBF)

近年来&#xff0c;大型语言模型&#xff08;LLMs&#xff09;在推理任务上的能力不断提升&#xff0c;尤其是 思维链&#xff08;Chain-of-Thought, CoT&#xff09; 技术&#xff0c;使得模型可以逐步推演逻辑&#xff0c;提高预测准确率。然而&#xff0c;当前的CoT推理仍然…...

【C】memory 详解

<memory.h> 是一个 C 标准库头文件&#xff0c;提供了一组内存管理函数&#xff0c;用于分配、释放和操作动态内存。这些函数主要操作的是未初始化的内存块&#xff0c;是早期 C 编程中常用的内存操作工具。 尽管在现代 C 编程中更推荐使用<cstring>或<memory&…...

linux——进程树的概念和示例

一些程序进程运行后&#xff0c;会调用其他进程&#xff0c;这样就组成了一个进程树。 比如,在Windows XP的“运行”对话框中输入“cmd”启动命令行控制台&#xff0c;然后在命令行中输入“notepad”启动记事本&#xff0c;那么命令行控制台进程“cmd.exe”和记事本进程“note…...

分布式系统相关面试题收集

目录 什么是分布式系统&#xff0c;以及它有哪些主要特性&#xff1f; 分布式系统中如何保证数据的一致性&#xff1f; 解释一下CAP理论&#xff0c;并说明在分布式系统中如何权衡CAP三者&#xff1f; 什么是分布式事务&#xff0c;以及它的实现方式有哪些&#xff1f; 什么是…...

CSAPP学习:前言

前言 本书简称CS&#xff1a;APP。 背景知识 一些基础的C语言知识 如何阅读 Do-做系统 在真正的系统上解决具体的问题&#xff0c;或是编写和运行程序。 章节 2025-1-27 个人认为如下章节将会对学习408中的操作系统与计算机组成原理提供帮助&#xff0c;于是先凭借记忆将其简单…...

kaggle比赛入门 - House Prices - Advanced Regression Techniques(第三部分)

本文承接上一篇。 1. 数据预处理流水线&#xff08;pipelines&#xff09; from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler, OneHotEnc…...

Linux 命令之技巧(Tips for Linux Commands)

Linux 命令之技巧 简介 Linux ‌是一种免费使用和自由传播的类Unix操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹&#xff08;Linus Benedict Torvalds&#xff09;于1991年10月5日首次发布。Linux继承了Unix以网络为核心的设计思想&#xff0c;是一个性能稳定的多用户…...

从 GShard 到 DeepSeek-V3:回顾 MoE 大模型负载均衡策略演进

作者&#xff1a;小天狼星不来客 原文&#xff1a;https://zhuanlan.zhihu.com/p/19117825360 故事要从 GShard 说起——当时&#xff0c;人们意识到拥有数十亿甚至数万亿参数的模型可以通过某种形式的“稀疏化&#xff08;sparsified&#xff09;”来在保持高精度的同时加速训…...

【番外篇】鸿蒙扫雷天纪:运混沌灵智勘破雷劫天局

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 这一节课我们不学习新的知识&#xff0c;我们来做一个扫雷小游戏 目录 扫雷小游戏概述一、扫雷游戏分析…...

【反悔堆】力扣1642. 可以到达的最远建筑

给你一个整数数组 heights &#xff0c;表示建筑物的高度。另有一些砖块 bricks 和梯子 ladders 。 你从建筑物 0 开始旅程&#xff0c;不断向后面的建筑物移动&#xff0c;期间可能会用到砖块或梯子。 当从建筑物 i 移动到建筑物 i1&#xff08;下标 从 0 开始 &#xff09;…...

字符串算法笔记

字符串笔记 说到字符串,首先我们要注意的就是字符串的输入以及输出,因为字符串的输入格式以及要求也分为很多种,我们就来说几个比较常见的格式 g e t s gets gets 我们先来说这个函数的含义...

AWTK 骨骼动画控件用法

创建骨骼动画控件 atlas 指定纹理图集文件&#xff0c;skeleton 指定骨骼动画数据文件。可以是相对路径或绝对路径。atlas 中引用的图片文件需要和 skeleton 文件在同一目录下。 scale_x 和 scale_y 指定缩放比例&#xff0c;根据实际情况调整。 scale_time 指定播放速度&am…...

解决Oracle SQL语句性能问题(10.5)——常用Hint及语法(7)(其他Hint)

10.5.3. 常用hint 10.5.3.7. 其他Hint 1)cardinality:显式的指示优化器为SQL语句的某个行源指定势。该Hint具体语法如下所示。 SQL> select /*+ cardinality([@qb] [table] card ) */ ...; --注: 1)这里,第一个参数(@qb)为可选参数,指定查询语句块名;第二个参数…...

如何写美赛(MCM/ICM)论文中的Summary部分

美赛(MCM/ICM)作为一个数学建模竞赛,要求参赛者在有限的时间内解决一个复杂的实际问题,并通过数学建模、数据分析和计算机模拟等手段给出有效的解决方案。在美赛的论文中,Summary部分(通常也称为摘要)是非常关键的,它是整个论文的缩影,能让评审快速了解你解决问题的思…...

DataWhale组队学习 fun-transformer task5

1. 词向量&#xff1a;单词的“身份证” 首先&#xff0c;我们定义了四个单词的词向量&#xff0c;每个向量维度为3。你可以把这些词向量想象成每个单词的“身份证”。每个身份证上有3个特征&#xff0c;用来描述这个单词的“性格”或“特点”。 word_1 np.array([1, 0, 0])…...

【huawei】云计算的备份和容灾

目录 1 备份和容灾 2 灾备的作用&#xff1f; ① 备份的作用 ② 容灾的作用 3 灾备的衡量指标 ① 数据恢复时间点&#xff08;RPO&#xff0c;Recoyery Point Objective&#xff09; ② 应用恢复时间&#xff08;RTO&#xff0c;Recoyery Time Objective&#xff09; 4…...

电力晶体管(GTR)全控性器件

电力晶体管&#xff08;Giant Transistor&#xff0c;GTR&#xff09;是一种全控性器件&#xff0c;以下是关于它的详细介绍&#xff1a;&#xff08;模电普通晶体管三极管进行对比学习&#xff09; 基本概念 GTR是一种耐高电压、大电流的双极结型晶体管&#xff08;BJT&am…...