13.0_[Java 面向接口]-类和接口

##################################################

目录

接口基础知识

什么是接口

如何定义和实现接口

接口表示一种能力

为什么需要接口

使用接口实现防盗门功能

扩展接口的能力

使用接口的好处

接口表示一种约定

使用接口实现打印机功能

面向接口编程

何谓面向接口

面向接口编程的好处


##################################################

接口基础知识

——————————

什么是接口

        接口也是 Java 中非常重要的内容

        接口和多态及抽象类有非常密切的关系

在生活中 接口是一套规范

满足这个规范的设备就可以将她们组装到一起从而实现该设备的功能

        以 USB 为例子

        USB 接口实际上是某些企业和组织等制定的一种约定或标准

        规定了接口的大小、形状、各引脚信号的范围和含义、通信速度、通信流程等

        并按照该约定或标准来设计各种设备 例如

U 盘

USB 风扇

USB 键盘

        都可以插到 USB 口上正常工作

        而在软件中 接口同样是一种规范和标准

        她们可以约束类的行为 是一些方法特征的集合 但是没有方法的实现

        从这个角度来看 接口可以看作是一种特殊的 抽象类

        但是采用与抽象类完全不同的语法来表示 两者的设计理念也是完全不同的

        抽象类利于代码复用 接口利于代码的扩展和维护

        Java 中接口的定义语法和类实现接口的语法如下:

[修饰符] interface 接口名 extends 父接口_1, 父接口_N {

    /* 常量定义 */

    /* 方法定义 */
}
class 类名 extends 父类名 implements 接口_1, 接口_N {

    /* 类成员 */
}

        接口的命名规则与类相同

        如果修饰符是 public 则该接口在整个项目中可见

        如果省略修饰符 则该接口只能在当前包可见

        接口中可以定义常量 不能定义变量

        接口中的属性都会自动用 public static final 修饰

        即接口中的属性都是全局静态常量

        接口中的常量必须在定义时指定初始值:

public static final int PO = 3.14;
int PI = 3.14;    // 在接口中这两个定义语句效果完全相同
int PI;    // 错误! 在接口中必须指定初始值 在类中会有默认值

        接口中所有方法都是抽象方法

        接口中方法都会自动用 public abstract 修饰

        即接口中只有全局抽象方法

        和抽象类一样 接口也不能实例化

        接口中也不能有构造方法

        接口之间可以通过 extends 实现继承关系

        一个接口可以继承多个接口 但是接口不能继承类!

        接口的实现类必须实现接口的全部方法

        否则必须定义为抽象类!

——————————

如何定义和实现接口

        一个类只能有一个直接父类 但可以通过 implements 实现多个接口

        当类在继承父类的同时又实现了多个接口时 extends 关键字必须位于 implements 关键字之间

        下面就使用 Java 接口来模拟生活中的  USB 接口

        在现实生活中 USB 接口相关工作都是按照如下步骤进行:

约定 USB 接口标准

制作符合 USB 接口约定的各种具体设备

将 USB 设备插到 USB 接口上进行工作

        我们可以先定义 USB 接口 通过 service 方法提供服务:

interface UsbInterface {
/* USB 接口 */

	void service ();	// USb 接口提供的服务方法
}

        然后定义 U 盘类 实现 USB 接口中的 services 方法进行数据传输

class UDisk implements UsbInterface {
/* U 盘类 */
	
	public void service () {
	/* 实现 USB 接口的服务方法 */
		
		System.out.println ( "连接 USB 接口开始传输数据.." );
	}
}

        定义 USB 风扇类 实现 USB 接口中的 services 方法获得电流使风扇转动:

class UFan implements UsbInterface {
/* USB 风扇类 */

	public void service () {
	/* 实现 USB 接口中的 service 方法 */
	
		System.out.println ( "连接 USB 口, 获得电流, 风扇开始转动!呼呼呼" );
	}
}

        测试类 使用 U 盘传输数据使用 USB 风扇吹风:

public class Test {
/* 测试类 */
	
	public static void main (String [] args) {
		
		// USB 盘
		UsbInterface uDisk = new UDisk();	// USB 盘向上转换为 USB 接口类型
		uDisk.service ();	// 调用传输数据服务
		
		// USB 风扇
		UsbInterface uFan = new UFan();
		uFan.service();
	}
}

        我们声明了接口 UsbInterface

        实例化接口的实现类 uDisk 和 Ufan 类型对象

        遵循了向上转型的规则

        运行结果如下:

C:\Users\byme\javaTest>javac Test.java

C:\Users\byme\javaTest>java Test
连接 USB 接口开始传输数据..
连接 USB 口, 获得电流, 风扇开始转动!呼呼呼

C:\Users\byme\javaTest>

##################################################

接口表示一种能力

——————————

为什么需要接口

        现在假如有一个需求 要求实现防盗门的功能:

        门有 开/关 两个功能

        锁有 上锁/开锁 两个功能

        首先防盗门是一个门 她有一把锁

        按照面向对象的思想 可以将门和锁分别定义为抽象类

        但是不能让防盗门在继承门的同时又继承锁:

第一 防盗门不是锁 不符合继承中 is a 的关系

第二 Java 仅仅支持单继承

        如何解决这个问题呢?现在就需要用到接口了!

        可以将锁定义为接口 让防盗门继承门 实现锁的接口

——————————

使用接口实现防盗门功能

        前面经过模拟 USB 接口 我们已经了解到 Java 接口的语法和规则

        现在就使用接口来实现防盗门功能

        防盗门是一个门 符合 is a 的关系 可以抽象出一个门类

        防盗门有一把锁 代表她有 上锁/开锁 的能力 表达的是 has a 的关系

        这里可以将锁定义成接口 让防盗门继承门类实现锁接口

        步骤如下:

定义抽象类 Door 具备开关锁功能

定义 Lock 接口具备上锁开锁的能力

定义 TheftproofDoor 类继承 Door 类的同时实现 Lock 接口

编写测试类 实现防盗门的开关和上锁开锁的功能

        定义抽象类 Door 具备开关锁功能:

abstract class Door {
/* 门类 */

	public abstract void open();	// 抽象开锁方法
	public abstract void close();	// 抽象关锁方法
}

        定义 Lock 接口 具备上锁开锁的能力:

interface Lock {
/* 锁接口 */

	void lockUp();	// 上锁服务
	void openLock();	// 开锁服务
}

        定义 TheftproofDoor 类继承 Door 类的同时实现 Lock 接口:

class TheftproofDoor extends Door implements Lock {
/* 继承门类 实现接口的方法 */

	public void open() {
	/* 实现开门方法 */
	
		System.out.println ( "用力推,门开了……" );
	}
	
	public void close() {
	/* 实现关门方法 */
	
		System.out.println ( "轻轻拉,门关上了……" );
	}
	
	public void lockUp() {
	/* 实现上锁服务 */
		
		System.out.println ( "插进钥匙,向左旋转钥匙三圈,锁上了,拔出钥匙!" );
	}
	
	public void openLock () {
	/* 实现开锁服务 */
		
		System.out.println ( "插进钥匙,向右旋转钥匙三圈,锁打开了,拔出钥匙.." );
	}
}

        编写测试类 实现防盗门的开关和上锁开锁的功能:

public class Test {
/* 测试类 */
	
	public static void main (String[] args) {
		
		TheftproofDoor tfd = new TheftproofDoor();
		
		tfd.close ();	// 关门
		tfd.lockUp ();	// 上锁
		tfd.openLock ();	// 开锁
		tfd.open ();	// 开门
	}
}

        运行测试:

C:\Users\byme\javaTest>javac Test.java

C:\Users\byme\javaTest>java Test
轻轻拉,门关上了……
插进钥匙,向左旋转钥匙三圈,锁上了,拔出钥匙!
插进钥匙,向右旋转钥匙三圈,锁打开了,拔出钥匙..
用力推,门开了……

C:\Users\byme\javaTest>

%%%%%

扩展接口的能力

        现在我们应该可以理解接口表示一种能力

        一个类实现了某个接口 就表示这个类具备了某种能力

        其实生活中这样的例子还有很多 抽象来说:

如木匠、铁匠并不是某个接口而代表一种能力

招聘木匠、铁匠就是招聘具备该能力的人

        所以生活中一个人可以具有多项能力

        程序中一个类可以实现多个接口

        既然接口代表一种能力 那么我们现在就在打开、关闭防盗门的基础上扩展防盗门的功能

        例如 防盗门要增加一个门铃 主要功能是拍照存档

主人不在家 当有客人来访问时 只要来人轻按门铃便自动拍照存储

        现在为防盗门增加一种拍照能力:

interface DoorBell {
/* 门铃接口 */
	
	void takePictures ();	// 拍照存档服务
}

        然后在 TheftproofDoor 类中实现该接口能力 在测试类中测试功能:

abstract class Door {

	public abstract void open();
	public abstract void close();
}

interface Lock {

	void lockUp();
	void openLock();
}

class TheftproofDoor extends Door implements Lock, DoorBell {
/* 关联了两个接口 锁和门铃 */

	public void open() {
	
		System.out.println ( "用力推,门开了……" );
	}
	
	public void close() {
	
		System.out.println ( "轻轻拉,门关上了……" );
	}
	
	public void lockUp() {
		
		System.out.println ( "插进钥匙,向左旋转钥匙三圈,锁上了,拔出钥匙!" );
	}
	
	public void openLock () {
		
		System.out.println ( "插进钥匙,向右旋转钥匙三圈,锁打开了,拔出钥匙.." );
	}
	
	public void takePictures () {
	/* 实现拍照能力 */
		
		System.out.println ( "铃... 咔嚓... 照片已存储!" );
	}
}

public class Test {
	
	public static void main (String[] args) {
		
		TheftproofDoor tfd = new TheftproofDoor();
		
		tfd.close ();
		tfd.lockUp ();
		
		tfd.takePictures ();	// 有客人来按铃 自动拍照
		
		tfd.openLock ();
		tfd.open ();
	}
}

interface DoorBell {
/* 门铃接口 */
	
	void takePictures ();	// 拍照存档服务
}

        运行结果如下:

C:\Users\byme\javaTest>javac Test.java

C:\Users\byme\javaTest>java Test
轻轻拉,门关上了……
插进钥匙,向左旋转钥匙三圈,锁上了,拔出钥匙!
铃... 咔嚓... 照片已存储!
插进钥匙,向右旋转钥匙三圈,锁打开了,拔出钥匙..
用力推,门开了……

C:\Users\byme\javaTest>

%%%%%

使用接口的好处

        接口不仅弥补了 Java 只能单继承的缺点 还利于代码的扩展和维护

        接口类似于一个组件 需要时可以自由组装

        从使用角度来讲 接口和抽象类的区别在于

抽象类利于代码复用

接口利于代码维护

    在 Java API 中 可以发现很多接口名都是以
able
    为后缀的
    就是表示
可以做某件事
    例如
Serializable
Comparable
Iterable
    等

    在微软公司的 .NET 中也有很多接口名以
able
    后缀
    例如
IComparable
INullable
IClonable
    等 也表示同样的意思

##################################################

接口表示一种约定

——————————

使用接口实现打印机功能

        要求实现打印机打印功能

        打印机的墨盒可能是彩色的 也可能是黑白的

        所用的纸张可以有多种类型 例如 A4、B5 等

        并且墨盒和纸张都不是打印机厂商提供的

        打印机厂商如何避免自己的打印机与市场上的墨盒、纸张不符呢?

有效解决该问题的途径是制定墨盒、纸张的约定或者标准然后打印机厂商按照约定对墨盒、纸张提供支持

        无论最后使用的是哪个厂商提供的墨盒或纸张 只要符合统一的约定打印机都可以打印

Java 中的接口表示这样一种约定

        通过 Java 实现打印机的具体步骤如下:

定义墨盒接口 InkBox 约定墨盒的标准

定义纸张接口 Paper 约定纸张的标准

定义打印机类、引用墨盒接口、纸张接口实现打印功能

墨盒厂商按照 InkBox 接口实现 ColorInkBox类和 GrayInkBox 类

纸张厂商按照 Paper 接口实现 A4Paper 类和 B5Paper 类

组装打印机 让打印机通过不同墨盒和纸张实现打印功能

        InkBox.java code:

public interface InkBox {
/* 墨盒接口 */
	
	public String getColor ();	// 约定墨盒有颜色
}

        Paper.java code:

public interface Paper {
/* 纸张接口 */
	
	public String getSize ();	// 约定墨盒的大小
}

        Printer.java code:

public class Printer {
/* 打印机类 */

	InkBox inkBox;	// 引用墨盒接口
	Paper paper;	// 引用纸张接口
	
	public void setInkBox (InkBox inkBox) {
	/* 设置墨盒 */
		
		this.inkBox = inkBox;	// 获取墨盒颜色
	}
	
	public void setPaper (Paper paper) {
	/* 设置纸张 */
		
		this.paper = paper;	// 获取纸张大小
	}
	
	public void print () {
	/* 使用墨盒在纸张上打印 */
		
		System.out.printf ( "使用%s墨盒在%s纸张上开始打印..\n", inkBox.getColor (), paper.getSize () );
	}
}

        ColorInkBox.java code:

public class ColorInkBox implements InkBox {
/* 彩色墨盒类 实现墨盒接口 */
	
	public String getColor () {
	/* 实现获取墨盒颜色服务 */
	
		return "彩色";
	}
}

        GrayInkBox.java code:

public class GrayInkBox implements InkBox {
/* 黑白墨盒类 实现墨盒接口 */
	
	public String getColor () {
	/* 实现获取墨盒颜色能力 */
	
		return "黑白";
	}
}

        A4_Paper.java code:

public class A4_Paper implements Paper {
/* A4 纸类 实现墨盒纸张接口 */
	
	public String getSize() {
	/* 实现获取纸张大小服务 */
		
		return "A4";
	}
}

        B5_Paper.java code:

public class B5_Paper implements Paper {
/* B5 纸类 实现墨盒纸张接口 */
	
	public String getSize() {
	/* 实现获取纸张大小能力 */
		
		return "B5";
	}
}

        Test.java code:

public class Test {
/* 测试类 */

	public static void main ( String [] args ) {
	/* main 方法中组装打印机并通过不同墨盒纸张实现打印 */
		
		// 定义打印机
		InkBox inkBox = null;	// 引用 InkBox 墨盒接口对象 inkBox 对象值为空墨盒
		Paper paper = null;	// 引用 Paper 纸张接口对象 paper 对象值为空纸张
		Printer printer = new Printer();	// 实例化一个打印机类的 printer 打印机对象
		
		// 使用黑白墨盒在 A4 纸张上打印
		inkBox = new GrayInkBox();	// inkBox 墨盒接口对象的值为黑白墨盒类
		paper = new A4_Paper ();	// 纸张接口对象向上转换为 A4 纸张类型
		printer.setInkBox ( inkBox );	// 设置黑白墨盒
		printer.setPaper ( paper );	// 设置 A4 纸
		printer.print ();	// 开始打印
		
		// 使用彩色墨盒在 A4 纸张上打印
		inkBox = new ColorInkBox();	// 更改墨盒为彩色墨盒
		/* 纸张不变 */
		printer.setInkBox ( inkBox );	// 设置黑白墨盒
		printer.print ();	// 开始打印
		
		// 使用黑白墨盒在 B5 纸张上打印
		inkBox = new GrayInkBox();	// 更改墨盒为黑白墨盒
		paper = new B5_Paper ();	// 更改纸张为 B5 纸
		printer.setInkBox ( inkBox );	// 设置黑白墨盒
		printer.setPaper ( paper );	// 设置 B5 纸
		printer.print ();	// 开始打印
		
		// 使用彩色墨盒在 B5 纸张上打印
		inkBox = new ColorInkBox();	// 更改墨盒为彩色墨盒
		/* 纸张不变 */
		printer.setInkBox ( inkBox );	// 设置彩色墨盒
		printer.print ();	// 开始打印
	}
}

        cmd demo:

C:\Users\byme\javaTest>dir *.java
 驱动器 C 中的卷是 System_8.1
 卷的序列号是 B6F6-A064

 C:\Users\byme\javaTest 的目录

2022/06/22  10:33               179 A4_Paper.java
2022/06/22  10:33               179 B5_Paper.java
2022/06/22  10:28               152 ColorInkBox.java
2022/06/22  10:29               151 GrayInkBox.java
2022/06/22  10:16                94 InkBox.java
2022/06/22  10:18                92 Paper.java
2022/06/22  10:53               488 Printer.java
2022/06/22  10:55             1,342 Test.java
               8 个文件          2,677 字节
               0 个目录  6,756,528,128 可用字节

C:\Users\byme\javaTest>javac Test.java

C:\Users\byme\javaTest>dir *.class
 驱动器 C 中的卷是 System_8.1
 卷的序列号是 B6F6-A064

 C:\Users\byme\javaTest 的目录

2022/06/22  10:52               285 A4_Paper.class
2022/06/22  10:52               285 B5_Paper.class
2022/06/22  10:52               297 ColorInkBox.class
2022/06/22  10:52               295 GrayInkBox.class
2022/06/22  10:52               135 InkBox.class
2022/06/22  10:52               132 Paper.class
2022/06/22  10:53               801 Printer.class
2022/06/22  10:55               622 Test.class
               8 个文件          2,852 字节
               0 个目录  6,756,528,128 可用字节

C:\Users\byme\javaTest>java Test
使用黑白墨盒在A4纸张上开始打印..
使用彩色墨盒在A4纸张上开始打印..
使用黑白墨盒在B5纸张上开始打印..
使用彩色墨盒在B5纸张上开始打印..

C:\Users\byme\javaTest>

        此时我们理解了接口表示一种约定

        其实生活中这样的例子还有很多

例如两相电源插座中接头的形状、两个接头间的距离和两盒接头的电压都遵循统一的约定

        在变量对象编程中提倡面向接口编程而不是面向实现编程
 

##################################################

面向接口编程

——————————

何谓面向接口

         什么是面向接口编程呢?

    简单来说
如果打印机厂商只面向一家或几家厂商的墨盒产品规格生产打印机
    而没有一个统一的约定
就无法使用更多厂商的墨盒
    如果这家墨盒厂商倒闭了
那么打印机就无用武之地了
    这是因为她们彼此之间依赖性太强了
或者说耦合性太强了
    而如果按照统一的约定生产打印机和墨盒就不会存在这个问题了

    打印机类实现了面向接口编程的思想
    Printer/打印机 类的两个属性使用了 InkBox/墨盒 与 Paper/纸张 两个接口
    就可以接收实现了这两个接口的所有类的对象!
    即使是新推出的墨盒类型 只要遵守该接口的约定 就能够被接收!
而如果使用面向实现编程
则两个属性类型使用 GrayInkBox/黑白墨盒 和 B5_Paper/B5 纸 大大限制了打印机的使用范围……
无法对新推出的 ColorInkBox/彩色墨盒 提供支持
这也体现了多态的可扩展课维护的特性

——————————

面向接口编程的好处

        接口体现了约定和实现相分离的原则

        通过面向接口编程可以降低代码间的耦合性 提高代码的可扩展性和可维护性

        面向接口编程就意味着:

开发系统时

主体构架使用接口

接口构成系统的骨架

        这样就可以通过更换接口的类来实现更换系统!

    面向接口编程可以实现接口和实现的分离
    这样做最大的好处就是能够在客户端未知的情况下修改实现代码

    那么怎样抽象出接口呢?
一种是用在层和层之间的调用
    层层之间最忌讳耦合度过高或修改过于频繁!
    设计优秀的接口能够解决这个问题
另一种是使用在那些不稳定的部分上
    如果某些需求的变化性很大
    那么定义接口也是一种解决办法!
    设计良好的接口就像日常使用的万用插座
    不论插头如何变化都可以使用!

    最后强调一点:
良好的接口定义一定是来自于需求的绝对不是程序员绞尽脑汁想出来的

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

燃吹

呜呜呜没钱钱吃饭了……

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值