首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
第一章:OOP:继承
第二章:关键字 super
第三章:关键字 static
第四章:成员变量初始化
第五章:OOP:多态
第六章:关键字 native
第七章:关键字 final
第八章:Object 类的使用
第九章:抽象类
第十章:接口
第十一章:经典接口介绍
第十二章:包装类
第十三章:内部类
当前位置:
首页>>
技术小册>>
Java语言基础6-面向对象高级
小册名称:Java语言基础6-面向对象高级
10.1 概述 - 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java 不支持多重继承。有了接口,就可以得到多重继承的效果。 - 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is a 的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打 印机、扫描仪、摄像头、充电器、MP3、手机、数码相机、移动硬盘等都支持 USB接口 连接。 - 接口就是规范,定义的是一组规则,体现了现实世界中"如果你是/要...则必须能..."的思想。继承是一个"是不是"的 is-a 关系,而接口实现则是 "能不能"的 has-a 关系。 - 例如:我们能不能用 USB 进行连接,或是否具备 USB 通信功能,就看我们是否遵循 USB 接口规范 ![](/uploads/images/20230725/a3c70ae0a15a6c25666ec6e5ca4c684f.png) - 例如:Java 程序是否能够连接使用某种数据库产品,那么要看该数据库产品有没有实现 Java 设计的 JDBC 规范。 ![](/uploads/images/20230725/811bca57397bfafdf578aae3c6e46f4f.png) 10.2 语法 10.10.1 概述 - 接口的定义,和定义类的语法类似,但是使用的是 interface 关键字。它也会编译成 .class 文件,但是一定要明确的是它不是类,而是另外一种引用数据类型。 引用数据类型:数组、类、接口。 10.10.2 接口的语法格式 - 语法: ```bash 权限修饰符 interface 接口名 { // 接口的成员列表: // ① 静态常量 // ② 抽象方法 // ③ 默认方法 // ④ 静态方法 // ⑤ 私有方法 } ``` 示例: ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface Fly { void fly(); } ``` ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Bird implements Fly { @Override public void fly() { System.out.println("小鸟在飞"); } } ``` ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public class SuperMan implements Fly { @Override public void fly() { System.out.println("超人在飞"); } } ``` ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Test { public static void main(String[] args) { Fly bird = new Bird(); bird.fly(); System.out.println("-----------------"); Fly superman = new SuperMan(); superman.fly(); } } ``` 10.10.3 接口中的成员说明 - 接口定义的是多个类共同的公共行为规范,这些行为规范是和外部交流的通道,这就意味着接口里通常定义一组公共方法。 - 在 JDK8 之前,接口中只允许出现: - ① 公共的静态常量:其中 public static final 可以省略。 - ② 公共的抽象方法:其中 public abstract 可以省略。 理解:接口是从多个相似类中抽象出来的规范,不需要提供具体实现。 - 在 JDK8 时,接口中允许声明默认方法和静态方法: - ① 公共的默认方法:其中 public 可以省略,但是建议保留,但是 default 不能省略。 - ② 公共的静态方法:其中 public 可以省略,但是建议保留,但是 static 不能省略。 - 在 JDK9 的时候,接口允许私有方法。 目前而言,接口中除了静态常量、抽象方法、默认方法、静态方法、私有方法外,接口中不能有其他成员(构造器、初始化块),接口中没有成员变量需要初始化。 10.10.4 接口中成员变量的思考 - 【问】:为什么接口中只能声明公共的静态常量? - 【答】:因为接口是标准规范,那么在规范中需要声明一些底线边界值,当实现者在实现这些规范的时候,不能随意的去修改和触碰这些底线边界值,否则就有 危险 。比如:USB1.0 规范中规定最大传输速率是 1.5 Mbps ,最大输出电流是 5V/500 mA 。 USB3.0 规范中规定最大传输速率是5Gbps (500MB/s) ,最大输出电流是 5V/900 mA。 - 【问】:为什么 JDK8 之前,只允许出现公共的默认方法? - 【答】:因为接口是代表行为标准,它只规定方法的 签名 ,方法 = 方法头 + 方法体;其中,方法头又称为方法签名,方法签名 = 修饰符 返回值类型 方法名(形参列表); ,而方法体是 { // 执行逻辑 };换言之,方法签名已经提供了功能的描述信息,调用者无需关注具体的方法实现细节。 - 【问】:为什么 JDK8 之后,允许接口中定义静态方法和默认方法? - 【答】: - 静态方法:在之前的标准类库设计中,有很多 Collection/Collections 、Path/Paths 这样的成对的接口和类,后面的类中都是静态方法,而这样静态方法都是为了前面的接口服务的,那么在设计这样一对 API 的时候,还不如将静态方法直接定义到接口中,这样使用和维护更为方便。 - 默认方法: - ① 如果要在老版本的接口中提供方法,如果添加抽象方法,就会涉及到原来使用这些接口的实现类都需要重写抽象方法,为了保持和旧版本的代码的兼容性,只能允许接口中定义默认方法,比如:JDK8 中对 Collection 、List 、Comparator 等接口都提供了丰富的默认方法。 - ② 当我们接口中的某个抽象方法,在很多类中的实现代码都是一样的,那么此时就可以将这个抽象方法设计为默认方法更为合适,那么实现类可以选择重写,也可以选择不重写。 - 【问】:为什么 JDK9 要允许接口中定义私有方法? - 【答】:因为有了默认方法和静态方法这样具有具体实现的方法,那么就可能出现多个方法由相同的代码抽取,而这些相同的代码抽取出来的代码又希望只在接口内部使用,所以增加了私有方法。 10.3 实现接口 10.3.1 实现接口的语法 - 语法: ```bash 权限访问修饰符 class implements 接口{ // 重写接口中的抽象方法【必须】,如果实现类是抽象类,可以不重写 // 重写接口中的默认方法【可选】 } ``` ```bash 权限访问修饰符 class extends 父类 implements 接口{ // 重写接口中的抽象方法【必须】,如果实现类是抽象类,可以不重写 // 重写接口中的默认方法【可选】 } ``` 注意: - ① 如果接口的实现类是非抽象类,那么必须重写接口中所有的抽象方法。 - ② 默认方法可以选择重写,也可以选择不重写。重写的时候,在子类中,default 单词就不需要写了,就如果重写 abstract 抽象方法一样。 - ③ 不能重写静态方法。 - 示例: ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface Fly { void fly(); default void addOil() { System.out.println("加油"); } } ``` ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Bird implements Fly { @Override public void fly() { System.out.println("小鸟在飞"); } } ``` ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Plane implements Fly { @Override public void fly() { System.out.println("飞机在飞"); } @Override public void addOil() { System.out.println("飞机加航空煤油"); } } ``` ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public class SuperMan implements Fly { @Override public void fly() { System.out.println("超人在飞"); } } ``` ```bash package com.github.demo4; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Test { public static void main(String[] args) { Bird bird = new Bird(); bird.fly(); System.out.println("-----------------"); SuperMan superman = new SuperMan(); superman.fly(); System.out.println("-----------------"); Plane plane = new Plane(); plane.fly(); plane.addOil(); } } ``` 10.3.2 如果调用对应的方法? - ① 对于接口中的静态方法,只能通过 接口名.方法名() 调用。 - ② 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用。 10.3.3 练习 - 示例: ```bash package com.github.demo5; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface LiveAble { /** * 喝水 */ static void drink() { System.out.println("喝水"); } /** * 呼吸 */ void breathe(); /** * 吃饭 */ void eat(); /** * 睡觉 */ default void sleep() { System.out.println("静止不动"); } } ``` ```bash package com.github.demo5; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Animal implements LiveAble { @Override public void breathe() { System.out.println("吸入氧气呼出二氧化碳"); } @Override public void eat() { System.out.println("吃东西"); } @Override public void sleep() { System.out.println("闭上眼睛睡觉"); } } ``` ```bash package com.github.demo5; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Plant implements LiveAble { @Override public void breathe() { System.out.println("吸入二氧化碳呼出氧气"); } @Override public void eat() { System.out.println("吸收营养"); } } ``` ```bash package com.github.demo5; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Test { public static void main(String[] args) { Animal animal = new Animal(); animal.breathe(); animal.eat(); animal.sleep(); System.out.println("-------------------"); Plant plant = new Plant(); plant.breathe(); plant.sleep(); plant.eat(); System.out.println("-------------------"); LiveAble.drink(); } } ``` 10.4 接口的多实现 - 在之前学过,在继承体系中,一个类只能继承一个父类。但是,对于接口而言,一个类是可以实现多个接口的,这叫做 接口的多实现 。与此同时,一个类能继承一个父类,同时实现多个接口。 - 语法: ```bash 权限修饰符 class 实现类 implements 接口1,接口2,接口3,...{ // 重写接口中的抽象方法【必须】,如果实现类是抽象类,可以不重写 // 重写接口中的默认方法【可选】 } ``` ```bash 权限访问修饰符 class extends 父类 implements 接口1,接口2,接口3,...{{ // 重写接口中的抽象方法【必须】,如果实现类是抽象类,可以不重写 // 重写接口中的默认方法【可选】 } ``` 注意:接口中多个抽象方法的时候,实现类必须重写所有的抽象方法。如果抽象方法有重名的,只需要重写一次。 示例: ```bash package com.github.demo6; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface A { void showA(); void show(); } ``` ```bash package com.github.demo6; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface B { void showB(); void show(); } ``` ```bash package com.github.demo6; /** * @author maxiaoke.com * @version 1.0 * @return */ public class C implements A, B { @Override public void showA() { System.out.println("showA"); } @Override public void showB() { System.out.println("showB"); } @Override public void show() { System.out.println("show"); } } ``` ```bash package com.github.demo6; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Test { public static void main(String[] args) { C c = new C(); c.show(); c.showA(); c.showB(); } } ``` 10.5 默认方法冲突问题 10.5.1 亲爹优先原则 - 当一个类,既继承了一个父类,又实现了若干个接口的时候,父类中的成员方法和接口中的默认方法重名,子类就近选择执行父类的成员方法。 - 示例: ```bash package com.github.demo7; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface A { default void method() { System.out.println("接口A中的method方法"); } } ``` ```bash package com.github.demo7; /** * @author maxiaoke.com * @version 1.0 * @return */ public class B { public void method() { System.out.println("B类中的method方法"); } } ``` ```bash package com.github.demo7; /** * @author maxiaoke.com * @version 1.0 * @return */ public class C extends B implements A { // 不重写method方法 } ``` ```bash package com.github.demo7; /** * @author maxiaoke.com * @version 1.0 * @return */ public class D extends B implements A { @Override public void method() { System.out.println("D中的method方法"); } } ``` ```bash package com.github.demo7; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Test { public static void main(String[] args) { C c = new C(); c.method(); // B类中的method方法 System.out.println("------------"); D d = new D(); d.method(); // D中的method方法 } } ``` 10.5.2 必须做出选择 - 当一个类同时实现多个接口,而多个接口中包含方法签名相同的默认方法时,必须进行重写,否则编译报错。在重写的方法中,可以选择使用 接口名.super.方法名 的方法选择保留哪个接口中的默认方法,也可以选择完全自己重写。 - 示例: ```bash package com.github.demo8; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface A { default void method() { System.out.println("今天晚上陪我吃饭"); } } ``` ```bash package com.github.demo8; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface B { default void method() { System.out.println("今天晚上陪我逛街"); } } ``` ```bash package com.github.demo8; /** * @author maxiaoke.com * @version 1.0 * @return */ public class C implements A, B { @Override public void method() { // 选择保留其中一个,通过“接口名.super.方法名"的方法选择保留哪个接口的默认方法。 A.super.method(); } } ``` ```bash package com.github.demo8; /** * @author maxiaoke.com * @version 1.0 * @return */ public class D implements A, B { @Override public void method() { System.out.println("滚,写代码,它不香吗?"); } } ``` ```bash package com.github.demo8; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Test { public static void main(String[] args) { C c = new C(); c.method(); // 今天晚上陪我吃饭 System.out.println("----------"); D d = new D(); d.method(); // 滚,写代码,它不香吗? } } ``` 10.6 接口的多继承 - 一个接口能继承另一个接口或者多个接口,接口的继承也使用 extends 关键字,子接口继承父接口的方法。 温馨提示: - ① 子接口重写默认方法的时候,default 关键字可以保留。 - ② 子类重写默认方法的时候,default 关键字不可以保留。 - 示例: ```bash package com.github.demo9; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface A { void a(); default void methodA() { System.out.println("A接口的default修饰的methodA方法"); } } ``` ```bash package com.github.demo9; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface B { void b(); default void methodB() { System.out.println("A接口的default修饰的methodB方法"); } } ``` ```bash package com.github.demo9; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface C extends A, B { @Override default void methodB() { System.out.println("C接口的default修饰的默认方法"); } } ``` ```bash package com.github.demo9; /** * @author maxiaoke.com * @version 1.0 * @return */ public class D implements C { @Override public void a() { System.out.println("D:a"); } @Override public void b() { System.out.println("D:b"); } } ``` ```bash package com.github.demo9; /** * @author maxiaoke.com * @version 1.0 * @return */ public class E implements A, B, C { @Override public void a() { System.out.println("E: a"); } @Override public void b() { System.out.println("E: b"); } } ``` ```bash package com.github.demo9; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Test { public static void main(String[] args) { D d = new D(); d.a(); // D:a d.b(); // D:b d.methodA(); // A接口的default修饰的methodA方法 d.methodB(); // C接口的default修饰的默认方法 System.out.println("--------------"); E e = new E(); e.a(); // E: a e.b(); // E: b e.methodA(); // A接口的default修饰的methodA方法 e.methodB(); // C接口的default修饰的默认方法 } } ``` 10.7 接口和实现类对象的多态引用 - 实现类实现接口,类似于子类继承父类,因此,接口类型的变量和实现类对象之间,也构成多态引用。通过接口类型的变量调用方法,最终执行的是实现类对象重写的方法。 - 示例: ```bash package com.github.demo10; /** * @author maxiaoke.com * @version 1.0 * @return */ public interface Fly { void fly(); } ``` ```bash package com.github.demo10; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Bird implements Fly { @Override public void fly() { System.out.println("小鸟飞"); } } ``` ```bash package com.github.demo10; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Plane implements Fly { @Override public void fly() { System.out.println("飞机在飞"); } } ``` ```bash package com.github.demo10; /** * @author maxiaoke.com * @version 1.0 * @return */ public class Test { public static void main(String[] args) { Fly bird = new Bird(); bird.fly();// 小鸟飞 System.out.println("----------------"); Fly plane = new Plane(); plane.fly();// 飞机在飞 } } ```
上一篇:
第九章:抽象类
下一篇:
第十一章:经典接口介绍
该分类下的相关小册推荐:
Java语言基础8-Java多线程
Java高并发秒杀入门与实战
SpringBoot合辑-初级篇
Java语言基础14-枚举和注解
Java语言基础15-单元测试和日志技术
经典设计模式Java版
手把手带你学习SpringBoot-零基础到实战
Java语言基础1-基础知识
Java必知必会-Maven初级
Mybatis合辑4-Mybatis缓存机制
Java语言基础13-类的加载和反射
java源码学习笔记