继承与接口
1. 接口
接口与类类似,使用关键字interface来定义,分为接口和接口体
1.1 接口的声明
interface 接口名
,例如:
interface A{
final int MAX = 100;
void add();
float sum(float x,float y);
}
1.2 接口体
- 接口体中常包含声明和抽象方法两部分,接口体中只有抽象方法,没有普通方法,而且接口体中所有的常量的访问权限一定都是public,而且都是static常量(允许省略public,final,static修饰符),所有的抽象方法的权限一定都是public(允许省略public abstract修饰符)
public static final int MAX=100
等价于int MAX = 100;
public abstract void add();
等价于void add();
2 接口的实现
2.1 类实现接口
- 类通过implement声明该类来实现一个或多个接口(!注意这里接口是可以多继承的,而类是不可以多继承的),如果实现多个接口,用逗号隔开接口名
- 例如:
class A implement Printable,Addable
2.2 重写接口中的方法
- 如果一个非抽象类实现了某个接口,那么这个类必须重写这个接口中的所有方法,由于接口中的方法都是public abstract类型的,所以在重写的时候要去掉abstract修饰符并给出方法体,而且访问权限也必须要明显的用public来修饰(实现接口的时候方法体的public不可以省略)。
- 接口的非抽象类的实现过程也就是将接口中的行为具体化的一个过程
- 如果一个类声明实现一个接口,但是没有重写接口中的所有方法,那么这个类必须是抽象类
例如:
public interface Computable{
int MAX = 46;
int f(int x);
}
public class China implements Computable{
int number;
public int f(int x){
int sum = 0;
for(int i = 1; i < x; i++) {
sum += i;
}
}
}
2.3 接口的细节实现
- 程序可以用接口名访问接口中的常量,但是如果一个类实现了接口,那么该类可以直接在类体中使用该接口中的常量。
- 定义接口时,如果关键字interface前面加上public关键字,就称这样的接口是一个public接口,则可以被任何一个类实现,如果一个接口不加public修饰,就称作友好接口,友好接口可以被与该接口在同一包中的类实现。
- 如果父类实现了某个接口,那么子类便也自然实现了该接口,便不必显式地使用关键字implement声明实现了这个接口
- 接口也可以被继承,接口可以通过extends关键字声明一个接口是另一个接口地子接口。由于接口中的方法和常量都是public,子接口将继承父接口中的全部方法和常量
- 用import引入一个包的时候同时引入了一个包中的接口和类:
import java.io.*;
3. 接口的UML图
//图来自https://blog.csdn.net/weixin_42762089/article/details/100074129?utm_source=app&app_version=4.10.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
4. 接口回调
- 类和接口都是java中一种重要的数据类型,接口声明的对象称作接口变量。接口属于引用型变量,这里我们可以将其视为一个容器,容器里面存放的是实现该接口的类的实例的引用,也就是用实现该接口的类创建的对象的引用。
- 接口回调是指:可以把实现某一接口的类的创建的对象的引用赋值给该接口声明的接口的变量,那么该接口变量就可以调用被类实现的接口方法。实际上,当接口变量调用被实现的接口方法时,就是通知相应的对象调用这个方法。
- 例如:
public interface Com{
int f(int a);
}
public class ImpleCom implements Com{
public int f(int a){
return a+a;
}
}
//接口回调的实现部分代码截取:
Com com;
ImpleCom object = new ImpleCom();
com = object;
//这里将类的一个实体对象赋值给了一个接口声明的变量,于是这个变量com就可以存放这个对象的引用,
//也就是通过com.f(a)可以实现实体类object(也就是ImpleCom)中的f(int a)方法了。实际上com所在的位置存放的是object的引用
例如:
interface ShowMessage{
void 显示商标(String s);
}
class TV implements ShowMessage{
public void 显示商标(String s){
System.out.println(s);
}
}
class PC implements ShowMessage{
public void 显示商标(String s){
System.out.println(s);
}
}
public class Example6_2{
public static void main(String args[]){
ShowMessage sm;
sm = new TV();
sm.显示商标("长城牌电视机");
sm = new PC();
sm.显示商标("联想奔月5008PC机")
}
}
//此实例是课本中的实例,不建议大家按照这种方式来命名,平时要遵守命名规范
5. 理解接口
- 大家可以发现,接口似乎能够做到的事情基本上都可以通过类来实现,似乎感受不到接口到底有什么用处,那么接口到底有什么用处呢?
- 接口的作用:
- 接口可以抽象出重要的标准行为,该标准行为用抽象方法来表示
- 可以把实现接口的类的对象的引用赋值给接口变量,该接口变量可以调用被该类实现的接口方法,即体现该类根据接口里的行为标准给出具体行为
由上可得,接口就是定义出来的一套标准。像我们生活中的其他标准一样,举个例子,假如某公司想开发几款不同的新手机,然后要给手机做一个充电插孔,那么插孔的形状(也就是物理特性)等之类的就要保持一致,不能够我今天开发的手机用的typec的插口,明天开发出来的手机我突发奇想想做一个typecplus的插口,每天一个新的形状插口,这样就会导致十分混乱
- 接口的应用场景:
- 接口的思想在于它可以要求某些类有相同的名称的方法,但是方法的具体内容可以不同。接口在要求一些类有相同名称的方法的同时,并不强迫这些类有相同的父类。
- 对类是否有某个功能非常关心,但是不关心功能的具体实现
//刹车
abstract class MotorVehicles {
abstract void brake();
}
//收费
interface MoneyFare {
void charge();
}
//空调
interface ControlTemperature {
void controlAirTemperature();
}
//公交车
class Bus extends MotorVehicles implements MoneyFare {
void brake() {
System.out.println("公共汽车使用毂式刹车技术");
}
public void charge() {
System.out.println("公共汽车:一元/张,不计算公里数");
}
}
//出租车
class Taxi extends MotorVehicles implements MoneyFare,ControlTemperature {
void brake() {
System.out.println("出租车使用盘式刹车技术");
}
public void charge() {
System.out.println("出租车:2元/公里,起价3公里");
}
public void controlAirTemperature() {
System.out.println("出租车安装了Hair空调");
}
}
//电影院
class Cinema implements MoneyFare,ControlTemperature {
public void charge() {
System.out.println("电影院:门票,十元/张");
}
public void controlAirTemperature() {
System.out.println("电影院安装了中央空调");
}
}
public class Example6_3 {
public static void main(String args[]) {
Bus bus101 = new Bus();
Taxi buleTaxi = new Taxi();
Cinema redStarCinema = new Cinema();
MoneyFare fare;
ControlTemperature temperature;
fare = bus101;
bus101.brake();
fare.charge();
fare = buleTaxi;
temperature = buleTaxi;
buleTaxi.brake();
fare.charge();
temperature.controlAirTemperature();
fare = redStarCinema;
temperature = redStarCinema;
fare.charge();
temperature.controlAirTemperature();
}
}
6. 接口与多态
- 由接口产生的多态就是指不同和的类在实现同一个接口时可能具有的不同的实现方式,那么接口变量在回调接口方法时就可能具有多种形态
interface CompurerAverage {
public double average(double a,double b);
}
class A implements CompurerAverage {
public double average(double a,double b) {
double aver=0;
aver=(a+b)/2;
return aver;
}
}
class B implements CompurerAverage {
public double average(double a,double b) {
double aver=0;
aver=Math.sqrt(a*b);
return aver;
}
}
public class Example6_4 {
public static void main(String args[]) {
CompurerAverage computer;
double a=11.23,b=22.78;
computer = new A();
double result = computer.average(a,b);
System.out.printf("%5.2f和%5.2f的算术平均值:%5.2f\n",a,b,result);
computer = new B();
result= computer.average(a,b);
System.out.printf("%5.2f和%5.2f的几何平均值:%5.2f",a,b,result);
}
}
7. 接口参数
- 如果一个方法的参数是接口类型,我们可以将任何实现改类的接口的实例的引用传递给该接口的参数,那么接口参数就可以回调类实现的接口方法
interface SpeakHello {
void speakHello();
}
class Chinese implements SpeakHello {
public void speakHello() {
System.out.println("中国人习惯问候语:你好,吃饭了吗? ");
}
}
class English implements SpeakHello {
public void speakHello() {
System.out.println("英国人习惯问候语:你好,天气不错 ");
}
}
class KindHello {
public void lookHello(SpeakHello hello) { //接口类型参数
hello.speakHello(); //接口回调
}
}
public class Example6_5 {
public static void main(String args[]) {
KindHello kindHello=new KindHello();
kindHello.lookHello(new Chinese());
kindHello.lookHello(new English());
}
}
8. abstract类与接口的比较
- abstract类和接口都可以有abstract方法
- 接口中只可以有常量,不能有变量;而abstract类中既可以有常量,又可以有变量
- abstract类中也可以有非abstract方法,接口中只能有抽象方法
9. 面向接口编程
- 接口只关心操作,但不关心这些操作的具体细节,可以使我们把主要精力放到程序设计上,而不必拘泥于细节的实现。也就是可以通过接口中声明若干个abstract方法,表明这些方法的重要性,方法体的细节内容由实现接口的类去完成。
- 使用接口进行程序设计的核心思想使使用接口回调,即接口变量存放该接口的类的对象的引用,从而接口变量就可以回调实现的接口方法了。