接口 | 我的日常分享

接口

接口

一、什么是接口

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

二、接口的语法

  • 接口相当于特殊的抽象类,定义方式、组成部分与抽象类类似。
1
2
3
4
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}

案例1:

Interface1.java

1
2
3
4
5
6
7
package demo1;
public interface Interface1{
//公开静态常量
public static final String NAME = "常量";
//公开抽象方法
public abstract void method();
}

Interface.java

1
2
3
4
5
6
7
8
9
package demo1;
//类实现接口
public class Interface implements Interface1{
//类实现某个接口必须重写其所有方法,否则必须为抽象类
@Override
public void method() {

}
}

TestInterface.java

1
2
3
4
5
6
7
package demo1;
public class TestInterface {
public static void main(String[] args) {
Interface1 i = new Interface();
i.method();
}
}

运行结果:

image-20220221144101448

接口有以下特性:

  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
  • 接口中每一个变量是静态常量,隐式以public static final修饰。
  • 接口中每一个方法也是隐式抽象的,隐式以public abstract修饰,声明时同样可省略abstract关键字。
  • 接口中的方法、变量都是public的。

案例2:接口中的变量、方法都会隐式修饰,public static finalpublic abstract可省略。

1
2
3
4
5
6
7
8
public interface Interface1{
//公开静态常量
// public static final String NAME = "常量";
String NAME = "常量";
//公开抽象方法
// public abstract void method();
void method();
}

案例3:接口是隐式抽象,隐式使用abstract修饰,与抽象类相似,无法创建对象

1
2
3
4
5
6
7
public class TestInterface {
public static void main(String[] args) {
new Interface1();
Interface1 i = new Interface();
i.method();
}
}

运行结果:

image-20220221145456574

image-20220221145521516

三、接口表示能力

  • 微观概念:接口是一种能力和约定。
    • 接口的定义:代表某种能力
    • 方法的定义:能力的具体要求
  • Java为单继承,当父类的方法种类无法满足子类需求时,可实现接口扩充子类能力。

案例1:让人具有飞的能力

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Person implements Flyable{
String name;
int age;

public Person() {
}

public Person(String name,int age) {
this.name = name;
this.age = age;
}

@Override
public void fly() {
System.out.println(name+"会飞了");
}
}

Flyable.java

1
2
3
4
5
//定义表示能力的接口后面一般加able
public interface Flyable {
//飞的能力
void fly();
}

TestPerson.java

1
2
3
4
5
6
7
8
9
public class TestPerson {
public static void main(String[] args) {
Person xiaoming = new Person("小明",18);
xiaoming.fly();
System.out.println("**********多态调用能力**********");
Flyable fly = xiaoming;
fly.fly();
}
}

运行结果:

image-20220221152518438

  • 接口支持多实现,可为类扩充多种能力。

案例2:让人具有飞和游的能力

Swimable.java

1
2
3
public interface Swimable {
void swim();
}

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Person implements Flyable,Swimable{
String name;
int age;

public Person() {
}

public Person(String name,int age) {
this.name = name;
this.age = age;
}

@Override
public void fly() {
System.out.println(name+"会飞了");
}

@Override
public void swim() {
System.out.println(name+"会游了");
}
}

TestPerson.java

1
2
3
4
5
6
7
8
9
10
11
12
public class TestPerson {
public static void main(String[] args) {
Person xiaoming = new Person("小明",18);
xiaoming.fly();
xiaoming.swim();
System.out.println("**********多态调用能力**********");
Flyable fly = xiaoming;
fly.fly();
Swimable swim = xiaoming;
swim.swim();
}
}

运行结果:

image-20220221153306708

四、接口的规范

  • 任何类在实现接口时,必须实现接口中所有的抽象方法,否则此类为抽象类。
  • 实现接口中的抽象方法时,访问修饰符必须为public
  • 一个类可以实现多个接口
  • 一个接口可以继承另一个接口,但是不能继承类。

五、接口的多态

接口引用:

  • 同父类一样,接口也可声明为引用,并指向实现类对象。
  • 注意:
    • 向上转型后,仅可调用接口中所声明的方法,不可调用实现类中独有的方法。
    • 如需使用实现类中的独有方法,可向下转型,强转回实现类本身类型。

Animal.java

1
2
3
4
5
6
7
8
public class Animal {
public void eat(){
System.out.println("Animal eat");
}
public void sleep(){
System.out.println("Animal sleep");
}
}

Runnable.java

1
2
3
public interface Runnable {
void run();//接口方法
}

Swimmable.java

1
2
3
public interface Swimmable {
void swim();//接口方法
}

TestPolymorpfic.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class TestPolymorpfic {
public static void main(String[] args) {
Dog wangcai = new Dog();
wangcai.sleep();
wangcai.run();
wangcai.eat();
wangcai.shoot();
wangcai.swim();
System.out.println("*******接口的多态*********");
Animal a = wangcai;
a.eat();
a.sleep();
Runnable r = wangcai;
r.run();
Swimmable s = wangcai;
s.swim();
/**
* 多种不同类型的引用指向同一个对象时,表示看待对象的视角不同。
* Dog wangcai = new Dog(); 将狗当狗看
* Animal a = wangcai; 将狗当动物看
* Runnable r = wangcai; 将狗当会跑的东西看
* Swimmable s = wangcai; 将狗当会游的东西看
* 不同引用所能看到的对象范围不同,只能调用自身类型中所声明的部分。
*/
}
}

运行结果:

image-20220226103258580

六、常见关系

  • 类与类
    • 单继承:extends 父类名称
  • 类与接口
    • 多实现:implements 接口名称1,接口名称2,接口名称3,...
  • 接口与接口
    • 多继承:extends 父接口1,父接口2,父接口3,...

七、常量接口与标记接口

  • 常量接口:将多个常用于表示状态或固定值的变量以静态常量的形式定义在接口中统一管理,提高代码的可读性。

Interface.java

1
2
3
4
public interface Interface {
String CONST1 = "aaa";
String CONST2 = "bbb";
}

TestInterface.java

1
2
3
4
5
6
public interface TestInterface {
public static void main(String[] args) {
System.out.println(Interface.CONST1);
System.out.println(Interface.CONST2);
}
}

运行结果:

image-20220226104634913

  • 标记接口:标记接口中没有包含任意成员,仅仅用作标记,SerializableCloneable

InterfaceC.java表示该对象可克隆

1
2
3
public interface InterfaceC extends Cloneable{

}

InterfaceS.java表示该对象可序列化

1
2
3
4
import java.io.Serializable;

public interface InterfaceS extends Serializable {
}

再来看看SerializableCloneable这两个接口中的内容,其实它们没有任何内容,仅仅用于标记。

image-20220226105213509

image-20220226105241063

标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。

标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。

例如:java.awt.event包中的MouseListener接口继承的 java.util.EventListener接口定义如下:

1
2
3
4
5
package java.util;
public interface EventListener
{

}

没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:

  • 建立一个公共的父接口:

    正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。

  • 向一个类添加数据类型:

    这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。

八、接口表示标准

接口表示一种标准,现实中比如耳机、充电器接口,必须要有一个标准,各大厂商按照这个标准进行生产,各种耳机、充电器才能够通用。

案例:Usb案例,各种外设遵循Usb接口,电脑才能够通用。接口回调。

Usb.java

Usb接口定义标准

1
2
3
public interface Usb {
void service();
}

Fan.java

风扇,按照Usb接口生产

1
2
3
4
5
6
7
public class Fan implements Usb{

@Override
public void service() {
System.out.println("风扇开始工作了");
}
}

Keyboard.java

键盘,按照Usb接口生产

1
2
3
4
5
6
public class Keyboard implements Usb{
@Override
public void service() {
System.out.println("键盘开始工作了");
}
}

Mouse.java

鼠标,按照Usb接口生产。

1
2
3
4
5
6
public class Mouse implements Usb{
@Override
public void service() {
System.out.println("鼠标开始工作了");
}
}

Compute.java

电脑,定义了三个Usb接口标准的USB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Compute {
/**
* 电脑有3个USB口
*/
Usb usb1;
Usb usb2;
Usb usb3;

//电脑开始工作
public void run(){
System.out.println("电脑开始工作");
if(usb1 != null){
usb1.service();
}
if(usb2 != null){
usb2.service();
}
if(usb3 != null){
usb3.service();
}
}
}

TestCompute.java

1
2
3
4
5
6
7
8
9
10
11
12
public class TestCompute {
public static void main(String[] args) {
Compute mac = new Compute();
Usb mouse = new Mouse();//多态
Usb fan = new Fan();//多态
Keyboard keyboard = new Keyboard();
mac.usb1 = mouse;
mac.usb2 = fan;
mac.usb3 = keyboard;//向上转型
mac.run();
}
}

运行结果:

image-20220226111937678

什么是接口回调:先有接口的使用者,后有接口的实现者。即上面案例,即先有电脑,后有鼠标、键盘等外设。

九、接口总结

9.1 与抽象类的异同

  • 相同:

    • 可编译成字节码文件。
    • 不能创建对象。
    • 可作为引用类型。
    • 具备Object类中所定义的方法。
  • 不同

    • 所有属性都是公开静态常量,隐式使用public static final修饰
    • 所有方法都是公开抽象方法,隐式使用public abstract修饰
    • 没有构造方法、动态代码块、静态代码块
  • 1、抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  • 2、抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  • 3、接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  • 4、一个类只能继承一个抽象类,而一个类却可以实现多个接口。

9.2 接口与类的区别:

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承。

:JDK 1.8 以后,接口里可以有静态方法和方法体了。

:JDK 1.8 以后,接口允许包含具体实现的方法,该方法称为”默认方法”,默认方法使用 default 关键字修饰。可参考 Java 8 默认方法

:JDK 1.9 以后,允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。可参考 Java 9 私有接口方法