Java 入门指南:接口(Interface)

news2024/9/20 20:48:08

引言

在Java编程中,接口(Interface)是一种非常重要的概念,它不仅是面向对象编程(OOP)的基石之一,也是实现高内聚低耦合设计原则的关键工具。接口定义了一组方法,但不提供这些方法的实现细节,而是由实现接口的类来具体实现。这种机制使得Java程序更加灵活、易于扩展和维护。

定义接口

基本语法

接口在Java中通过 interface 关键字来定义。一个接口可以包含常量(默认是 public static final 的)、方法(默认是 public abstract 的)以及从Java 8开始引入的默认方法和静态方法。接口不能包含实例变量(但可以包含静态常量)和构造方法。

public interface MyInterface {  
    // 常量  
    int MAX_SIZE = 100;  
  
    // 方法声明  
    void doSomething();  
  
    // Java 8 默认方法  
    default void defaultMethod() {  
        // 默认实现  
    }  
  
    // Java 8 静态方法  
    static void staticMethod() {  
        // 静态方法实现  
    }  
}

详情解释

  1. 接口中定义的变量会在编译的时候自动加上 public static final 修饰符(注意看一下反编译后的字节码)

Java 官方文档上有这样的声明:

Every field declaration in the body of an interface is implicitly public, static, and final.

换句话说,接口可以用来作为常量类使用,还能省略掉 public static final,看似不错,但这种选择并不可取。因为接口的本意是对方法进行抽象,而常量接口会对子类中的变量造成命名空间上的“污染”。

  1. 没有使用 privatedefault 或者 static 关键字修饰的方法是隐式抽象的,在编译的时候会自动加上 public abstract 修饰符。

  2. 从 Java 8 开始,接口中允许有静态方法

    静态方法无法由(实现了该接口的)类的对象调用,它只能通过接口名来调用,比如说 Electronic.isEnergyEfficient("LED")

    接口中定义静态方法的目的是为了提供一种简单的机制,使我们不必创建对象就能调用方法,从而提高接口的竞争力。

  3. 默认方法为实现该接口而不覆盖该方法的类提供默认实现。既然要提供默认实现,就要有方法体,后面不能直接使用“;”号来结束——编译器会报错。

假如我们需要在所有的实现类中追加某个具体的方法, default 方法就很有帮助。

  1. 接口不允许直接实例化,否则编译器会报错。

  1. 接口可以是空的,既可以不定义变量,也可以不定义方法。最典型的例子就是 Serializable 接口,在 java.io 包下。
public interface Serializable { }

Serializable 接口用来为序列化的具体实现提供一个标记,也就是说,只要某个类实现了 Serializable 接口,那么它就可以用来序列化了。

  1. 不要在定义接口的时候使用 final 关键字,否则会报编译错误,因为接口就是为了让子类实现的,而 final 阻止了这种行为。

  1. 接口的抽象方法不能是 private、protected 或者 final,否则编译器都会报错。

  1. 接口的变量是隐式 public static final(常量),所以其值无法改变。

实现接口

类通过 implements 关键字来实现接口。一个类可以实现多个接口,但必须用逗号分隔接口名。实现接口的类必须提供接口中所有方法的实现(除非该类是抽象类)。

public class MyClass implements MyInterface {  
    @Override  
    public void doSomething() {  
        // 方法实现  
    }  
  
    // 对于默认方法,可以选择覆盖或不覆盖  
    @Override  
    public void defaultMethod() {  
        // 覆盖默认方法实现  
    }  
  
    // 静态方法不能被覆盖,但可以直接通过接口名调用  
}

默认方法和静态方法

从Java 8开始,接口中可以包含默认方法和静态方法。默认方法提供了一种在不破坏现有实现的情况下向接口添加新方法的方式。静态方法则类似于工具方法,可以直接通过接口名调用。

Lambda表达式与函数式接口

Lambda 表达式 是Java 8引入的一种简洁的匿名函数实现方式。函数式接口是只包含一个抽象方法的接口(可以包含多个默认方法或静态方法)。Lambda 表达式 可以用于实现函数式接口,从而以更简洁的方式编写代码。

接口的作用

  1. 使某些实现类具有需要的功能,比如说,实现了 Cloneable 接口的类具有拷贝的功能,实现了 Comparable 或者 Comparator 的类具有比较功能。

  2. Java 原则上只支持单一继承,但通过接口可以实现多重继承的目的

    如果有两个类共同继承(extends)一个父类,那么父类的方法就会被两个子类重写。然后,如果有一个新类同时继承了这两个子类,那么在调用重写方法的时候,编译器就不能识别要调用哪个类的方法了。这也正是著名的菱形问题

在这里插入图片描述
Java通过以下方式解决了这个问题:

  1. 默认方法:从Java 8开始,接口可以包含具有默认实现的方法,称为默认方法。这样,即使多个接口中有相同的方法签名,实现这些接口的类也可以直接使用其中一个接口中的默认实现,或者覆盖该方法以提供自己的实现。

  2. 静态方法:接口还可以包含静态方法,但这些方法不会被实现类继承。

  3. 显式覆盖:如果多个接口中存在相同的方法签名,并且每个接口提供了不同的默认实现,那么实现这些接口的类必须明确地覆盖这个方法并提供自己的实现

interface InterfaceA {
    default void method() {
        System.out.println("InterfaceA's method");
    }
}

interface InterfaceB extends InterfaceA {
    default void method() {
        System.out.println("InterfaceB's method");
    }
}

class MyClass implements InterfaceB {
    // 必须覆盖method()方法,因为InterfaceA和InterfaceB中都有相同的默认实现
    @Override
    public void method() {
        System.out.println("MyClass's method");
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.method();  // 输出: MyClass's method
    }
}

在这个例子中:

  • InterfaceAInterfaceB 都声明了一个默认方法 method()
  • InterfaceB 继承了 InterfaceA 并重写了 method() 方法。
  • MyClass 实现了 InterfaceB,并且必须明确地覆盖 method() 方法,因为它同时继承了来自 InterfaceAInterfaceB 的不同实现。

接口的用途

定义规范

接口为类提供了一种定义规范的方式。任何实现接口的类都必须实现接口中声明的所有方法(除非该类是抽象类)。这有助于确保类的行为一致性,并使得代码更加易于理解和维护。

假设我们需要为一组形状定义通用的行为,比如计算面积和周长。我们可以创建一个接口 Shape,其中定义了这两个方法。

public interface Shape {
    double area();
    double perimeter();
}

然后,我们可以创建几个类来实现这个接口,例如 CircleRectangle

public class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}

public class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public double area() {
        return width * height;
    }

    @Override
    public double perimeter() {
        return 2 * (width + height);
    }
}

多态

接口是实现多态性的重要手段。通过接口引用,我们可以指向实现了该接口的任何对象,并在运行时调用其实现的方法。这种机制提高了代码的灵活性和可扩展性。

现在我们可以通过 Shape 接口引用来指向 CircleRectangle 的实例,并调用它们的方法。

public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        Shape rectangle = new Rectangle(4.0, 6.0);

        System.out.println("Circle Area: " + circle.area());
        System.out.println("Rectangle Perimeter: " + rectangle.perimeter());
    }
}

解耦

接口有助于实现模块之间的解耦。通过定义接口,我们可以将模块之间的依赖关系从具体的实现类中分离出来,从而降低了模块之间的耦合度。这使得在修改或替换某个模块的实现时,不需要修改其他模块的代码

如果我们需要改变 CircleRectangle 类的具体实现,只要它们仍然遵循 Shape 接口的定义,那么上面的 Main 类就不需要做任何改动。

框架设计

在 Java 框架设计中,接口扮演着核心角色。许多框架都通过定义接口来提供扩展点,允许开发者通过实现这些接口来定制框架的行为。例如,Spring 框架中的大量接口用于实现依赖注入AOP等功能。

在框架设计中,接口可以作为扩展点。例如,我们可以创建一个 ShapeFactory 接口,允许用户通过实现该接口来定制创建形状的方式。

public interface ShapeFactory {
    Shape createShape(String type);
}

// 具体实现
public class SimpleShapeFactory implements ShapeFactory {
    @Override
    public Shape createShape(String type) {
        if ("circle".equals(type)) {
            return new Circle(1.0);  // 默认半径
        } else if ("rectangle".equals(type)) {
            return new Rectangle(1.0, 1.0);  // 默认尺寸
        }
        throw new IllegalArgumentException("Unsupported shape type");
    }
}

// 使用
public class Main {
    public static void main(String[] args) {
        ShapeFactory factory = new SimpleShapeFactory();
        Shape shape = factory.createShape("circle");
        System.out.println("Shape Area: " + shape.area());
    }
}

通过这种方式,我们可以轻松地扩展 ShapeFactory 的实现,而不必修改客户端代码。

接口和抽象类的区别

接口(英文:Interface),在 Java 中是一个抽象类型,是抽象方法的集合;接口通过关键字 interface 来定义。接口与抽象类的不同之处在于:

  • 抽象类可以有方法体的方法,但接口没有(Java 8 以前)。
  • 接口中的成员变量隐式为 static final,但抽象类不是的。
  • 一个类可以实现多个接口,但只能继承一个抽象类。
  • 接口和接口中的每个方法都是隐式抽象的,不需要使用 abstract

语法层面

  • 抽象类可以提供成员方法的实现细节,而接口中只能存在 public abstract 方法;

  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的;

  • 接口中不能含有静态代码块,而抽象类可以有静态代码块;

  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

设计层面

抽象类是对一种事物的抽象,即对类抽象,继承抽象类的子类和抽象类本身是一种 is-a 的关系。抽象类是对整个类整体进行抽象,包括属性、行为。

但是接口却是对类局部(行为)进行抽象。接口和类之间并没有很强的关联关系,举个例子来说,所有的类都可以实现 [[序列化与反序列化#序列化接口 Serializable|Serializable]],从而具有序列化的功能,但不能说所有的类和 Serializable 之间是 is-a 的关系。

继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系
如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计

接口的三种模式

在编程领域,好的设计模式能够让我们的代码事半功倍。在使用接口的时候,经常会用到三种模式,分别是策略模式、适配器模式和工厂模式。

策略模式

策略模式的思想是,针对一组算法,将每一种算法封装到具有共同接口的实现类中,接口的设计者可以在不影响调用者的情况下对算法做出改变,根据所传递的参数对象的不同而产生不同的行为

适配器模式

适配器模式的思想是,针对调用者的需求对原有的接口进行转接。生活当中最常见的适配器就是HDMI(英语:High Definition Multimedia Interface,中文:高清多媒体接口)线,可以同时发送音频和视频信号。

例如,Coach 接口中定义了两个方法(defend()attack()),如果类直接实现该接口的话,就需要对两个方法进行实现。

如果我们只需要对其中一个方法进行实现的话,就可以使用一个抽象类作为中间件,即适配器(AdapterCoach)用这个抽象类实现接口,并对抽象类中的方法置空(方法体只有一对花括号)

这时候,新类就可以绕过接口,继承抽象类,我们就可以只对需要的方法进行覆盖,而不是接口中的所有方法。

工厂模式

将工厂抽象成一个接口,由具体的工厂类来实现这个接口,并创建具体的产品对象。即,一个工厂接口可以创建多种不同类型的对象,每种类型的产品对象由相应的具体工厂类来创建。

接口工厂模式的基本结构通常包括以下角色:

  • 抽象产品接口(Product):定义产品的标准接口规范;

  • 具体产品类(ConcreteProduct):实现抽象产品接口规范;

  • 抽象工厂接口(Factory):定义工厂的标准接口规范;

  • 具体工厂类(ConcreteFactory):实现抽象工厂接口规范,并通过实现工厂接口的方法来创建具体的产品对象。

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

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

相关文章

@Mapper报红

检查pom.xml&#xff0c;导入 org.mybatis.spring.boot 依赖&#xff1a; <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency…

Java-EE 网络编程(1)

目录 一、网络初识 二、协议 协议的定义 协议分层 协议分层的好处 TCP/IP五层模型 五层协议 协议之间如何配合工作的 三、网络编程套接字 TCP/UDP特点 UDP回显服务器 UDP的socket api 回显服务器 回显客户端 一、网络初识 先了解一些核心概念&#xff1a; 局域网…

CsvExport:一个.Net高性能、低内存的CSV导出开源库

在我们项目开发中&#xff0c;导出CSV数据功能是非常常见的。 今天推荐一个高性能、低内存的CSV导出开源库。 01 项目简介 CsvExport是一个基于C#非常简单和快速的CSV导出开源库。 该开源库的核心特点&#xff1a; 导出功能兼容性高&#xff08;自动检测分隔符&#xff0c;…

CSC7720 可正、负应用5V2.1A同步整流

CSC7720是一款同步整流芯片&#xff0c;可以代替肖特基二极管提高反激变换器的效率。CSC7720 支持工作在非连续模式&#xff08;DCM&#xff09;的反激变换器中&#xff0c;其内部集成了低 RDS&#xff08;ON &#xff09;的 N 沟道功率MOSFET&#xff0c;外围应用简单&#xf…

STM32—SPI通信

1.SPI简介 四根通信线&#xff1a;SCK(Serial Clock)(时钟线)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select) 同步、全双工 支持总线挂载多设备&#xff08;一主多从&#xff09; 所有SPI设备的SCK、MOSI、MISO分别连在一起 主机…

【Python学习-UI界面】PyQt5 小部件13-Slider 拖动条

高级布局管理器&#xff0c;允许通过拖动边界来动态改变子小部件的大小。 Splitter控件提供一个手柄&#xff0c;可以拖动以调整控件的大小 样式如下: 常用方法如下&#xff1a; 序号方法描述1addWidget将小部件添加到拆分器的布局中2indexOf返回布局中小部件的索引3insetW…

炖羊肉

炖羊肉必备的两种香辛料&#xff0c;白胡椒、白芷&#xff0c;让你炖出的羊肉软嫩&#xff0c;汤汁鲜美 在烹饪艺术的广阔领域中&#xff0c;炖羊肉作为一道经典佳肴&#xff0c;其风味的层次与深度往往取决于所选香辛料的精妙搭配。其中&#xff0c;白胡椒与白芷作为炖羊肉时…

fun状态上传,并可手动控制

文章目录 引言上传原因:矛盾点:基础工程源码: 代码实操fun状态上传fun状态下发控制 引言 上传原因: 续上一节, 我们把fun像小灯一样, 加入了预警工程, 但是我们fun其实还有其他用处, 比如我们人工手动开风扇, 排风, 所以我们需要把fun的状态上传, 然后也可以通过服务器手动控制…

EmguCV学习笔记 VB.Net 2.3 Mat类

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV学习笔记目录 Vb.net EmguCV学习笔记目录 C# 笔者的博客网址&#xff1a;VB.Net-CSDN博客 教程相关说明以及如何获得pdf教…

StackStorm自动化平台

1. StackStorm概述 1.1 StackStorm介绍 StackStorm是一个开源的事件驱动自动化平台&#xff0c;它允许开发者和系统管理员自动化IT和网络操作。StackStorm结合了IT运维、DevOps和网络安全团队的需求&#xff0c;提供了一个集中式的工作流自动化解决方案&#xff0c;包括事件响…

Windows10、ARM开发板、虚拟机Ubuntu可同时上网

一、Windows10端设置 1、打开网络配置 2、打开适配器 3、将window连接的wifi网卡设置为共享模式 4、查看本地连接的ip 到此&#xff0c;window10端设置完毕 二、设置虚拟机端&#xff08;Ubuntu&#xff09; 1、打开网络配置 2、打开适配器 3、查看本地连接的网卡名称 4、配置…

超越流水线,企业研发规范落地新思路

作者&#xff1a;子丑 内容大纲&#xff1a; 1、研发规范≠流程约束 2、自动化工具→研发规范载体 3、研发规范在工具上的落地示例 4、研发规范的选型方法与常见实践 研发规范≠流程约束 这个故事特别适合研发规范的场景&#xff0c;我们要避免成为把猫绑在柱子上的信众…

[NOIP2007 普及组] 守望者的逃离 题解

题意 给定 M ( 0 ≤ M ≤ 1 0 3 ) , S ( 1 ≤ S ≤ 1 0 8 ) , T ( 1 ≤ T ≤ 3 1 0 5 ) M(0 \leq M \leq 10^3),S(1 \leq S \leq 10^8),T(1 \leq T \leq 3\times 10^5) M(0≤M≤103),S(1≤S≤108),T(1≤T≤3105)&#xff0c;守望者开始在位置 0 0 0&#xff0c;对于每一秒&…

C++调用C#方法(附踩坑点)

C调用C#方法 写在前面效果思路步骤可能的问题 写在后面 写在前面 工作需要用C调用C#写到代码&#xff0c;看来网上写的方法&#xff0c;自己也踩了一些坑&#xff0c;这里总结一下&#xff0c;我只试了CLR的方法。 主要参考了下面几篇博客 C调用C#库简单例程&#xff08;Lucky…

【BUU】[NewStarCTF 2023 公开赛道]Final -CP读取文件内容

漏洞检测 访问首页发现是ThinkPHP5 的站点 用工具扫描一下,发现存在ThinkPHP5.0.23 RCE漏洞 访问验证,写入shell 成功写入shell. 根目录发现flag,但是权限不足 提权获取flag 准备提权,这里一开始尝试了find,但是find权限不足 尝试采用cp命令,移动到web目录,发现访问还是…

MySQL InnoDB中一个update语句从执行到提交的全过程(3)

接上文MySQL InnoDB中一个update语句从执行到提交的全过程&#xff08;2&#xff09;-CSDN博客 目录 六、本地提交 怎样保证binlog和redo log的状态一致呢&#xff1f; MySQL 中的内部 XA 机制 宕机时不同状态的处理 物理落盘策略 七、主备复制 八、返回提交成功 总结一…

Windows 10/11和Linux双系统用户请勿安装最新更新 否则将无法启动

据蓝点网报道&#xff0c;Windows 10/11 最新累积更新存在已知问题&#xff0c;如果你同时安装了 Linux 双系统则会在更新后导致系统无法正常启动。 启动时会出现如下报错&#xff1a; Verifiying shim SBAT data failed: Security Policy Violation.Something has gone serio…

私域场景中的数字化营销秘诀

​在当今的商业世界&#xff0c;私域场景的营销变得愈发重要。今天咱们就来深入探讨一下私域场景中的几个关键营销手段。 一、会员管理与营销 企业一旦拥有完善的会员体系&#xff0c;数字化手段就能大放异彩。它可以助力企业对会员进行精细划分&#xff0c;深度了解会员的消费…

win11笔记本电脑 声音输出设置为扬声器 | 添加蓝牙设备 | win11环境变量设置

&#x1f947; 版权: 本文由【墨理学AI】原创首发、各位读者大大、敬请查阅、感谢三连 &#x1f389; 声明: 作为全网 AI 领域 干货最多的博主之一&#xff0c;❤️ 不负光阴不负卿 ❤️ 文章目录 win11笔记本电脑 声音输出设置为扬声器搜索设置&#xff0c;打开设置选择声音选…

AI安全-文生图

1 需求 2 接口 3 示例 大模型图像安全风险探析 - 先知社区 前言 文生图模型是一种新兴的人工智能技术,它通过对大规模文本数据的学习,能够生成逼真的图像。这种模型包含两个主要组件:一个文本编码器和一个图像生成器。 文本编码器接收文本输入,并将其转换为一种数字化的表示…