6.1 接口
使用关键字
interface来定义一个接口,接口的定义和类十分相似,分为接口声明和接口体
interface Runnable{void run();}
接口声明
定义一个接口包含接口声明和接口体,和类不同的是,定义接口需要使用interface关键字来声明自己是一个接口。
interface 接口名字{}
接口体
接口体包含常量声明(接口没有变量) 和抽象方法两部分。接口体中
只有抽象方法,没有普通方法。
接口体中所有常量访问权限一定都是public,并且是static常量,所有抽象方法访问权限一定都是public允许省略public abstract修饰符
总结下
- 接口没有变量,只有常量
public static final - 接口只有抽象方法,没有普通方法
public abstract - 前缀可以省略
public interface Runnable {public static final String tag = "default";public abstract void run();}
省略后
public interface Runnable {String tag = "default";void run();}
6.2 实现接口
Java语言中,接口由类来实现以便使用接口中的方法,一个类需要在类声明中使用关键字
implements声明该类实现一个或者多个接口(如果多个接口,接口之间用,分隔开)
class Cat extends Animal implements Eatable,Sleepable{}
猫继承自动物,实现了,吃和睡这两个接口。
重写接口中的方法
如果一个类实现了某个接口,那么这个类必须重写这个接口中的
所有方法
这里有个小细节,我们知道接口中的方法是 public abstract 方法,那么在我们重写时需要注意以下两点
- 去掉
abstract - 添加方法体,且方法一定是 public 访问权限,否则就降低了 访问权限,这样是不允许的
用户也可以自定义接口,一个Java源文件由**类**和**接口**组成。
interface Eatable {String tag = "animal";void eat();}class Cat implements Eatable {public void eat() {// 直接访问接口中的常量System.out.println(tag);System.out.println("小猫会吃");}}class Test {public static void main(String[] args) {Cat cat = new Cat();cat.eat();}}
animal小猫会吃
如果一个类实现了一个接口,但是没有重写接口中的所有方法,那么这个类必须是**抽象类**
抽象类既可以重写接口中的方法,也可直接拥有接口中的方法
interface Eatable {String tag = "animal";void eat();}abstract class Cats implements Eatable {}
接口的细节说明
程序可以用接口名访问接口中的常量,但是如果一个类实现了接口,那么该类可以在类体中直接使用该接口中的常量。
interface Eatable {String tag = "animal";void eat();}class Cat implements Eatable {@Overridepublic void eat() {// 通过接口名访问常量System.out.println(Runnable.tag);// 类内直接访问System.out.println(tag);}}
- 如果 interface 前面加了 public 关键字,就这样的接口是一个
public 接口,可以被任意一个类实现 - 不加 public 的接口是一个友好的接口,只能被同一个包内中的类实现
- 如果父类实现了某个接口,子类自然也就实现了该接口,不必显式地使用关键字 implements 实现这个接口
- 接口可以被继承,通过关键字 extends 继承另一个接口 (很少这样做)
6.3 接口的UML图
- 顶部是名字,接口名字必须是
斜体字需要使用<<interface>>修饰名字 - 第二层是常量层,格式 常量名:类型
- 第三层是方法层,格式 方法名(参数列表):返回值
6.4 接口回调
把实现某一类接口的类创建的对象的引用传递给该接口声明的变量,然后调用实现类的方法,这个过程称之为
接口的回调
这里和前面的上转型对象十分相似,但是叫法不同
interface Eatable {String tag = "animal";void eat();}class Cat implements Eatable {@Overridepublic void eat() {System.out.println("猫食肉");}}class Rabbit implements Eatable{@Overridepublic void eat() {System.out.println("兔子食草");}}class Test{public static void main(String[] args) {Eatable cat = new Cat();cat.eat(); // 接口的回调}}
6.5 理解接口
接口的语法规则很容易记住,但是真正的理解接口更重要。
理解接口的关键
- 接口可以抽象出重要的行为标准,该行为标准用抽象方法表示。
- 可以把接口实现类的对象引用赋值为接口变量
6.6 接口与多态
上面我们知道了接口回调的概念,通过接口回调我们就可以实现多态。
不同的类,实现了同一接口,就具备了不同的实现方式,那么在接口回调方法时,就具备了多种形态。
interface Eatable {String tag = "animal";void eat();}class Cat implements Eatable{@Overridepublic void eat() {System.out.println("猫吃肉");}}class Rabbit implements Eatable{@Overridepublic void eat() {System.out.println("兔子吃草");}}class Animal{public static void eat(Eatable eatable){eatable.eat();}}class Main{public static void main(String[] args) {Animal.eat(new Cat());Animal.eat(new Rabbit());}}
6.7 abstract类与接口的比较
- abstract 类可接口都可以有 abstract方法
- 接口中只能有常量,不能有变量,抽象类可以有变量,也可以有常量
- 抽象类中可以有非抽象方法,但是接口不可以
6.8 使用抽象类还是接口?
- 如果某个问题需要通过继承解决,也就是说需要从写父类方法时,还要继承一些父类的特性,那么我们就应该使用抽象类。
- 否则就使用接口
为什么有了抽象类还要有接口?
- 因为Java是单继承语言,抽象类抽象颗粒细度难以把控,所以引入了接口。
- 接口是更高级别的抽象,通过多接口实现可以更好的把控颗粒细度。
举个栗子
public interface Flyable {void fly();}interface Swimmable{void swim();}class Brid implements Flyable{@Overridepublic void fly() {System.out.println("小鸟会飞");}}class Fish implements Swimmable{@Overridepublic void swim() {System.out.println("鱼会游泳");}}
6.9 面向接口编程
现在学了接口来对前面的 笔芯 案例 改造下,同样要满足 ”开闭原则“
class Refill{private String color;public Refill(String color) {this.color = color;}public void w(){System.out.println("我能书写"+color);}}interface Writable {void write(Refill refill);}public class Pen implements Writable{@Overridepublic void write(Refill refill) {refill.w();}}class Test{public static void main(String[] args) {Pen pen = new Pen();pen.write(new Refill("红色"));pen.write(new Refill("蓝色"));}}
我能书写红色我能书写蓝色
6.10 小结
- 接口的接口体中可以有常量和abstract方法
- 和类一样,接口也是Java中重要的引用数据类型,接口中只能存放实现接口类的实例对象引用
- 当接口存放了实现类的对象引用时,接口调用类实现写接口方法称之为接口回调
- 借助接口回调可以实现多态性
- 在设计多态程序时,要熟练使用回调技术以及面向接口编程的思想,以实现”开闭原则”
