1、概述

一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java 不支持多重继承。有了接口,就可以得到多重继承的效果。

另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is-a 的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3 机、手机、数码相机、移动硬盘等都支持 USB 连接。

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个”是不是”的关系,而接口实现则是”能不能”的关系。

接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
image.png

2、接口的特点

  • 用interface来定义的
  • 接口中的成员变量都默认是由 public static final 修饰的。
  • 接口中的抽象方法都默认是由 public abstract 修饰的。
  • 接口中没有构造器
  • 接口采用多继承机制

    3、接口的特点

  1. 接口使用 interface 来定义。
  2. 在 Java 中:接口和类是并列的两个结构

  3. 如何去定义两个接口:定义接口中的成员

    1. JDK7 及以前:只能定义全局常量和抽象方法
      1. 全局常量:public static final 的,但是书写中,可以省略不写。
      2. 抽象方法:public abstract 的
    2. JDK8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法
  4. 接口中不能定义构造器!意味着接口不可以实例化。

  5. Java 开发中,接口通过让类去实现(implements)的方式来使用。
    1. 如果实现类覆盖了接口中的所有方法,则此实现类就可以实例化
    2. 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
  6. Java 类可以实现多个接口 —-》弥补了 Java 单继承性的局限性

    1. 格式:class AA extends BB implementd CC,DD,EE
  7. 接口与接口之间是继承,而且可以多继承

  8. 接口的具体使用,体现多态性

    1. 接口的主要用途就是被实现类实现。(面向接口编程)
  9. 接口,实际可以看作是一种规范 ```java /*

    • 接口的使用
    • 1.接口使用上也满足多态性
    • 2.接口,实际上就是定义了一种规范
    • 3.开发中,体会面向接口编程!
    • */ public class USBTest { public static void main(String[] args) {

      Computer com = new Computer(); //1.创建了接口的非匿名实现类的非匿名对象 Flash flash = new Flash(); com.transferData(flash); //2. 创建了接口的非匿名实现类的匿名对象 com.transferData(new Printer()); //3. 创建了接口的匿名实现类的非匿名对象 USB phone = new USB(){

      1. @Override
      2. public void start() {
      3. System.out.println("手机开始工作");
      4. }
      5. @Override
      6. public void stop() {
      7. System.out.println("手机结束工作");
      8. }

      }; com.transferData(phone); //4. 创建了接口的匿名实现类的匿名对象 com.transferData(new USB(){

      1. @Override
      2. public void start() {
      3. System.out.println("mp3 开始工作");
      4. }
      5. @Override
      6. public void stop() {
      7. System.out.println("mp3 结束工作");
      8. }

      }); } } class Computer{

      public void transferData(USB usb){//USB usb = new Flash(); usb.start();

      System.out.println(“具体传输数据的细节”);

      usb.stop(); } }

interface USB{ //常量:定义了长、宽 void start();

  1. void stop();

} class Flash implements USB{

  1. @Override
  2. public void start() {
  3. System.out.println("U 盘开始工作");
  4. }
  5. @Override
  6. public void stop() {
  7. System.out.println("U 盘结束工作");
  8. }

} class Printer implements USB{ @Override public void start() { System.out.println(“打印机开启工作”); }

  1. @Override
  2. public void stop() {
  3. System.out.println("打印机结束工作");
  4. }

}

  1. <a name="ftuFt"></a>
  2. # 4、接口的应用:代理模式(Proxy)
  3. 代理模式是 Java 开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
  4. ```java
  5. /*
  6. * 接口的应用:代理模式
  7. *
  8. *
  9. */
  10. public class NetWorkTest {
  11. public static void main(String[] args) {
  12. Server server = new Server();
  13. // server.browse();
  14. ProxyServer proxyServer = new ProxyServer(server);
  15. proxyServer.browse();
  16. }
  17. }
  18. interface NetWork{
  19. public void browse();
  20. }
  21. //被代理类
  22. class Server implements NetWork{
  23. @Override
  24. public void browse() {
  25. System.out.println("真实的服务器来访问网络");
  26. }
  27. }
  28. //代理类
  29. class ProxyServer implements NetWork{
  30. private NetWork work;
  31. public ProxyServer(NetWork work){
  32. this.work = work;
  33. }
  34. public void check(){
  35. System.out.println("联网前的检查工作");
  36. }
  37. @Override
  38. public void browse() {
  39. check();
  40. work.browse();
  41. }
  42. }

应用场景:

  • 安全代理:屏蔽对真实角色的直接访问。
  • 远程代理:通过代理类处理远程方法调用(RMI)
  • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象

分类:

  • 静态代理(静态定义代理类)
  • 动态代理(动态生成代理类)

    • JDK 自带的动态代理,需要反射等知识 ```java public class StaticProxyTest {

      public static void main(String[] args) { Proxy s = new Proxy(new RealStar()); s.confer(); s.signContract(); s.bookTicket(); s.sing(); s.collectMoney(); } }

interface Star { void confer();// 面谈

  1. void signContract();// 签合同
  2. void bookTicket();// 订票
  3. void sing();// 唱歌
  4. void collectMoney();// 收钱

} //被代理类 class RealStar implements Star {

  1. public void confer() {
  2. }
  3. public void signContract() {
  4. }
  5. public void bookTicket() {
  6. }
  7. public void sing() {
  8. System.out.println("明星:歌唱~~~");
  9. }
  10. public void collectMoney() {
  11. }

}

//代理类 class Proxy implements Star { private Star real;

  1. public Proxy(Star real) {
  2. this.real = real;
  3. }
  4. public void confer() {
  5. System.out.println("经纪人面谈");
  6. }
  7. public void signContract() {
  8. System.out.println("经纪人签合同");
  9. }
  10. public void bookTicket() {
  11. System.out.println("经纪人订票");
  12. }
  13. public void sing() {
  14. real.sing();
  15. }
  16. public void collectMoney() {
  17. System.out.println("经纪人收钱");
  18. }

}

  1. 在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。<br />-【面试题】排错:
  2. ```java
  3. interface A {
  4. int x = 0;
  5. }
  6. class B {
  7. int x = 1;
  8. }
  9. class C extends B implements A {
  10. public void pX() {
  11. // 编译不通过,x 不明确
  12. System.out.println(x);
  13. // System.out.println(super.x); //1
  14. // System.out.println(A.x);//0 因为接口中的成员变量都是用static和final修饰的,所以可以使用接口名直接调用
  15. }
  16. public static void main(String[] args) {
  17. new C().pX();
  18. }
  19. }

排错 2:

  1. interface Playable {
  2. void play();
  3. }
  4. interface Bounceable {
  5. void play();
  6. }
  7. interface Rollable extends Playable, Bounceable {
  8. Ball ball= new Ball("PingPang"); //省略了 public static final
  9. }
  10. public class Ball implements Rollable {
  11. private String name;
  12. public String getName() {
  13. return name;
  14. }
  15. public Ball(String name) {
  16. this.name= name;
  17. }
  18. public void play() { //此处会将两个接口中的方法全部重写
  19. ball = new Ball("Football"); //The final field Rollable.ball cannot be assigned
  20. System.out.println(ball.getName());
  21. }
  22. }

5、Java 8 中关于接口的改进

Java 8 中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:
使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像 Collection/Collections 或者 Path/Paths 这样成对的接口和类。

默认方法:
默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API 中对 Collection、List、Comparator 等接口提供了丰富的默认方法。
例1

  1. /*
  2. * JDK8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)。
  3. *
  4. *
  5. */
  6. public interface CompareA {
  7. //静态方法
  8. public static void method1() {
  9. System.out.println("CompareA:西安");
  10. }
  11. //默认方法
  12. public default void method2(){
  13. System.out.println("CompareA:深圳");
  14. }
  15. default void method3(){
  16. System.out.println("CompareA:杭州");
  17. }
  18. }
  1. public class SubClassTest {
  2. public static void main(String[] args) {
  3. SubClass s = new SubClass();
  4. // s.method1();
  5. // SubClass.method1();
  6. // 知识点 1:接口中定义的静态方法,只能通过接口来调用。
  7. CompareA.method1();
  8. // 知识点 2:通过实现类的对象,可以调用接口中的默认方法。
  9. // 如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
  10. s.method2();
  11. // 知识点 3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
  12. // 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
  13. // 知识点 4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
  14. // 那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
  15. // 这就需要我们必须在实现类中重写此方法
  16. s.method3();
  17. }
  18. }
  19. class SubClass extends SuperClass implements CompareA,CompareB{
  20. public void method2(){
  21. System.out.println("SubClass:上海");
  22. }
  23. public void method3(){
  24. System.out.println("SubClass:深圳");
  25. }
  26. // 知识点 5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
  27. public void myMethod(){
  28. method3(); //调用自己定义的重写的方法
  29. super.method3(); //调用的是父类中声明的
  30. // 调用接口中的默认方法
  31. CompareA.super.method3();
  32. CompareB.super.method3();
  33. }
  34. }

知识点:

  • 接口中定义的静态方法,只能通过接口来调用
  • 通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
  • 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。—>类优先原则
  • 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。—>接口冲突。这就需要我们必须在实现类中重写此方法
  • 如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
    • super.方法名(); //调用的是父类中声明的
    • 接口名.super.方法名(); //调用接口中的默认方法

      6、抽象类和接口有哪些共同点和区别?