详解Java:抽象类和接口

news2024/11/16 9:54:38

前言:在前文中我们学习认知到了多态的使用和相关知识,算是打开了Java世界的大门,而本次要分享的抽象类和接口则是我们在面向对象编程中最常用的编程结构之一

目录

一.抽象类 

abstract 

抽象类特性

二.接口

语法规则 

接口使用

接口特性

实现多个接口

接口间的继承 


一.抽象类 

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类

我们回顾之前设计的打印图形的例子:

class Shape {
    //属性....
    public void draw() {
        System.out.println("画图形!");
    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}
class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("●");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

我们发现, 父类Shape 中的 draw方法好像并没有什么实际工作, 主要的绘制图形都是由 Shape的各种子类的 draw方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个抽象方法(abstract method), 包含抽象方法的类我们称为抽象类(abstract class)

又比如是我们之前设计的图形类或者动物类

我们需要定义一个抽象概念的类来作为跳板,方便我们使用子类,因此抽象类的存在是非常有必要的 

abstract 

在Java中我们可以使用abstract关键字来定义抽象类和抽象方法,一个类如果被abstract修饰则称为抽象类,抽象类中被abstract修饰的方法则称为抽象方法,抽象方法不用给出具体的实现体

对于刚才所说的打印图形类,我们就可以这样定义

// 抽象类:被abstract修饰的类
abstract class Shape {
    // 抽象方法:被abstract修饰的方法,没有方法体
    abstract public void draw();
    protected double area; // 面积
    // 抽象类也是类,也可以增加普通方法和属性
    public double getArea(){
        return area;
    }
}

其中需要注意的:抽象类也是类,内部可以包含普通方法和属性,或者是构造方法

抽象类特性

1. 抽象类不能直接实例化对象

Shape shape = new Shape();
// 编译出错Error: java: Shape是抽象的; 无法实例化

2. 抽象方法不能是 private 

abstract class Shape {
    abstract private void draw();
}
// 编译出错Error: 非法的修饰符组合: abstract和private

3. 抽象方法不能被 final static 修饰,因为抽象方法要被子类重写

public abstract class Shape {
    abstract final void methodA();
    abstract public static void methodB();
}
// 编译报错:
// Error: 非法的修饰符组合: abstract和final
// Error: 非法的修饰符组合: abstract和static

4. 抽象类必须被继承,并且继承后子类必须要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰

5. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类

6. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量 

二.接口

在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座等

电脑的USB口上可以插:U盘、鼠标、键盘...所有符合USB协议的设备

电源插座插孔上可以插:电脑、电视机、电饭煲...所有符合规范的设备 

通过上述例子可以看出:接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用;在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型

语法规则 

接口的定义格式与定义类的格式基本相同,将class关键字换成interface关键字,就定义了一个接口

public interface name {
    // 抽象方法
    public abstract void method1(); // public abstract 是固定搭配,可以不写
    public void method2();
    abstract void method3();
    void method4();
    // 注意:在接口中上述写法都是抽象方法,跟推荐方式4,代码更简洁
}

 在编写接口的过程中,有一些约定俗成的编写习惯和约定,并不一定需要遵守,但是为了养成良好的编程风格,最好还是遵守一下

  1. 创建接口时, 接口的命名一般以大写字母 I 开头.
  2. 接口的命名一般使用 "形容词" 词性的单词.
  3. 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性

接口使用

接口不能直接调用使用,必须要有一个"实现类""实现"该接口,实现接口中的所有抽象方法,我们需要使用implements关键字来实现接口,一般语法如下: 

public class 类名称 implements 接口名称{
        // ...
}

我们需要注意:子类和父类之间是 extends 继承关系,类与接口之间是 implements 实现关系

我们举个USB接口的例子来参考认识一下接口的使用:

// USB接口
interface USB {
    void openDevice();
    void closeDevice();
}
// 鼠标类,实现USB接口
class Mouse implements USB {
    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }
    public void click(){
        System.out.println("鼠标点击");
    }
}
// 键盘类,实现USB接口
class KeyBoard implements USB {
    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }
    public void inPut(){
        System.out.println("键盘输入");
    }
}
// 笔记本类:使用USB设备
class Computer {
    public void powerOn(){
        System.out.println("打开笔记本电脑");
    }
    public void powerOff(){
        System.out.println("关闭笔记本电脑");
    }
    public void useDevice(USB usb){
        usb.openDevice();
        if(usb instanceof Mouse){
            Mouse mouse = (Mouse)usb;
            mouse.click();
        }else if(usb instanceof KeyBoard){
            KeyBoard keyBoard = (KeyBoard)usb;
            keyBoard.inPut();
        }
        usb.closeDevice();
    }
}
public class Test_2 {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.powerOn();
        // 使用鼠标设备
        computer.useDevice(new Mouse());
        // 使用键盘设备
        computer.useDevice(new KeyBoard());
        computer.powerOff();
    }
}

接口特性

1. 接口类型是一种引用类型,但是不能直接new接口的对象

public class TestUSB {
    public static void main(String[] args) {
        USB usb = new USB();
    }
}
// Error:USB是抽象的; 无法实例化

2. 接口中每一个方法都是public的抽象方法,即接口中的方法会被隐式的指定为 public abstract (只能是public abstract,其他修饰符都会报错) 

public interface USB {
    // Error: 此处不允许使用修饰符private
    private void openDevice();
    void closeDevice();
}

3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

public interface USB {
    void openDevice();
    // 编译失败:因为接口中的方式默认为抽象方法
    // Error: 接口抽象方法不能带有主体
    void closeDevice(){
        System.out.println("关闭USB设备");
    }
}

 4. 重写接口中方法时,不能使用默认的访问权限

public interface USB {
    void openDevice(); // 默认是public的
    void closeDevice(); // 默认是public的
}
public class Mouse implements USB {
    @Override
    void openDevice() {
        System.out.println("打开鼠标");
    }
}
// 编译报错,重写USB中openDevice方法时,不能使用默认修饰符
// 正在尝试分配更低的访问权限; 以前为public

 5. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

public interface USB {
    double brand = 3.0; // 默认被:final public static修饰
    void openDevice();
    void closeDevice();
}
public class TestUSB {
    public static void main(String[] args) {
        System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的
        // 编译报错:无法为最终变量brand分配值
        USB.brand = 2.0; // 说明brand具有final属性
    }
}

 6. 接口中不能有静态代码块和构造方法

public interface USB {
    // 编译失败
    public USB(){
    }
    {} // 编译失败
    void openDevice();
    void closeDevice();
}

7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是 .class 
8. 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
9. jdk8中:接口中还可以包含 default 方法

实现多个接口

在之前的文章中,我们提到Java不允许一个类直接继承多个类,但是为了实现这样的功能,我们就引入了实现多个接口的思路,即Java中不支持多继承,但是一个类可以实现多个接口,注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类

下面通过类来表示一组动物

class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
}

 另外我们再提供一组接口, 分别表示 "会飞的", "会跑的", "会游泳的"

interface IFlying {
    void fly();
}
interface IRunning {
    void run();
}
interface ISwimming {
    void swim();
}

接下来我们创建几个具体的动物:猫, 是会跑的

class Cat extends Animal implements IRunning {
    public Cat(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用四条腿跑");
    }
}

鱼, 是会游的

class Fish extends Animal implements ISwimming {
    public Fish(String name) {
        super(name);
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在用尾巴游泳");
    }
}

青蛙, 既能跑, 又能游(两栖动物)

class Frog extends Animal implements IRunning, ISwimming {
    public Frog(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在往前跳");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在蹬腿游泳");
    }
}

还有一种神奇的动物, 水陆空三栖, 叫做 "鸭子"

class Duck extends Animal implements IRunning, ISwimming, IFlying {
    public Duck(String name) {
        super(name);
    }
    
    @Override
    public void fly() {
        System.out.println(this.name + "正在用翅膀飞");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在漂在水上");
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用两条腿跑");
    }
}

有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力 

  • 猫是一种动物, 具有会跑的特性.
  • 青蛙也是一种动物, 既能跑, 也能游泳
  • 鸭子也是一种动物, 既能跑, 也能游, 还能飞

接口间的继承 

在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。接口可以继承一个接口,,达到复用的效果,需要使用 extends 关键字

interface IRunning {
    void run();
}
interface ISwimming {
    void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
}
class Frog implements IAmphibious {
    //...
}

通过接口继承创建一个新的接口 IAmphibious 表示 "两栖的"。此时实现接口创建的 Frog 类,就继续要实现 run 方法,也需要实现 swim 方法,接口间的继承相当于把多个接口合并在一起。




 本次的分享就到此为止了,希望我的分享能给您带来帮助,也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[笔记]深入解析Windows操作系统《番外》windows关键进程解释

文章目录 前言一、Linux起源与发展二、什么是shell1.什么是Shell 总结 前言 一、Linux起源与发展 二、什么是shell 1.什么是Shell 总结 以上就是今天要讲的内容,本文仅仅简单介绍了linux命令行的使用。 参考: shells 概念 centOS7中的几个Ctrl组合…

原型模式 rust和java的实现

文章目录 原型模式介绍优点缺点使用场景 实现java 实现rust 实现 rust代码仓库 原型模式 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当…

Spring-ProxyFactory

ProxyFactory选择cglib或jdk动态代理原理 ProxyFactory在生成代理对象之前需要决定是使用JDK动态代理还是CGLIB技术: public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {Overridepublic AopProxy createAopProxy(AdvisedSupport co…

59基于matlab的爬行动物搜索算法(Reptile search algorithm, RSA)

基于matlab的爬行动物搜索算法(Reptile search algorithm, RSA)一种新型智能优化算法。该算法主要模拟鳄鱼的捕食行为,来实现寻优求解,具有收敛速度快,寻优能力强的特点。程序已调通,可直接运行。 59matlab…

案例续集留言板

前端没有保存数据的功能,后端把数据保存下来(内存,数据库等等......) 前端代码如下 : <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initia…

Centos, RockyLinux 常用软件安装汇总

一、基本指令&#xff1a; 命令作用clear清屏pwd显示当前路径cat / more显示文本文档uname -a查看当前版本hostnamectl查看当前版本cat /etc/redhat-release查看当前版本free查看剩余内存df -h[查看磁盘剩余空间]du -sh 查看文件夹名"dir"占用的空间lsof -i:8080查看…

未来的拥塞控制与 Linux EEVDF 调度器

有破要有立。 前面提到 经典端到端拥塞控制将越来越失效&#xff0c;未来该如何&#xff0c;谈谈我的看法。 端到端拥塞控制的难点根本上是要解决公平性问题&#xff0c;顺带着提高资源利用率。我们很容易理解&#xff0c;在共享资源场景下&#xff0c;不公平一定是低效的&am…

在AutoDL云环境上训练Stable Diffusion Lora模型

AutoDL官网&#xff1a; AutoDL算力云 | 弹性、好用、省钱。租GPU就上AutoDLAutoDL为您提供专业的GPU租用服务&#xff0c;秒级计费、稳定好用&#xff0c;高规格机房&#xff0c;7x24小时服务。更有算法复现社区&#xff0c;一键复现算法。https://www.autodl.com/ 新建实例…

优酷网页截图黑屏及了解浏览器图形服务API-meethigher

一、背景 周六跟同事逛了上海的豫园、城隍庙、静安寺、静安公园。豫园门票40&#xff0c;相传是明代私人园林&#xff0c;园主人为当年的四川布政使&#xff0c;是江南风格古典园林&#xff0c;风景还不错。 周日天气降温&#xff0c;直接睡了一天&#xff0c;想起同事推荐的《…

springboot项目使用Swagger3

一、Swagger介绍 号称世界上最流行的Api框架&#xff1b;Restful Api 文档在线自动生成工具>Api文档与API定义同步更新直接运行&#xff0c;可以在在线测试API 接口支持多种语言&#xff1a;&#xff08;java&#xff0c;Php…&#xff09; 二、Swagger3 准备工作 1、在p…

【文件IO】

文章目录 File常见方法和属性属性构造方法方法 InputStream方法FileInputStream OutputStream利用 OutputStreamWriter 进行字符写入 总结按字节读取数据按字节写入数据按字符读取数据按字符写入数据 File常见方法和属性 属性 修饰符及类型属性说明static StringpathSeparato…

JavaScript从入门到精通系列第三十五篇:JavaScript中的DOM简介

文章目录 前言 1&#xff1a;对象分类 2&#xff1a;宿主对象 一&#xff1a;DOM 1&#xff1a;dom简介 2&#xff1a;Dom概念图示 二&#xff1a;节点 1&#xff1a;节点概述 2&#xff1a;常用节点分类 3&#xff1a;节点模型示意图 4&#xff1a;节点属性 5&…

Java 之 IO/NIO/OKIO

BIO blocking io AIO Asynchronous IO 从内存读取到写入--输出 从外部到内存 -- 输入 OutputStream //文件不存在则自动创建 try {OutputStream outputStream new FileOutputStream("text.txt");outputStream.write(a);outputStream.write(b);} catch (IOExcep…

若依Linux与Docker集群部署

若依Linux集群部署 1. 若依2.MYSQL Linux环境安装2.1 MYSQL数据库部署和安装2.2 解压MYSQL安装包2.3 创建MYSQL⽤户和⽤户组2.4 修改MYSQL⽬录的归属⽤户2.5 准备MYSQL的配置⽂件2.6 正式开始安装MYSQL2.7 复制启动脚本到资源⽬录2.8 设置MYSQL系统服务并开启⾃启2.9 启动MYSQL…

终止进程后,GPU显存仍被占用问题 | kill -9彻底杀死进程 | ps aux|grep python

本文部分内容参考博客&#xff0c;十分感谢&#xff01;&#xff01;&#xff01; 问题描述&#xff1a;在Linux终端把进程终止后&#xff0c;发现显存没有被释放出来&#xff01; ---------------------------------------------------------------------------------------F…

Git分支与Git标签的介绍及其场景应用

目录 一、Git分支 1.1 定义 1.2 基本概念 1.3 特点与优势 1.4 Git分支操作命令 1.4.1 查看分支 1.4.2 创建分支 1.4.3 删除分支 1.4.4 切换分支 1.4.5 创建并切换到新建分支 1.5 场景应用 1.5.1 前期准备 1.5.2 具体操作 二、Git标签 2.1 定义 2.2 类型 2.3 标…

Some/IP学习笔记

目录 1.概述 2.SOME/IP 报文格式 3.数据结构序列化 1.概述 SOME/IP全称为Scalable Service Oriented MiddlewarE Over IP&#xff0c;是车载以太网技术中的核心内容&#xff0c;它为网络提供了面向服务的通信方式。一个服务可以包含0个或者多个事件&#xff08;events&#…

【C++ 学习 ㉟】- 异常详解

目录 一、C 异常处理的基本语法 1.1 - 抛出异常 1.2 - 检测和捕获异常 二、在函数调用链中异常栈展开的匹配原则 三、异常重新抛出 四、异常规范 五、C 标准异常体系 程序的错误大致可以分为以下三种&#xff1a; 语法错误&#xff1a;在编译和链接阶段就能发现&#xf…

Linux——vim简介、配置方案(附带超美观的配置方案)、常用模式的基本操作

vim简介、配置方案、常用模式的基本操作 本章思维导图&#xff1a; 注&#xff1a;本章思维导图对应的xmind和.png文件都已同步导入至资源 1. vim简介 vim是Linux常用的文本编辑器&#xff0c;每个Linux账户都独有一个vim编辑器 本篇我们介绍vim最常用的三种模式&#xff1a;…