JAVA 接口、抽象类的关系和用处 详细解析
接口 - Java教程 - 廖雪峰的官方网站

一个 抽象类 如果实现了一个接口,可以只选择实现接口中的 部分方法(所有的方法都要有,可以一部分已经写具体,另一部分继续保留抽象),原因在于:
- 抽象类本身是 不完整的类,它可以有未实现的方法(即抽象方法),因此可以选择不完全实现接口。
- 由继承该抽象类的具体子类去完成未实现的方法。
这也是抽象类的一个强大功能,它在实现接口时,提供了一个“中间层次”,部分实现接口的行为,为具体的子类提供基础。
这里有两个箭头指向同一个类(例如 AbstractList),是因为:
- 接口(如
List)定义了行为规范:接口是用来定义类应该具有的功能和行为,例如List定义了与列表相关的方法(如add(),get()等),但不提供具体实现。 - 抽象类(如
AbstractList)提供了部分实现:抽象类用于实现接口的一部分行为,同时为具体类(如ArrayList和LinkedList)提供可以复用的代码。
AbstractList 和 List 的区别
-
List接口:- 是一个完全抽象的接口,只定义了列表操作的规范。
- 方法如
add(E element),get(int index),remove(int index)等都只是方法声明,没有实现。
-
AbstractList抽象类:- 是一个抽象类,实现了
List接口的大部分通用功能。 - 目的是让具体实现类(如
ArrayList和LinkedList)复用这些功能,只需实现特定的方法即可。例如,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");
-
编译时看接口,运行时看实现类:
list的编译时类型是List,所以编译器只会允许你调用List接口中声明的方法,比如add()、remove()等。- 但
list的运行时类型是ArrayList,所以具体执行的add()方法是ArrayList类中定义的实现。
-
接口 vs 抽象类:
List是接口,定义了add()的方法规范。AbstractList是一个抽象类,部分实现了List的规范,并提供了通用实现。- 但是:在
ArrayList中,它直接继承了AbstractList,并可能覆写了某些方法,所以最终调用的是ArrayList的实现。
为什么我们总是见到 List,而没有见过 AbstractList?
-
AbstractList是设计细节:AbstractList是为具体实现类(如ArrayList和LinkedList)服务的,目的是 减少代码重复。- 它为实现类提供了一些通用功能,比如:
- 默认实现
addAll()方法。 - 默认实现
iterator()方法。
- 默认实现
- 但是,
AbstractList是抽象的,不能直接使用,所以开发者不会直接实例化或引用它。
-
面向接口编程的原则:我们习惯通过接口(如
List)去引用对象,这是面向接口编程的核心思想。
default 方法
-
- 在 接口 中,
default方法允许有具体的实现,提供一个方法体。 - 抽象类 中不需要使用
default关键字,因为抽象类本身可以包含普通的具体方法(带方法体)和抽象方法(没有方法体)。
- 在 接口 中,
-
为什么
default方法存在于接口:- 原本接口中的方法必须全部是抽象的,这意味着接口升级时(比如增加新方法),所有实现这个接口的类都必须修改,去实现新增的方法。
- 为了兼容老代码,同时给接口增加新功能,Java 8 引入了
default方法。default方法是为了 在接口中提供默认实现,而不破坏已有的实现类。
-
抽象类和接口在具体方法上的区别:
- 抽象类的普通方法天然支持具体实现,不需要额外关键字。
- 接口中的
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,所有的实现类(ClassA 和 ClassB)都必须实现这个方法,否则代码无法编译。
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教程 - 廖雪峰的官方网站 一个 抽象类 如果实现了一个接口,可以只选择实现接口中的 部分方法(所有的方法都要有,可以一部分已经写具体,另一部分继续保留抽象),原因在于: 抽象类本身…...
数据结构与算法再探(六)动态规划
目录 动态规划 (Dynamic Programming, DP) 动态规划的基本思想 动态规划的核心概念 动态规划的实现步骤 动态规划实例 1、爬楼梯 c 递归(超时)需要使用记忆化递归 循环 2、打家劫舍 3、最小路径和 4、完全平方数 5、最长公共子序列 6、0-1背…...
使用PC版本剪映制作照片MV
目录 制作MV模板时长调整拖动边缘缩短法分割删除法变速法整体调整法 制作MV 导入音乐 导入歌词 点击歌词 和片头可以修改字体: 还可以给字幕添加动画效果: 导入照片,自动创建照片轨: 修改片头字幕:增加两条字幕轨&…...
Python爬虫获取custom-1688自定义API操作接口
一、引言 在电子商务领域,1688作为国内领先的B2B平台,提供了丰富的API接口,允许开发者获取商品信息、店铺信息等。其中,custom接口允许开发者进行自定义操作,获取特定的数据。本文将详细介绍如何使用Python调用1688的…...
Autogen_core: Reflection
目录 代码代码逻辑解释:数据类定义:CoderAgent 类:ReviewerAgent 类:主程序: 完成的功能: 代码 from dataclasses import dataclassdataclass class CodeWritingTask:task: strdataclass class CodeWritin…...
GitHub 仓库的 Archived 功能详解:中英双语
GitHub 仓库的 Archived 功能详解 一、什么是 GitHub 仓库的 “Archived” 功能? 在 GitHub 上,“Archived” 是一个专门用于标记仓库状态的功能。当仓库被归档后,它变为只读模式,所有的功能如提交代码、创建 issue 和 pull req…...
.NET Core缓存
目录 缓存的概念 客户端响应缓存 cache-control 服务器端响应缓存 内存缓存(In-memory cache) 用法 GetOrCreateAsync 缓存过期时间策略 缓存的过期时间 解决方法: 两种过期时间策略: 绝对过期时间 滑动过期时间 两…...
Ubuntu 20.04安装Protocol Buffers 2.5.0
个人博客地址:Ubuntu 20.04安装Protocol Buffers 2.5.0 | 一张假钞的真实世界 安装过程 Protocol Buffers 2.5.0源码下载:https://github.com/protocolbuffers/protobuf/tree/v2.5.0。下载并解压。 将autogen.sh文件中以下内容: curl htt…...
【贪心算法】洛谷P1090 合并果子 / [USACO06NOV] Fence Repair G
2025 - 01 - 21 - 第 45 篇 【洛谷】贪心算法题单 -【 贪心算法】 - 【学习笔记】 作者(Author): 郑龙浩 / 仟濹(CSND账号名) 洛谷 P1090[NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G 【贪心算法】 文章目录 洛谷 P1090[NOIP2004 提高组] 合并果子 / [USACO06…...
14.模型,纹理,着色器
模型、纹理和着色器是计算机图形学中的三个核心概念,用通俗易懂的方式来解释: 1. 模型:3D物体的骨架 通俗解释: 模型就像3D物体的骨架,定义了物体的形状和结构。 比如,一个房子的模型包括墙、屋顶、窗户等…...
【微服务与分布式实践】探索 Dubbo
核心组件 服务注册与发现原理 服务提供者启动时,会将其服务信息(如服务名、版本、所在节点的网络地址等)注册到注册中心。服务消费者则可以从注册中心发现可用的服务提供者列表,并与之通信。注册中心会存储服务的信息,…...
Scale AI 创始人兼 CEO采访
Scale AI 创始人兼 CEO 亚历山大王(Alexander Wang)首次亮相节目接受采访。他的公司专注于为人工智能工具提供准确标注的数据。早在 2022 年,王成为世界上最年轻的白手起家亿万富翁。 美国在全球人工智能竞赛中的地位,以及它与中…...
Java 大视界 -- Java 大数据在生物信息学中的应用与挑战(67)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
NeuIPS 2024 | CoT推理的新突破:推理边界框架(RBF)
近年来,大型语言模型(LLMs)在推理任务上的能力不断提升,尤其是 思维链(Chain-of-Thought, CoT) 技术,使得模型可以逐步推演逻辑,提高预测准确率。然而,当前的CoT推理仍然…...
【C】memory 详解
<memory.h> 是一个 C 标准库头文件,提供了一组内存管理函数,用于分配、释放和操作动态内存。这些函数主要操作的是未初始化的内存块,是早期 C 编程中常用的内存操作工具。 尽管在现代 C 编程中更推荐使用<cstring>或<memory&…...
linux——进程树的概念和示例
一些程序进程运行后,会调用其他进程,这样就组成了一个进程树。 比如,在Windows XP的“运行”对话框中输入“cmd”启动命令行控制台,然后在命令行中输入“notepad”启动记事本,那么命令行控制台进程“cmd.exe”和记事本进程“note…...
分布式系统相关面试题收集
目录 什么是分布式系统,以及它有哪些主要特性? 分布式系统中如何保证数据的一致性? 解释一下CAP理论,并说明在分布式系统中如何权衡CAP三者? 什么是分布式事务,以及它的实现方式有哪些? 什么是…...
CSAPP学习:前言
前言 本书简称CS:APP。 背景知识 一些基础的C语言知识 如何阅读 Do-做系统 在真正的系统上解决具体的问题,或是编写和运行程序。 章节 2025-1-27 个人认为如下章节将会对学习408中的操作系统与计算机组成原理提供帮助,于是先凭借记忆将其简单…...
kaggle比赛入门 - House Prices - Advanced Regression Techniques(第三部分)
本文承接上一篇。 1. 数据预处理流水线(pipelines) 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操作系统,其内核由林纳斯本纳第克特托瓦兹(Linus Benedict Torvalds)于1991年10月5日首次发布。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户…...
从 GShard 到 DeepSeek-V3:回顾 MoE 大模型负载均衡策略演进
作者:小天狼星不来客 原文:https://zhuanlan.zhihu.com/p/19117825360 故事要从 GShard 说起——当时,人们意识到拥有数十亿甚至数万亿参数的模型可以通过某种形式的“稀疏化(sparsified)”来在保持高精度的同时加速训…...
【番外篇】鸿蒙扫雷天纪:运混沌灵智勘破雷劫天局
大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 这一节课我们不学习新的知识,我们来做一个扫雷小游戏 目录 扫雷小游戏概述一、扫雷游戏分析…...
【反悔堆】力扣1642. 可以到达的最远建筑
给你一个整数数组 heights ,表示建筑物的高度。另有一些砖块 bricks 和梯子 ladders 。 你从建筑物 0 开始旅程,不断向后面的建筑物移动,期间可能会用到砖块或梯子。 当从建筑物 i 移动到建筑物 i1(下标 从 0 开始 )…...
字符串算法笔记
字符串笔记 说到字符串,首先我们要注意的就是字符串的输入以及输出,因为字符串的输入格式以及要求也分为很多种,我们就来说几个比较常见的格式 g e t s gets gets 我们先来说这个函数的含义...
AWTK 骨骼动画控件用法
创建骨骼动画控件 atlas 指定纹理图集文件,skeleton 指定骨骼动画数据文件。可以是相对路径或绝对路径。atlas 中引用的图片文件需要和 skeleton 文件在同一目录下。 scale_x 和 scale_y 指定缩放比例,根据实际情况调整。 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. 词向量:单词的“身份证” 首先,我们定义了四个单词的词向量,每个向量维度为3。你可以把这些词向量想象成每个单词的“身份证”。每个身份证上有3个特征,用来描述这个单词的“性格”或“特点”。 word_1 np.array([1, 0, 0])…...
【huawei】云计算的备份和容灾
目录 1 备份和容灾 2 灾备的作用? ① 备份的作用 ② 容灾的作用 3 灾备的衡量指标 ① 数据恢复时间点(RPO,Recoyery Point Objective) ② 应用恢复时间(RTO,Recoyery Time Objective) 4…...
电力晶体管(GTR)全控性器件
电力晶体管(Giant Transistor,GTR)是一种全控性器件,以下是关于它的详细介绍:(模电普通晶体管三极管进行对比学习) 基本概念 GTR是一种耐高电压、大电流的双极结型晶体管(BJT&am…...
