6.1 接口

使用关键字 interface 来定义一个接口,接口的定义和类十分相似,分为接口声明和接口体

  1. interface Runnable{
  2. void run();
  3. }

接口声明
定义一个接口包含接口声明和接口体,和类不同的是,定义接口需要使用interface关键字来声明自己是一个接口。

  1. interface 接口名字{
  2. }

接口体

接口体包含常量声明(接口没有变量) 和抽象方法两部分。接口体中只有抽象方法没有普通方法
接口体中所有常量访问权限一定都是public,并且是 static常量,所有抽象方法访问权限一定都是 public 允许省略 public abstract 修饰符

总结下

  • 接口没有变量,只有常量 public static final
  • 接口只有抽象方法,没有普通方法 public abstract
  • 前缀可以省略
  1. public interface Runnable {
  2. public static final String tag = "default";
  3. public abstract void run();
  4. }

省略后

  1. public interface Runnable {
  2. String tag = "default";
  3. void run();
  4. }

6.2 实现接口

Java语言中,接口由类来实现以便使用接口中的方法,一个类需要在类声明中使用关键字 implements 声明该类实现一个或者多个接口(如果多个接口,接口之间用,分隔开)

  1. class Cat extends Animal implements Eatable,Sleepable{
  2. }

猫继承自动物,实现了,吃和睡这两个接口。

重写接口中的方法

如果一个类实现了某个接口,那么这个类必须重写这个接口中的所有方法

这里有个小细节,我们知道接口中的方法是 public abstract 方法,那么在我们重写时需要注意以下两点

  • 去掉abstract
  • 添加方法体,且方法一定是 public 访问权限,否则就降低了 访问权限,这样是不允许的

用户也可以自定义接口,一个Java源文件由**类****接口**组成。

  1. interface Eatable {
  2. String tag = "animal";
  3. void eat();
  4. }
  5. class Cat implements Eatable {
  6. public void eat() {
  7. // 直接访问接口中的常量
  8. System.out.println(tag);
  9. System.out.println("小猫会吃");
  10. }
  11. }
  12. class Test {
  13. public static void main(String[] args) {
  14. Cat cat = new Cat();
  15. cat.eat();
  16. }
  17. }
  1. animal
  2. 小猫会吃

如果一个类实现了一个接口,但是没有重写接口中的所有方法,那么这个类必须是**抽象类**

抽象类既可以重写接口中的方法,也可直接拥有接口中的方法

  1. interface Eatable {
  2. String tag = "animal";
  3. void eat();
  4. }
  5. abstract class Cats implements Eatable {
  6. }

接口的细节说明

程序可以用接口名访问接口中的常量,但是如果一个类实现了接口,那么该类可以在类体中直接使用该接口中的常量。

  1. interface Eatable {
  2. String tag = "animal";
  3. void eat();
  4. }
  5. class Cat implements Eatable {
  6. @Override
  7. public void eat() {
  8. // 通过接口名访问常量
  9. System.out.println(Runnable.tag);
  10. // 类内直接访问
  11. System.out.println(tag);
  12. }
  13. }
  • 如果 interface 前面加了 public 关键字,就这样的接口是一个 public 接口,可以被任意一个类实现
  • 不加 public 的接口是一个友好的接口,只能被同一个包内中的类实现
  • 如果父类实现了某个接口,子类自然也就实现了该接口,不必显式地使用关键字 implements 实现这个接口
  • 接口可以被继承,通过关键字 extends 继承另一个接口 (很少这样做)

6.3 接口的UML图

  • 顶部是名字,接口名字必须是 斜体字 需要使用 <<interface>>修饰名字
  • 第二层是常量层,格式 常量名:类型
  • 第三层是方法层,格式 方法名(参数列表):返回值

image.png

6.4 接口回调

把实现某一类接口的类创建的对象的引用传递给该接口声明的变量,然后调用实现类的方法,这个过程称之为 接口的回调

这里和前面的上转型对象十分相似,但是叫法不同

  1. interface Eatable {
  2. String tag = "animal";
  3. void eat();
  4. }
  5. class Cat implements Eatable {
  6. @Override
  7. public void eat() {
  8. System.out.println("猫食肉");
  9. }
  10. }
  11. class Rabbit implements Eatable{
  12. @Override
  13. public void eat() {
  14. System.out.println("兔子食草");
  15. }
  16. }
  17. class Test{
  18. public static void main(String[] args) {
  19. Eatable cat = new Cat();
  20. cat.eat(); // 接口的回调
  21. }
  22. }

6.5 理解接口

接口的语法规则很容易记住,但是真正的理解接口更重要。

理解接口的关键

  • 接口可以抽象出重要的行为标准,该行为标准用抽象方法表示。
  • 可以把接口实现类的对象引用赋值为接口变量


6.6 接口与多态

上面我们知道了接口回调的概念,通过接口回调我们就可以实现多态。

不同的类,实现了同一接口,就具备了不同的实现方式,那么在接口回调方法时,就具备了多种形态。

  1. interface Eatable {
  2. String tag = "animal";
  3. void eat();
  4. }
  5. class Cat implements Eatable{
  6. @Override
  7. public void eat() {
  8. System.out.println("猫吃肉");
  9. }
  10. }
  11. class Rabbit implements Eatable{
  12. @Override
  13. public void eat() {
  14. System.out.println("兔子吃草");
  15. }
  16. }
  17. class Animal{
  18. public static void eat(Eatable eatable){
  19. eatable.eat();
  20. }
  21. }
  22. class Main{
  23. public static void main(String[] args) {
  24. Animal.eat(new Cat());
  25. Animal.eat(new Rabbit());
  26. }
  27. }

6.7 abstract类与接口的比较

  • abstract 类可接口都可以有 abstract方法
  • 接口中只能有常量,不能有变量,抽象类可以有变量,也可以有常量
  • 抽象类中可以有非抽象方法,但是接口不可以

6.8 使用抽象类还是接口?

  • 如果某个问题需要通过继承解决,也就是说需要从写父类方法时,还要继承一些父类的特性,那么我们就应该使用抽象类。
  • 否则就使用接口

为什么有了抽象类还要有接口?

  • 因为Java是单继承语言,抽象类抽象颗粒细度难以把控,所以引入了接口。
  • 接口是更高级别的抽象,通过多接口实现可以更好的把控颗粒细度。

举个栗子

  1. public interface Flyable {
  2. void fly();
  3. }
  4. interface Swimmable{
  5. void swim();
  6. }
  7. class Brid implements Flyable{
  8. @Override
  9. public void fly() {
  10. System.out.println("小鸟会飞");
  11. }
  12. }
  13. class Fish implements Swimmable{
  14. @Override
  15. public void swim() {
  16. System.out.println("鱼会游泳");
  17. }
  18. }

6.9 面向接口编程

现在学了接口来对前面的 笔芯 案例 改造下,同样要满足 ”开闭原则“

  1. class Refill{
  2. private String color;
  3. public Refill(String color) {
  4. this.color = color;
  5. }
  6. public void w(){
  7. System.out.println("我能书写"+color);
  8. }
  9. }
  10. interface Writable {
  11. void write(Refill refill);
  12. }
  13. public class Pen implements Writable{
  14. @Override
  15. public void write(Refill refill) {
  16. refill.w();
  17. }
  18. }
  19. class Test{
  20. public static void main(String[] args) {
  21. Pen pen = new Pen();
  22. pen.write(new Refill("红色"));
  23. pen.write(new Refill("蓝色"));
  24. }
  25. }
  1. 我能书写红色
  2. 我能书写蓝色

6.10 小结

  • 接口的接口体中可以有常量和abstract方法
  • 和类一样,接口也是Java中重要的引用数据类型,接口中只能存放实现接口类的实例对象引用
  • 当接口存放了实现类的对象引用时,接口调用类实现写接口方法称之为接口回调
  • 借助接口回调可以实现多态性
  • 在设计多态程序时,要熟练使用回调技术以及面向接口编程的思想,以实现”开闭原则”