17、Java基础---接口

本文详细介绍了Java中的接口概念,包括接口的声明、实现、限制及多个接口的实现。接口作为类的设计蓝图,用于定义行为规范,比如播放器的"播放"和"停止"操作。接口中的方法默认为public和abstract,实现接口的类必须实现所有方法。同时,接口可以持有常量,并可以被多个类实现,实现多态性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接口

一、接口

接口是引用类型的一种,与类相似但也存在诸多不同;如果将类比作“电路的设计图”,那么接口就是“遥控器的设计图”

接口声明

这里以视频播放器、CD播放器、DVD播放器等播放器(播放设备)为例进行讲解,所有的播放器都可以执行“播放” 和“停止” 等操作;虽然播放器的实际运行各不相同, 但遥控器有"播放按钮” 和“停止按钮” 这一点是共通的。

共通部分的遥控器如图a,将"Player遥控器由play和stop两个按钮组成“ 这一遥控器的设计图表示为程序, 就是图b所示的接口声明,与类声明相似但开头的关键字并不是class,而是interface, 这一点与类有所不同

接口中的所有方法都为public且abstract,必须用 ';'来替换方法体{}进行声明, 这一点与类中的抽象方法相同

接口的实现

接口中声明的抽象方法的主体要在在实现该接口的类中定义

实现接口Player的类VideoPlayer的声明如图a所示,implements Player部分表示接口Player的实现。这个声明和派生类的声明相似,不过使用的关键字并不是extends, 而是implements

来理解类VideoPlayer的声明:该类会实现Player遥控器,为此需要实现各个按钮所调用的方法主体,它们之间的关系如图b所示

类VideoPlayer在实现接口Player的同时也会实现play和stop这两个方法(重写方法并定义主体);
重写的方法必须声明为public, 这是因为接口的方法为public, 已无法再强化其访问控制,这与类的派生中的重写是一样

【接口中的方法为public且abstract。在实现该接口的类中, 各方法在实现时需要加上public修饰符】

创建视频播放器VideoPlayer和CD播放器CDPlayer,以实现接口Player,程序如下:

// 播放器 接口
public interface Player {
	void play();			// ○播放
	void stop();			// ○停止
}
//===== 视频播放器 =====//
public class VideoPlayer implements Player {
	private int id;					// 制造编号
	private static int count = 0;	// 到目前为止已经赋的制造编号
	public VideoPlayer() {									// 构造函数
		id = ++count;
	}
	public void play() {									// ○播放
		System.out.println("■视频播放开始!"); 
	}
	public void stop() {									// ○停止
		System.out.println("■视频播放结束!"); 
	}
	public void printInfo() {								// 显示制造编号
		System.out.println("该机器的制造编号为[" + id + "]。"); 
	}
}
//===== CD播放器 =====//
public class CDPlayer implements Player {

	public void play() {						// ○播放
		System.out.println("□CD播放开始!"); 
	}

	public void stop() {						// ○停止
		System.out.println("□CD播放结束!"); 
	}

	public void cleaning() {				   // 清洗
		System.out.println("□已清洗磁头。"); 
	}
}

这些接口和类如图所示,VideoPlayer 遥控器和CDPlayer遥控器都包含Player 遥控器上的play和stop 按钮。
理解一下”类VideoPlayer和CDPlayer 如何实现了Player"

类VideoPlayer
方法play显示"■视频播放开始!",方法stop显示"■视频播放结束I";
这个类的实例在创建时会被赋上制造编号1、2、3……赋给各个实例的制造编号为实例变员id. 表示已经赋到哪一号的是类变量count;方法printinfo负责显示制造编号

类CDPlayer
这个类中并未定义宇段;方法play显示“口CD播放开始!",方法stop显示“口CD播放结束!";方法cleaning显示“ 口已清洗磁头。”
这两个类并未继承接口Player中的字段和方法等资产,只继承了Player的方法规格(遥控器上的按钮规格)

二、接口语法规则和限制

1)无法创建接口类型的实例
接口相当于遥控器(而不是电路)的设计图,因此无法创建电路主体(实例),下面的声明会发生错误

Player c = new Player();	//错误

2)接口类型的变量可以引用实现该接口的类的实例
接口类型的变量可以引用实现该接口的类的实例,下面的操作可以正常执行:

Player pl = new CDPlayer(); 
Player p2 = new VideoPlayer();

Player遥控器的变量可以引用实现它的类CDPlayer和类VideoPlayer的实例【接口类型的变量可以引用实现类的实例】

// 接口Player的使用示例
class PlayerTester {

	public static void main(String[] args) {
		Player[] a = new Player[2];
		a[0] = new VideoPlayer();	// 视频播放器
		a[1] = new CDPlayer();		// CD播放器

		for (Player p : a) {
			p.play();				// 播放
			p.stop();				// 停止
			System.out.println();
		}
	}
}

输出:

数组a是一个元素类型为接口Player类型的数组,a[0]引用VideoPlayer的实例,a[1]引用CDPlayer的实例。扩展for语句中依次对各个元素调用方法play和方法stop如图,从中可以看出接口Player类型的遥控器只有play和stop按钮。因此, 通过a[0]和a[1] 无法调用printinfo或者cleaning方法。

3)接口实现时必须实现所有的方法

实现接口就是实现遥控器上各个按钮的功能,未实现接口中所有方法的类必须声明为抽象类

4)可以持有常量

接口可以持有下述成员:

1、类

2、接口

3、常量【Public、static、final 字段】,但不可以持非作常量字段【可以读写数值的变量】

4、抽样方法【public、abstract 方法】

持有常量的接口示例如下,接口Skinnable的名称表示“可换肤的“:

package com.example;// 换肤接口

public interface Skinnable {
	int BLACK = 0;			// 黑色
	int RED = 1;			// 红色
	int GREEN = 2;			// 绿色
	int BLUE = 3;			// 蓝色
	int LEOPARD = 4;		// 豹纹
	void changeSkin(int skin);			// ★换肤
}

换肤时指定的颜色和花纹都声明为了常量,接口中的字段都声明为public且static且final。它们并不是类中所说的“实例变量”而是"类变量” 。【接口中声明的字段为public且static且final, 即为不可以改写数值的类变量】

声明中带有static的类变量可以通过“类名 .字段名“ 进行访问,接口中的常量可以通过“接口名.字段名“ 进行访问;在本示例中,黑色可以通过Skinnable . BLACK进行访问,豹纹可以通过Skinnable.LEOPARD进行访问

5)命名方法与类相同

原则上,接口的名称为名词,但也可以使用表示动作的形容词。特别是表示“ 可.... 的"之意的接口名称可以使用-able

6)接口的访问属性与类相同
虽然接口中的所有成员都自动设为public, 但接口本身的访问属性与类相同, 可以任意指定;如果加上public, 则为公开访问,而如果未加上public, 则为包访问

三、类的派生和接口的实现

在新创建类时,可以同时执行类的派生和接口的实现。

package com.example;
//===== 二维接口 =====//
public interface Plane2D {
	int getArea();			// ○计算面积
}

该接口中声明的getArea方法用来计算面积并返回结果,由于点和直线没有面积,因此这些类中无需实现该接口,下面在长方形类Rectangle中进行实现,再来新创建一个平行四边形类Parallelogram,实现接口Plane2D

package com.example.shape3;

/**
 * 类Rectangle是表示长方形的类。
 * 该类派生自表示图形的抽象类Shape。
 * @see		Shape
*/
public class Rectangle extends Shape implements Plane2D {

	/**
	 *	表示长方形的长的int型字段。
	*/
	private int width;

	/**
	 *	表示长方形的宽的int型字段。
	*/
	private int height;

	/**
	 *	创建长方形的构造函数。
	 *	接收长和宽作为参数。
     * @param width  长方形的长。
     * @param height 长方形的宽。
	*/
	public Rectangle(int width, int height) {
		this.width = width;
		this.height = height;
	}

	/**
	 * 方法toString返回表示与长方形相关的图形信息的字符串。
	 * @return 返回字符串"Rectangle(width:4, height:3)"。
	 *			4和3这两部分分别对应长和宽的值。
	*/
	public String toString() {
		return "Rectangle(width:" + width + ", height:" + height + ")"; 
	}

	/**
	 * 方法draw用于绘制长方形。
     * 通过排列星号'*'进行绘图。
	 * 循环width次显示长度个数的'*'并换行。
	*/
	public void draw() {
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= width; j++)
				System.out.print('*'); 
			System.out.println(); 
		}
	}

	public int getArea() { return width * height; }		// ○计算面积
}

Parallelogram

package com.example.shape3;
//===== 平行四边形 =====//
public class Parallelogram extends Shape implements Plane2D {
	private int width;			// 底边长
	private int height;			// 宽
	public Parallelogram(int width, int height) {
		this.width = width;	this.height = height;
	}
	public String toString() {							// 字符串表示 
		return "Parallelogram(width:" + width + ", height:" + height + ")";
	}
	public void draw() {								// 绘图
		for (int i = 1; i <= height; i++) {
			for (int j = 1; j <= height - i; j++) System.out.print(' '); 
			for (int j = 1; j <= width; j++) System.out.print('#'); 
			System.out.println(); 
		}
	}
	public int getArea() { return width * height; }		// ○计算面积
}

类和接口的关系如图,Rectangle和Parallelogram 派生自类Shape, 并实现了接口Plane2D

要点1:当类的声明中同时存在extends和implements时, 一定要先书写extends

把包含Shape的类群比作是根据血缘关系而结成的"shape家族”,而Plane2D和实现它的类群之间并不具有血缘关系, 而是属于同一个圈子, 故而可以比作朋友关系,无论是Shape家族,还是与其毫无关系的类,如果希望属于Plane2D圈子,只要实现Plane2D就可以了【接口就是将类划分为朋友关系之类的分组,朋友关系可以与具有血缘关系的派生毫无关系】

四、多个接口的实现

类的派生和接口的实现之间最大的不同在于是否可以同时派生/实现多个类,类的派生只允许单继承, 而一个类可以实现多个接口,一般的形式如下所示,当声明类时,implements后面是要实现的接口,接口之间使用逗号进行分隔。类A中会实现接口B和接口c中的所有方法:

class A implements B,C{
	//实现接口B的方法
	//实现接口C的方法
} 

下面所示的程序中同时实现了接口Player和接口Skinnable:

package com.example.player;// 可换肤的随身播放器

class PortablePlayer implements Player, Skinnable {
	private int skin = BLACK;

	public PortablePlayer() { }							// 构造函数

	public void play() {								// ○播放
		System.out.println("◆播放开始!"); 
	}

	public void stop() {								// ○停止
		System.out.println("◆播放结束!"); 
	}

	public void changeSkin(int skin) {					// ★换肤
		System.out.print("皮肤换成了");
		switch (skin) {
		 case BLACK:   System.out.print("乌黑");	break;
		 case RED:     System.out.print("深红");	break;
		 case GREEN:   System.out.print("柳叶");	break;
		 case BLUE:    System.out.print("露草");	break;
		 case LEOPARD: System.out.print("豹纹");	break;
		 default:	   System.out.print("素色");	break;
		}
		System.out.println("。");
	}	
}

类PortablePlayer为 “可换肤的随身播放器” ,接口和类之间的关系如图 :

类PortablePlayer中实现了接口Player的方法play和stop, 以及接口Skinnable的方法changeSkin 。在类中,可以使用简名来访问实现的接口中的字段,类PortablePlayer的使用示例如下:
 

package com.example.player;// 类PortablePlayer的使用示例

class PortablePlayerTester {

	public static void main(String[] args) {
		PortablePlayer a = new PortablePlayer();
		a.play();								// 播放
		a.stop();								// 停止
		a.changeSkin(Skinnable.LEOPARD);		// 将皮肤换成豹纹
	}
}

输出:

由于接口Skinnable中定义的常量为类变量(静态字段),因此可以使用"接口名.字段名"进行访问,本程序中选择的Skinnable.LEOPARD为豹纹

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值