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

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; 抽象类本身…...

反向代理模块b

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当于…...

Nuitka打包python脚本

Python脚本打包 Python是解释执行语言&#xff0c;需要解释器才能运行代码&#xff0c;这就导致在开发机上编写的代码在别的电脑上无法直接运行&#xff0c;除非目标机器上也安装了Python解释器&#xff0c;有时候还需要额外安装Python第三方包&#xff0c;相当麻烦。 事实上P…...

pytorch线性回归模型预测房价例子

import torch import torch.nn as nn import torch.optim as optim import numpy as np# 1. 创建线性回归模型类 class LinearRegressionModel(nn.Module):def __init__(self):super(LinearRegressionModel, self).__init__()self.linear nn.Linear(1, 1) # 1个输入特征&…...

练习题 - DRF 3.x Caching 缓存使用示例和配置方法

在构建现代化的 Web 应用程序时,性能优化是一个非常重要的环节。尤其是在使用 Django Rest Framework (DRF) 开发 API 服务时,合理地利用缓存技术可以显著提高应用的响应速度和减轻数据库的负担。DRF 提供了多种缓存机制,包括基于内存、文件系统、数据库以及第三方缓存服务(…...

如何解压7z文件?8种方法(Win/Mac/手机/网页端)

7z 文件是一种高效的压缩文件格式&#xff0c;由 7 - Zip 软件开发者所采用。它运用独特的压缩算法&#xff0c;能显著缩小文件体积&#xff0c;便于存储与传输各类数据&#xff0c;像软件安装包、大型资料集等。但要使用其中内容&#xff0c;就必须解压&#xff0c;因为处于压…...

python学opencv|读取图像(五十)使用addWeighted()函数实现图像加权叠加效果

【1】引言 前序学习进程中&#xff0c;学习了图像互相叠加的不同操作方法&#xff0c;包括add()函数直接叠加BGR值和使用bitwise()函数对BGR值进行按位计算叠加等&#xff0c;相关文章链接包括且不限于&#xff1a; python学opencv|读取图像&#xff08;四十二&#xff09;使…...

window中80端口被占用问题

1&#xff0c;查看报错信息 可以看到在启动项目的时候&#xff0c;8081端口被占用了&#xff0c;导致项目无法启动。 2&#xff0c;查看被占用端口的pid #语法 netstat -aon|findstr :被占用端口#示例 netstat -aon|findstr :8080 3&#xff0c;杀死进程 #语法 taikkill /pid…...

06-机器学习-数据预处理

数据清洗 数据清洗是数据预处理的核心步骤&#xff0c;旨在修正或移除数据集中的错误、不完整、重复或不一致的部分&#xff0c;为后续分析和建模提供可靠基础。以下是数据清洗的详细流程、方法和实战示例&#xff1a; 一、数据清洗的核心任务 问题类型表现示例影响缺失值数值…...

电梯系统的UML文档12

5.2.1 DoorControl 的状态图 图 19: DoorControl 的状态图 5.2.2 DriveControl 的状态图 图 20: DriveControl 的状态图 5.2.3 LanternControl 的状态图 图 21: LanternControl 的状态图 5.2.4 HallButtonControl 的状态图 图 22: HallButtonControl 的状态图 5.2.5 CarB…...

萌新学 Python 之运算符

Python 中运算符包括&#xff1a;算术运算符、比较运算符、逻辑运算符、赋值运算符、位运算符、海象运算符 算术运算符&#xff1a;加 减 - 乘 * 除 / 取整 // 求余 % 求幂 ** 注意&#xff1a;取整时&#xff0c;一正一负整除&#xff0c;向下取整 比如 5 // …...

嵌入式知识点总结 Linux驱动 (五)-linux内核

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.内核镜像格式有几种&#xff1f;分别有什么区别&#xff1f; 2.内核中申请内存有哪几个函数&#xff1f;有什么区别&#xff1f; 3.什么是内核空间&#xff0c;用户空间&…...

zabbix7 配置字体 解决中文乱码问题(随手记)

目录 问题网传的方法&#xff08;无效&#xff09;正确的修改方式步骤 问题 zabbix 最新数据 中&#xff0c;图标的中文显示不出。 网传的方法&#xff08;无效&#xff09; 网传有一个方法&#xff1a;上传字体文件到/usr/share/zabbix/assets/fonts&#xff1b;修改/usr/…...

预测不规则离散运动的下一个结构

有一个点在19*19的平面上运动&#xff0c;运动轨迹为 一共移动了90步&#xff0c;顺序为 y x y x y x 0 17 16 30 10 8 60 15 15 1 3 6 31 10 7 61 14 15 2 12 17 32 9 9 62 16 15 3 4 12 33 10 9 63 18 15 4 3 18 34 15 12 6…...

CTFSHOW-WEB入门-命令执行29-32

题目&#xff1a;web 29 题目&#xff1a;解题思路&#xff1a;分析代码&#xff1a; error_reporting(0); if(isset($_GET[c])){//get一个c的参数$c $_GET[c];//赋值给Cif(!preg_match("/flag/i", $c)){eval($c);//if C变量里面没有flag&#xff0c;那么就执行C…...

SQL Server 建立每日自动log备份的维护计划

SQLServer数据库可以使用维护计划完成数据库的自动备份&#xff0c;下面以在SQL Server 2012为例说明具体配置方法。 1.启动SQL Server Management Studio&#xff0c;在【对象资源管理器】窗格中选择数据库实例&#xff0c;然后依次选择【管理】→【维护计划】选项&#xff0…...

doris:HLL

HLL是用作模糊去重&#xff0c;在数据量大的情况性能优于 Count Distinct。HLL的导入需要结合hll_hash等函数来使用。更多文档参考HLL。 使用示例​ 第 1 步&#xff1a;准备数据​ 创建如下的 csv 文件&#xff1a;test_hll.csv 1001|koga 1002|nijg 1003|lojn 1004|lofn …...

双层Git管理项目,github托管显示正常

双层Git管理项目&#xff0c;github托管显示正常 背景 在写React项目时&#xff0c;使用Next.js,该项目默认由git托管。但是我有在项目代码外层记笔记的习惯&#xff0c;我就在外层使用了git托管。 目录如下 code 层内也有.git 文件&#xff0c;对其托管。 我没太在意&…...

准备知识——旋转机械的频率和振动基础

旋转频率&#xff0c;也称为转速或旋转速率&#xff08;符号ν&#xff0c;小写希腊字母nu&#xff0c;也作n&#xff09;&#xff0c;是物体绕轴旋转的频率。其国际单位制单位是秒的倒数(s −1 )&#xff1b;其他常见测量单位包括赫兹(Hz)、每秒周期数(cps) 和每分钟转数(rpm)…...

知识库管理驱动企业知识流动与工作协同创新模式

内容概要 知识库管理在现代企业中扮演着至关重要的角色&#xff0c;其价值不仅体现在知识的积累&#xff0c;还在于通过优质的信息流动促进协作与创新。有效的知识库能够将分散的信息整合为有序、易于访问的资源&#xff0c;为员工提供实时支持&#xff0c;进而提升整体工作效…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

​​企业大模型服务合规指南:深度解析备案与登记制度​​

伴随AI技术的爆炸式发展&#xff0c;尤其是大模型&#xff08;LLM&#xff09;在各行各业的深度应用和整合&#xff0c;企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者&#xff0c;还是积极拥抱AI转型的传统企业&#xff0c;在面向公众…...

Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解

文章目录 一、开启慢查询日志&#xff0c;定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...

Linux安全加固:从攻防视角构建系统免疫

Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...