Java SE面向对象--10.接口、多态

版权声明:转载请注明原始链接 https://blog.csdn.net/sswqzx/article/details/82719643

学习目标

  • 接口
  • 三大特征——多态
  • 引用类型转换

一、接口

1.1 概述

接口,是Java语言中一种引用类型,接口中只定义了方法,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。

接口的定义,它与类的定义方式相似,和类是同等级别的。使用 interface 关键字来完成接口的定义。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。

引用数据类型:数组,类,接口。

接口的使用,它不能创建对象,但是可以被实现(使用implements 关键字来完成接口的实现,类似于被继承)。一个实现接口的类(可以看做是接口的子类或者叫做实现类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。

类和接口的关系叫做实现关系:某个类实现了某个接口。

类和类的关系叫做继承关系:某个类继承了某个类。

1.2 定义实现格式

接口的定义格式

public interface 接口名称 {
    // 抽象方法
    // 默认方法
    // 静态方法
    // 私有方法
}

接口的实现格式: 接口和类不同,实现类在实现接口的时候可以实现多个接口,即接口的多实现特性。

public class InterImpl implements 接口名称,接口名称 {
  // 必须实现(重写)接口中的所有的抽象方法
  // 可以直接调用接口中的默认方法,方法体不适合可重写该方法
  // 静态方法的作用是可以不实现接口,使用接口名直接调用
  // 私有方法是给接口本身调用使用的,目的是为了提高代码的复用性
}

实现接口注意事项

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

1、实现类在实现接口的时候,需要实现接口中的所有的抽象方法。

2、接口中的抽象方法有默认的关键字即:public 和 abstract。即使不书写这两个关键字依然默认,所以如果接口中没有书写,那么实现类在实现接口中方法的时候一定要书写public,因为在类中如果不书写public则为默认权限,而子类实现接口中的方法时,访问权限要大于或等于父接口。

3、接口和类不同,实现类在实现接口的时候可以实现多个接口。

如果实现类实现了两个以上接口,而两个以上接口中有相同方法名的方名时(非静态),实现类只需要在类中实现一次即可。最终会调用实现类实现方法。

含有抽象方法( JDK7之前 )

抽象方法:使用abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。

如果实现类实现了两个以上接口,而两个以上接口中有相同方法名的抽象方名时,实现类只需要在类中实现一次即可。最终会调用实现类实现方法。

代码如下:

// 接口1
interface InterfaceName1  {
    public abstract void method1();//抽象方法
    void method2(); // 此处为写public关键字,但是系统默认,建议不要省略
}
//接口2
interface InterfaceName2  {
    public abstract void method1();//抽象方法 和接口1中方法重名,实现类实现一次即可
    public abstract void method(); 
}
//接口实现类
class Impl_Name implements InterfaceName1 , InterfaceName2 {
  /* 当类实现接口之后,需要实现接口中的所有的抽象方法 */
  // 实现的为接口2的方法
  public void method(){
    System.out.println("method run....");
  }

  // 下列实现的为接口1 和 2同名的方法以及接口2中的方法
  public void method1(){
    System.out.println("method1 run....");
  }
  // 如果接口中的方法没有写public,实现类实现方法的时候一定要记得写,否则权限报错
  public void method2(){
    System.out.println("method2 run....");
  }
}
// 测试类
public class Demo{
  public static void main(String[] args ){
    // 创建实现类对象
    Demo d = new Demo();
    // 调用实现的方法
    d.method();
    d.method1();
    d.method2();
  }
}

含有默认方法的接口(JDK8)

在JDK8中增加了默认的方法和静态的方法,

默认方法:

​ 使用 default 修饰,不可省略,供子类对象调用或者子类重写。默认权限修饰符是public。default关键字不可以在类中使用,只能在接口中使用。类中不写任何权限修饰符就会默认为默认权限。

默认方法的使用事项:

​ 1、默认方法是有方法体的方法,类实现了接口之后可以直接调用。如果方法体不满足类中需求,可以在类中重写该默认方法。

​ 2、如果类实现了两个接口中有同名的默认方法,那么实现类中没有方法指名调用具体哪一个接口中的方法。因此在这种情况下,要求实现类必须将接口同同名的默认方法重写一次。

// 接口1
interface InterfaceName1  {
   public default void method(){
        System.out.println("method run....");
   }
   public default void show(){
        System.out.println("show run....");
   } 
}
//接口2
interface InterfaceName2  {
    public default void method(){
        System.out.println("method run....");
   }
   public default void method1(){
        System.out.println("show run....");
   } 
}
//接口实现类
class Impl_Name implements InterfaceName1 , InterfaceName2 {
    // 在实现类中必须重写两个接口中的同名的默认方法
    public default void method(){
        System.out.println("重写之后的...method run....");
    }

    // 如果其他的默认方法不适合实现类使用,可以选择重写。如果适合则不用重写
    public default void method1(){
        System.out.println("show方法适合使用,所以只重写method1()....");
    } 
}
// 测试类
public class Demo{
  public static void main(String[] args ){
    // 创建实现类对象
    Demo d = new Demo();
    // 可以直接调用接口中的默认方法
    d.method();
    d.method1();
    d.show();
  }
}

含有静态方法的接口(JDK8)

静态方法:使用 static 修饰,供接口名直接调用。不用在使用子类对象间接调用,这样调用的方式更为简单些。权限修饰符可以是public 或 private。

说明:可以使用private修饰的原因是源于静态的思想。在接口中的其他静态方法中是可以调用私有的静态方法。而使用private修饰的方法更为安全。 在JDK9的特性中一起讲。

静态方法的使用事项:

​ 1、接口中的静态方法是可以使用接口名直接调用的,所以使用该接口的类可以不直接实现该接口直接调用静态方法。

​ 2、如果两个接口中的静态方法重名,实现类是可以不用重写该方法的,因为调用的时候是使用接口名直接调用,使用哪个接口名则直接调用哪个接口中的静态方法。静态方法不参与重写。

​ 3、如果接口中的方法全部都是静态方法,那么可以不用写实现类去实现,接口名调用即可。

代码如下:

// 接口1
interface InterfaceName1  {
   public static void method(){
        System.out.println("接口1...method run....");
   }
   public static void show(){
        System.out.println("接口1...show run....");
   } 
}
//接口2
interface InterfaceName2  {
    public default void method(){
        System.out.println("接口2...method run....");
   }
   public default void show(){
        System.out.println("接口2...show run....");
   } 
}
//接口实现类 不需要

// 测试类
public class Demo{
  public static void main(String[] args ){
    // 使用接口名直接调用
    InterfaceName1.method();
    InterfaceName1.show();
    InterfaceName2.method();
    InterfaceName2.show();
  }
}

含有私有方法和私有静态方法(jdk9)

jdk9诞生这个新的技术主要是用来提高代码的复用性。

举例:如果本类中默认方法和静态方法有重复的内容我们需要书写很多遍。而我们把共性的内容放到私有方法中,这样减少代码的书写,并且只能在本类中使用。更加安全。

私有方法:使用 private 修饰,只供接口中的默认方法或者静态方法调用。

代码如下:

// 接口1
interface InterfaceName  {
  /*
    在下列代码中,两个方法中都使用到了for循环相同的代码,此时如果有更多的方法中需要使用到此循环,那么这些代码又需要重复写多遍。我们可以把重复的代码写在一个私有的静态的方法中,这样就可以让其他方法(不管是静态还是非静态)去调用,从而提高代码的复用性。因为是私有的,所以安全性高。
  */
   public static void method(){
        System.out.println("*****************");
            /*
                for( int i = 1; i < 10 ; i++){
                    System.out.println("i=" + i);
                }
            */
        function();// 调用私有方法
        System.out.println("*****************");
   }
   public default void show(){
        System.out.println("==================");
            /*
                for( int i = 1; i < 10 ; i++){
                    System.out.println("i=" + i);
                }
            */
        function();// 调用私有方法
        System.out.println("===================");
   } 

  // 供其他方法调用的私有静态方法
  private static void function(){
    for( int i = 1; i < 10 ; i++){
        System.out.println("i=" + i);
    }
  }
}

// 测试类
public class Demo{
  public static void main(String[] args ){
    // 使用接口名直接调用
    InterfaceName1.method();
    InterfaceName1.show();

  }
}

优先级的问题

当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。代码如下:

定义接口:

interface A {
    public default void methodA(){
        System.out.println("AAAAAAAAAAAA");
    }
}

定义父类:

class D {
    public void methodA(){
        System.out.println("DDDDDDDDDDDD");
    }
}

定义子类:

class C extends D implements A {
    // 未重写methodA方法
}

定义测试类:

public class Test {
    public static void main(String[] args) {
        C c = new C();
        c.methodA(); 
    }
}
输出结果:
DDDDDDDDDDDD

1.3 接口的多继承【了解】

一个接口能继承另一个或者多个接口。接口的继承使用 extends 关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。代码如下:

定义父接口:

interface A {
    public default void method(){
        System.out.println("AAAAAAAAAAAAAAAAAAA");
    }
}

interface B {
    public default void method(){
        System.out.println("BBBBBBBBBBBBBBBBBBB");
    }
}

定义子接口:

interface D extends  A,B{
    public default void method() {
        System.out.println("DDDDDDDDDDDDDD");
    }
}

小贴士:

子接口重写默认方法时,必须写default关键字。

子类重写默认方法时,不用写default关键字。

1.4 其他成员特点

  • 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。

  • 接口中,没有构造方法,不能创建对象。

  • 接口中,没有静态代码块。

二、 多态

2.1 概述

引入

面向对象语言三大特征:封装(private)、继承(extends)、多态。

多态:表示的是一个事物的多种表现形态。同一个事物,以不同的形态表现出来.

前提【重点】

  1. 继承或者实现【二选一】
  2. 父类或接口的引用指向子类对象【格式体现】

2.2 Java中多态的体现

多态体现的格式

父类或接口类型 变量名 = new 子类对象;
变量名.方法名();

父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

使用父类的引用,表示自己的子类对象。或者父接口指向实现类对象.

代码如下:

Cat c = new Cat();  //使用猫类型表示自己,这里不会发生多态现象
Animal a = new Cat();//使用动物的类型再表示猫,这时就发生的多态的现象。

代码体现如下

//演示多态技术
class Animal{
    public void eat(){}
    public void show(){
        System.out.println("show run....");
    }
    public void say(){
        System.out.println("say run.....");
    }
}
class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼!");
    }
}
class Dog extends Animal{
    public void eat(){
        System.out.println("狗啃骨头!!");
    }
}
public class DuoTaiDemo {
    public static void main(String[] args) {
        //不使用多态体现
        Cat c = new Cat();//子类的类型指向子类的引用
        Dog d = new Dog();//子类的类型指向子类的引用
        Animal a = new Animal();//父类的类型指向父类的引用

        /*  多态在代码中的体现
            我们在使用多态的时候,永远只能使用父类的类型接收子类的对象,而不能使用
            子类的类型接受父类的对象。
        */
        Animal cc = new Cat();//这里发生了多态
        Animal dd = new Dog();//这里也发生了多态
        Animal aa = new Animal();//这里没有发生多态
        cc.eat();
        dd.eat();
        aa.eat();
//      Demo( c );
//      Demo( d );
//      Demo( a );
        /*c.eat();
        c.say();
        c.show();
        d.eat();
        d.say();
        d.show();
        代码重复使用,如果方法较多的情况下,会产生大量的重复代码
        可以将所有的调用抽取到一个方法中,通过调用抽取的方法来实现
        */
    }
    //创建方法,传递对戏的引用,调用函数
    public static void Demo( Animal a ){// Animal a = new Dog();   Animal a = new Cat();
        a.eat();
        a.say();
        a.show();
    }
    /*
    public static void Demo( Dog a ){//一次只能传递一种类型。使用仍然存在代码的重复
        a.eat();
        a.say();
        a.show();
    }
    public static void Demo( Cat a ){//一次只能传递一种类型。使用仍然存在代码的重复
        a.eat();
        a.say();
        a.show();
    }
    */
}

多态注意事项

1、在使用多态的时候,永远只能使用父类的类型接收子类的对象,而不能使用子类的类型接收父类的对象.

2、当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。(编译看父类、运行看子类)

2.3 多态的弊端

代码演示

// 动物类
class Animal{
    public void eat(){}
}
// 描述猫
class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼!");
    }
    //猫的特有行为
    public void CatchMouse(){
        System.out.println("猫抓老鼠!");
    }
}
//描述狗
class Dog extends Animal{
    public void eat(){
        System.out.println("狗啃骨头!!");
    }
    //狗的特有行为
    public void lookHome(){
        System.out.println("狗看家!!");
    }
}
// 测试类
public class DuoTaiDemo {
    public static void main(String[] args) {
        Animal c = new Cat();
        c.eat();
        Animal d = new Dog();
        //使用多态调用方法
        d.eat();//正常
    /*
        如果已经发生多态现象,但是我们还想调用子类的特有属性或者行为,这时需要使用
        强制类型转换,把当前父类类型转成具体的子类类型。
        在多态中的类型转换问题:
            1、隐式的类型提升。只要有多态就会发生类型提升(向上转型)。
            2、把父类类型转成子类类型(强制类型转换,向下转型)。
        什么时候使用向下转型:
            只要在程序中我们需要使用子类的特有属性或行为(方法、函数)的时候,才会使用向下转型。
        向下转型有风险,使用需谨慎。
            在Java中要使用向下转型,必须先做类型的判断,然后在转型
            Java中的类型判断 需要使用关键字 instanceof
            格式:
                被转的引用变量名  instanceof 被转成的类型
                如果引用变量所在的那个对象 和被转成的类型一致,这个表达式返回的是true,否则是false
            在多态中使用转型的时候,一定要判断,防止类型转换异常的发生:
                如果在程序发生ClassCastException,一定是把不是这种类型的对象转成了这种类型。
    */
        Demo(c);
    }
    public static void Demo( Animal a ){
        /*
            类型传递时自动转换成了Animal类型
            在这个过程中容易将引用对象具体属于哪个子类搞混
            容易出现类型转换异常的发生
        */
        System.out.println("=========");
        a.eat();
        if( a instanceof Dog){
            Dog dd = (Dog)a;
            dd.lookHome();
        }else{
            Cat cc = (Cat)a;
            cc.CatchMouse();
        }
    }
}

多态的弊端总结

在使用多态技术的时候,程序在编译的时候,使用多态调用成员(变量和方法),要求被调用的成员在父类中一定要存在,如果父类中没有编译就会失败。(不能使用子类特有功能或者属性)

注意:

​ 1、只要有多态的地方,一定发生类型的提升(肯定是把子类对象使用父类类型在表示)。

​ 2、在多态的使用中,至始至终只要子类对象,没有父类对象产生。

2.4 多态中的转型

在使用多态时,存在一个弊端:不能使用子类中特有的方法。

如果在多态中,必须要使用子类特有的功能,需要在多态操作时进行类型的转换。

复习下之前学习过的类型转换:

自动类型提升 例: double d = 12; //int类型自动提升给double类型

强制类型转换 例: double dou = 3.14; int i = (int)dou;

多态的转型分为向上转型与向下转型两种:

向上转型

  • 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。

当父类引用指向一个子类对象时,便是向上转型。

使用格式:

父类类型  变量名 = new 子类类型();
如:Animal a = new Cat();
Cat类型被向上转为了Animal类型

向下转型

  • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。

一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

使用格式:

子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;  

如果一定要在父类引用中使用子类对象特有的功能,就需要向下转操作。

说明:子类对象中特定的功能只能子类对象自己调用。

多态类型转换的代码体现:

// 演示多态中的向下转型
class Animal{
    public void eat(){}
}
class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼!");
    }
    //猫的特有行为
    public void CatchMouse(){
        System.out.println("猫抓老鼠!");
    }
}
class Dog extends Animal{
    public void eat(){
        System.out.println("狗啃骨头!!");
    }
    //狗的特有行为
    public void lookHome(){
        System.out.println("狗看家!!");
    }
}
public class DuoTaiDemo {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.eat();
        //向下转型
        Dog dd = (Dog)a;
        // 调用子类特有方法
        dd.lookHome();
    }
}

2.5 多态类型转换时常见异常

在多态类型转换时经常会发生一个异常错误:ClassCastException(类型转换异常)。

多态类型转换常见异常代码演示:

abstract class Animal{
    abstract void eat();
}
class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼");
    }
    //猫有自己的特有行为 抓老鼠
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

class Dog extends Animal{
    public void eat(){
        System.out.println("狗啃骨头");
    }
    //狗也有自己的行为 看家
    public void lookHome(){
        System.out.println("狗看家");
    }
}
public class DuoTaiDemo {
    public static void main(String[] args) {
        Animal a= new Cat();
        demo(a);//传递的是猫的对象
    }
    public static void demo( Animal  a ){
        a.eat();
       //把Animal类型的a转成 Dog类的d

       //Dog d = (Dog)a;//将传递过来的猫的对象强制转换为狗是不可以的,会发生转换异常
        /*
            向下转型有风险,使用需谨慎。
            在Java中要使用向下转型,必须先做类型的判断,然后在转型
            Java中的类型判断 需要使用关键字 instanceof
            格式:
                被转的引用变量名  instanceof 被转成的类型
                如果引用变量所在的那个对象和被转的类型一致,表达式返回的是true,否则是false

            在多态中使用转型的时候,一定要判断,防止类型转换异常的发生:
                如果在程序发生ClassCastException,一定是把不是这种类型的对象转成了这种类型。
        */
        if( a instanceof Dog ){
            Dog d = (Dog)a;
            d.lookHome();
        }
        else if( a instanceof Cat ){
            Cat c = (Cat)a;
            c.catchMouse();
        }   
    }
}

总结:

多态属于欺骗行为,只要有多态,就会有类型的转换。

把子类对象赋值给父类的引用,这时发生了向上的转型(隐式类型转换)。

如果我们需要使用子类的特有行为或属性,这时必须向下转型,需要把父类的引用转成具体所指的那个对象的类型。

在向下转型的时候一定要做类型的判断,防止ClassCastException异常的发生。

向下转型判断格式

if( 父类引用变量名  instanceof  子类对象所属的类型 ){
    子类类型 变量名 = (子类类型)父类引用;
}

例如:

if(a  instanceof  Dog) {
    Dog d = (Dog)a;
}

2.6 多态中的成员特点总结

学习多态中的成员使用规律:需要掌握的是以多态形式使用成员,需要考虑程序的编译和运行2个阶段。

成员变量

成员变量:是直接使用父类引用操作变量。而没有通过其他的方法来操作。

成员变量:

在多态中,使用父类的引用访问成员变量,代码在编译的时期,需要查看父类中有没有这个成员变量,如果有,编译通过,没有编译失败。

多态中,使用父类的引用访问成员变量,编译通过的前提下,如果直接使用父类引用操作成员变量,这时操作的依然是父类中的成员变量。

简化记忆:引用变量,编译运行都看引用类中的变量(编译运行看左边)。

成员方法

在多态中,使用父类引用调用成员方法的时候,一般方法都有复写存在。

在使用父类引用调用方法的时候,编译时期要看父类中有没有这个方法,有,编译通过,没有编译失败。

在运行的时候,运行的是子类中复写父类之后的那个方法。如果没有复写,运行的肯定还是父类的方法。

··简化记忆:编译时期看左边,运行时期看右边。··

三、 接口多态综合案例

接口多态的综合案例之笔记本电脑案例的面向接口编程

耦合性概念 :耦合性就是依赖关系。

手机和电池耦合度就很高:

电脑和外接的硬件设备(鼠标,键盘)耦合度就很低。

笔记本是一个生产厂商.鼠标是另外一个生产厂商.*笔记本要使用外接的设备*,*笔记本上就要预留一些插口*,*而这些插口的预留大小和规则*,*一定要遵守某个规定*,*这时生产的笔记本才可以使用外接设备*.*而外接设备在生产的时候也应该遵守一定的规则*,*然后生产符合规则的设备*.*这样笔记本和外接设备之间就可以进行通信*.

这里写图片描述

接口的思想 :

举例:*笔记本电脑*,USB 接口的故事.

1、接口的出现对功能实现了扩展. (扩展功能)

2、接口的出现定义规则. 一方使用规则,另一方则实现规则.这样双方就可以实现通信.

3、接口的出现降低了耦合性 (解耦) 笔记本和鼠标…

阶段一需求: *笔记本电脑案例*,*按照面向对象的思想*,*用代码体现*.

​ 类: 笔记本电脑. 属性: CPU,内存,硬盘… 行为: 运行

/*
 * 描述电脑类
 */
public class Computer {
    //属性
    //行为
    public void run(){
        System.out.println("computer run....");
    }
}

测试类:

/*
 * 面向对象思想运行笔记本电脑中的方法
 */
public class ComputerTest {
    public static void main(String[] args) {
        // 创建笔记本电脑类的对象
        Computer c = new Computer();
        c.run();
    }
}

阶段二需求 :*想要在笔记本电脑上加一个鼠标*.

​ 类: 鼠标 行为: 开启, 关闭.

/*
 * 定义一个鼠标类
 */
public class Mouse {
    //行为:开启
    public void open(){
        System.out.println("mouse open....");
    }
    //行为:关闭
    public void close(){
        System.out.println("mouse close....");
    }
}

请问:笔记本如何使用鼠标呢?

在笔记本中多了一个使用鼠标的功能,*需要修改原来笔记本类中的代码*,*添加一个功能*

/*
 * 描述电脑类
 */
public class Computer {
    //属性
    //行为
    public void run(){
        System.out.println("computer run....");
    }
    //拆开电脑,增加使用鼠标的功能
    public void useMouse(Mouse m){
        if(m != null){
            m.open();
            m.close();
        }
    }
}

测试类:

/*
 * 需求一:面向对象思想运行笔记本电脑中的方法
 * 需求二:在笔记本电脑上添加一个鼠标
 */
public class ComputerTest {
    public static void main(String[] args) {
        // 创建笔记本电脑类的对象
        Computer c = new Computer();
        c.run();
        Mouse m = new Mouse();
        //电脑使用鼠标
        c.useMouse(m);
    }
}

问题:如果想要加入一个键盘呢?

​ 需要定义一个键盘类,并在电脑类中加入一个使用键盘的功能。

/*
 * 定义一个键盘类
 */
public class Keyboard {
    // 行为:开启
    public void open() {
        System.out.println("keyboard open....");
    }
    // 行为:关闭
    public void close() {
        System.out.println("keyboard close....");
    }
}

电脑类:

/*
 * 描述电脑类
 */
public class Computer {
    //属性
    //行为
    public void run(){
        System.out.println("computer run....");
    }
    //拆开电脑,增加使用鼠标的功能
    public void useMouse(Mouse m){
        if(m != null){
            m.open();
            m.close();
        }
    }
    //拆开电脑,增加使用键盘的功能
    public void useKeyboard(Keyboard k){
        if(k != null){
            k.open();
            k.close();
        }
    }


}

测试类

/*
 * 需求一:面向对象思想运行笔记本电脑中的方法
 * 需求二:在笔记本电脑上添加一个鼠标
 * 需求三:在笔记本电脑上添加一个键盘
 */
public class ComputerTest {
    public static void main(String[] args) {
        // 创建笔记本电脑类的对象
        Computer c = new Computer();
        c.run();
        Mouse m = new Mouse();
        //电脑使用鼠标
        c.useMouse(m);
        Keyboard k = new Keyboard();
        //电脑使用键盘
        c.useKeyboard(k);
    }
}

注意:

其实发现从鼠标开始,这个问题就已经产生了.一旦需要添加新设备的时候,都需要修改电脑类的源代码,(在生活中可以理解为将电脑拆开,将外围设备添加到电脑内部). 这样扩展性就非常差。

问题 :如何在设计上进行修改和改进呢?

​ 之前的设计在于外围设备的增加和笔记本电脑之间的耦合性过高.互相依赖太紧密.

问题 :如何降低外围设备和笔记本电脑之间的耦合性呢?

​ 外围设备还不确定,我们不要面对外围的具体设备.为了让笔记本可以使用这些外围设备,可以事先定义好一些规则.笔记本只要使用这些规则就可以了.有了这些规则就可以对笔记本进行功能的扩展,后期这些外围设备只要符合这些规则就可以被笔记本使用了.

问题 :规则在 Java**中如何体现呢**?

接口.

实现 :

1、定义一个 USB 接口.

2、描述笔记本电脑. 行为: 运行功能, 使用USB接口的功能.

USB接口:

/*
 * 定义一个USB接口
 */
public interface USB {
    public void open();
    public void close();
}

鼠标类:

/*
 * 定义一个鼠标类
 */
public class Mouse implements USB{
    //行为:开启
    public void open(){
        System.out.println("mouse open....");
    }
    //行为:关闭
    public void close(){
        System.out.println("mouse close....");
    }
}

键盘类:

/*
 * 定义一个键盘类
 */
public class Keyboard implements USB{
    // 行为:开启
    public void open() {
        System.out.println("keyboard open....");
    }
    // 行为:关闭
    public void close() {
        System.out.println("keyboard close....");
    }
}

电脑类:

/*
 * 描述电脑类
 */
public class Computer {
    //属性
    //行为
    public void run(){
        System.out.println("computer run....");
    }
    //给电脑外围设备提供预留的功能,该功能要遵守USB接口的规则
    public void useUSB( USB u ){//USB u=m
        if(u != null){
            u.open();
            u.close();
        }
    }
}

测试类:

public class ComputerTest {
    public static void main(String[] args) {
        // 创建笔记本电脑类的对象
        Computer c = new Computer();
        c.run();
        Mouse m = new Mouse();
        //电脑使用鼠标
        c.useUSB(m);
        Keyboard k = new Keyboard();
//      //电脑使用键盘
        c.useUSB(k);
    }
}

总结:发现接口的出现:

1、 扩展了笔记本电脑的功能.

2、 定义了规则.

3、降低了笔记本电脑和外围设备之间的耦合性. 解耦

猜你喜欢

转载自blog.csdn.net/sswqzx/article/details/82719643
今日推荐