A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 xiekai_sjz 于 2018-9-9 10:10 编辑

day10 接口, 多态    接口是一组行为的规范、定义。接口是面向对象编程体系中的思想精髓之一,使用接口可以让我们的程序更加利于变化。今天我们就来学习接口,和java三大特性中的多态。
以下是今天的学习目标:
  • 写出定义接口的格式
  • 写出实现接口的格式
  • 说出接口中成员的特点
  • 能够说出使用多态的前提条件
  • 理解多态的向上转型
  • 理解多态的向下转型
  • 能够完成笔记本电脑案例(方法参数为接口)


以下是今天的详细笔记:


接口接口概述与生活举例
接口的作用:
        提供一种公共的规范标准
接口的定义基本格式
Java中的接口: 多个类的公共规范

接口的定义格式:

    权限修饰符 interface 接口名 {

    }

        // 示例
    public interface USB {

    }

接口中包含的成员:
    JDK7:
        1. 常量 (自定义常量)  字面值常量: "String" 1 true
        2. 抽象方法  abstract ();
    JDK8 新增:
        3. 默认方法 default
        4. 静态方法 static
    JDK9 新增:
        5. 私有方法 private
补充:
[Java] 纯文本查看 复制代码
// JDK9 中定义一个接口
public interface USB {
    // 自定义常量
    // 抽象方法
    
    // 默认方法
    // 静态方法
    
    // 私有方法
}

接口的抽象方法定义
注意事项:
    1. 接口中的抽象方法, 修饰符必须是: public abstract
    2. public abstract 可以省略 (省略后也默认是 public abstract)
    3. 方法的三要素, 可以随意定义
接口的抽象方法使用
接口的使用:
    1. 接口不能创建对象, 要定义实现类来"实现"该接口 (implements)
    2. 接口的实现类必须"覆盖重写(实现)"接口中"所有的"抽象方法
    3. 创建实现类的对象, 调用重写的方法进行使用

一个类实现一个接口后, 该类有哪2种选择:
        1. 覆盖重写所有的抽象方法
        2. 如果没有覆盖重写所有抽象方法, 则必须将自己定义为抽象类

实现类实现接口的格式:

    public class 实现类名称 implements 接口名称 {
        // 需要重写父接口的所有抽象方法
    }
5分钟练习: 使用接口
需求:
定义接口USB, 其中包含2个抽象方法:
        打开功能: open()
        关闭功能: close()
定义接口实现类:键盘 Keyboard, 实现USB接口, 实现USB接口规范的功能
        打开功能实现方式: 打印"开启键盘"
        关闭功能实现方式: 打印"关闭键盘"
定义测试类:
        创建Keyboard对象, 调用开启和关闭方法
代码:
[Java] 纯文本查看 复制代码
/*
定义接口USB, 其中包含2个抽象方法:
        打开功能: open()
        关闭功能: close()
 */
public interface USB {
    // 打开
    public abstract void open();
    // 关闭
    public abstract void close();
}

/*
定义接口实现类:键盘 Keyboard, 实现USB接口, 实现USB接口规范的功能
        打开功能实现方式: 打印"开启键盘"
        关闭功能实现方式: 打印"关闭键盘"
 */
public class Keyboard implements USB {
    @Override
    public void open() {
        System.out.println("开启键盘");
    }

    @Override
    public void close() {
        System.out.println("关闭键盘");
    }
}

/*
需求:
定义接口USB, 其中包含2个抽象方法:
        打开功能: open()
        关闭功能: close()
定义接口实现类:键盘 Keyboard, 实现USB接口, 实现USB接口规范的功能
        打开功能实现方式: 打印"开启键盘"
        关闭功能实现方式: 打印"关闭键盘"
定义测试类:
        创建Keyboard对象, 调用开启和关闭方法
 */
public class Test {
    public static void main(String[] args) {
        // 创建实现类对象
        Keyboard keyboard = new Keyboard();
        keyboard.open();  // 开启键盘
        keyboard.close(); // 关闭键盘
    }
}

接口的默认方法定义
默认方法:
        JDK 8 开始, 接口中允许定义默认方法
        默认方法只能定义在接口中

默认方法的定义格式:
        使用default关键字修饰, 有方法体
        
        // 默认方法格式
    public default 返回值类型 方法名称(参数列表) {
        // 方法体
    }        

默认方法的作用: 解决接口升级的问题
接口的默认方法使用
默认方法的 public 可以省略不写 (省略后也默认是public)

默认方法的2种使用方式:
    1. 通过实现类对象直接调用执行
    2. 在实现类中覆盖重写后, 再调用重写的方法
5分钟练习: 练习默认方法
需求:
定义接口USB, 其中包含1个默认方法:
        传输数据功能: transfer(), 方法内部打印"通过USB口传输数据"
定义实现类: U盘 UDisk, 实现USB接口
        不用重写默认方法
定义实现类: 手机 Phone, 实现USB接口
        重写默认的传输方法: 方法内部打印"使用USB线传输数据"
定义测试类:
        创建U盘对象, 调用传输数据方法
        创建手机对象, 调用传输数据方法
代码:
[Java] 纯文本查看 复制代码
public interface USB {
    // 默认方法
    public default void transfer() {
        System.out.println("通过USB口传输数据");
    }
}

public class UDisk implements USB {
}

public class Phone implements USB {
    @Override
    public void transfer() {
        System.out.println("使用USB线传输数据");
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建U盘
        UDisk uDisk = new UDisk();
        uDisk.transfer();  // 通过USB口传输数据

        // 创建手机
        Phone phone = new Phone();
        phone.transfer();  // 使用USB线传输数据
    }
}

接口的静态方法定义
静态方法:
        JDK 8开始, 接口当中允许定义静态方法
        接口中的静态方法, public 可以省略(省略后也默认是public)
接口的静态方法使用
接口中静态方法的使用方式:
        接口名.静态方法名();

实现类对象"不能"调用接口的静态方法
        原因: 一个实现类可以同时实现多个接口, 多个接口中可能有相同的静态方法声明, 但方法体却不同, 无法知道调用的是哪个具体的方法
5分钟练习: 测试静态方法
需求:
定义接口USB, 其中包含1个静态方法:
        显示USB规范功能: static void show(), 方法内部打印如下内容:
                "USB用于规范电脑与外部设备的连接和通讯, 是应用在PC领域的接口技术"
定义测试类:
        在测试类中, 直接使用USB接口名调用静态show()方法
代码:
[Java] 纯文本查看 复制代码
public interface USB {
    // 静态方法
    public static void show() {
        System.out.println("USB用于规范电脑与外部设备的连接和通讯, 是应用在PC领域的接口技术");
    }
}

/*
需求:
定义接口USB, 其中包含1个静态方法:
        显示USB规范功能: static void show(), 方法内部打印如下内容:
                "USB用于规范电脑与外部设备的连接和通讯, 是应用在PC领域的接口技术"
定义测试类:
        在测试类中, 直接使用USB接口名调用静态show()方法
 */
public class Test {
    public static void main(String[] args) {
        // 接口名直接调用静态方法
        USB.show();  // USB用于规范电脑与外部设备的连接和通讯, 是应用在PC领域的接口技术
    }
}

接口的私有方法定义
私有方法:
        JDK 9开始, 接口当中允许定义私有方法
        可以定义 静态私有方法 和 非静态私有方法
        
        // 非静态私有方法
        private 返回值类型 方法名(参数列表) {
                // 方法体
        }
        
        // 静态私有方法
        private static 返回值类型 方法名(参数列表) {
             // 方法体   
    }

私有方法的作用:
        解决多个默认方法中代码重复的问题
接口的私有方法使用
接口中的私有方法只能在接口内部使用

    静态私有方法, 解决多个静态方法代码重复的问题
    非静态私有方法, 解决多个默认方法代码重复问题
接口的常量定义和使用
接口中的常量定义格式:
        public static final 数据类型 常量名称 = 数据值;
[Java] 纯文本查看 复制代码
// 示例
    public interface USB2 {
        // 常量一旦赋值, 不可以修改
        public static final String VERSION = "2.0";
    }

使用格式:
        接口名.常量名

[Java] 纯文本查看 复制代码
// 示例
    System.out.println(USB2.VERSION); // 2.0

注意事项:
    1. 常量可以省略 public static final (省略后也默认是 public static final)
    2. 常量必须进行赋值, 一旦赋值后不能改变
    3. 常量的命名规范: 所有字母全都大写, 单词之间用下划线_进行分隔

接口的内容小结
在Java 9+版本中, 接口的内容可以有:
    1. 成员变量 (其实是常量)
        格式: [public] [static] [final] 数据类型 常量名 = 数据值;
        注意:
            常量必须进行赋值, 而且一旦赋值不能改变
            常量名称完全大写, 用下划线进行分隔
    2. 抽象方法
        格式: [public] [abstract] 返回值类型 方法名称(参数列表);
        注意: 实现类必须覆盖重写接口所有的抽象方法, 除非实现类是抽象类
    3. Java 8 开始, 接口里允许定义默认方法
        格式: [public] default 返回值类型 方法名称(参数列表) { 方法体 }
        注意:默认方法也可以被覆盖重写
    4. Java 8 开始, 接口里允许定义静态方法
        格式: [public] static 返回值类型 方法名称(参数列表) { 方法体 }
        注意: 应该通过接口名称进行调用, 不能通过实现类对象调用接口静态方法
    5. Java 9 开始, 接口里允许定义私有方法
        格式:
            普通私有方法: private 返回值类型 方法名称(参数列表) { 方法体 }
            静态私有方法: private static 返回值类型 方法名称(参数列表) { 方法体 }
        注意:private的方法只有接口自己才能调用, 不能被实现类或别人使用
接口的注意事项和特点
注意事项:
    1. 接口没有 静态代码块, 构造方法
    2. 一个类只能继承一个父类, 但可以同时实现多个接口
        public class 实现类类名 extends 父类类名 implements 接口A, 接口B, ... {}
    3. 多个接口存在重复的抽象方法, 实现类只需覆盖重写一次
    4. 实现类没有覆盖重写所有抽象方法, 那实现类就必须是一个抽象类
    5. 多个接口当中存在重复的默认方法, 那么实现类一定要对冲突的默认方法进行覆盖重写
    6. 一个类, 父类方法和接口默认方法冲突, 优先用父类当中继承的方法
类和接口之间关系总结
类, 接口之间的各种关系:
    1. 类与类: 单继承, 多层继承
    2. 类与接口: 多实现
    3. 接口与接口: 多继承

        public interface A {}
        public interface B {}
        public interface C extends A, B {}

注意事项:
    1. 多个父接口中, 抽象方法重复, 没关系
    2. 多个父接口中, 默认方法重复, 则子接口必须覆盖重写默认方法, 而且要带着 default 关键字
补充:
[Java] 纯文本查看 复制代码
// Demo演示
// 某些情况下, 多个接口中存在方法名和参数列表一样, 但返回值类型不一致的情况, 子接口无法同时继承, 实现类无法同时实现
/*
接口多实现的冲突情况
 */
public class Demo {
}



interface A {
    public abstract void method();  // 返回值类型void
}

interface B {
    public abstract int method();  // 返回值类型int
}

// 子接口多继承A和B如何编写?
//interface C extends A, B {}  // 不能这样写


// 实现类多实现A和B如何编写?
//class D implements A, B { }  // 不能这样写

什么时候用抽象类? 什么时候用接口
        如果一个事物, 可以概括描述子类, 则适合定义为抽象类
                超极本和笔记本, 超极本属于笔记本, 笔记本可以定义为抽象类
        如果一个事物, 只是为子类扩展出某种功能, 则适合定义为接口
                超极本和USB, USB只是扩展某种功能, 适合定义为接口, 里面的连接断开方法由超极本自己实现

多态多态的概述多态的前提:
    1. 继承或实现
    2. 子类重写父类方法
    3. 父类引用指向子类对象  Person s = new Student();
多态的格式与使用
多态的要点:
        父类引用指向子类对象

格式:
        父类类名 对象名 = new 子类类名();
        父接口名 对象名 = new 实现类类名();
补充:
[Java] 纯文本查看 复制代码
public class Fu {}
public class Zi extends Fu {}

public class Test {
    public static void main(String[] args) {
        // 多态: 左侧父类的引用, 指向了右侧子类的对象
        Fu obj = new Zi();
    }
}

多态中成员变量的使用特点
多态中成员变量的特点:
        直接通过对象名称访问成员变量: 看等号左边是谁, 优先用谁, 没有则向上找
        间接通过成员方法访问成员变量: 看该方法属于谁, 优先用谁, 没有则向上找
多态中成员方法的使用特点
多态中成员方法的特点:
        new的对象是哪个类, 就优先用哪个类的方法, 没有则向父类中找

多态口诀:
        成员变量: 编译看左边, 运行看左边
        *静态成员方法: 编译看左边, 运行看左边
        非静态成员方法: 编译看左边, 运行看右边
说明:
        编译看是否报错, 运行看执行结果
        左边指父类中是否有该成员的定义, 右边指子类中是否有该成员的定义

总结一句话: 除了非静态成员方法运行看右边, 其他无论编译或运行都看左边
补充:
[Java] 纯文本查看 复制代码
// Demo演示
/*
多态调用成员方法
    非静态成员方法: 编译看左边, 运行看右边
                编译看左边父类中是否定义了该方法
                运行按照右边子类中重写的方法执行
 */
public class Demo {
    public static void main(String[] args) {
        // 多态创建对象
        Fu obj = new Zi();
        obj.method();        // 是否报错?
//        obj.methodZi();      // 是否报错?

        // 成员变量: 编译看左边, 运行看左边
        System.out.println(obj.num);  // 10

        // 静态方法: 编译看左边, 运行也看左边
        obj.staticMethod();  // 父类静态方法


    }
}

class Fu {

    int num = 10;

    public static void staticMethod() {
        System.out.println("父类静态方法");
    }


    public void method() {
        System.out.println("父类方法method");
    }
}

class Zi extends Fu {

    int num = 20;

    public static void staticMethod() {
        System.out.println("子类静态方法");
    }

    // 重写父类方法
    @Override
    public void method() {
        System.out.println("子类重写方法method");
    }
    // 子类特有方法
    public void methodZi() {
        System.out.println("子类特有方法methodZi");
    }
}

使用多态的好处
多态可以简化代码书写

补充:
[Java] 纯文本查看 复制代码
// 多态能简化方法的定义
public abstract class Employee { void work(); }
public class Teacher extends Employee {}
public class Assistant extends Employee {}


// 需求: 定义方法, 让讲师和助教工作
// 以前的方式: 定义重载方法
public static void method(Teacher t) {
    t.work();
}
public static void method(Assistant a) {
    a.work();
}
method(new Teacher());
method(new Assistant());


// 多态的方式, 只需要定义一个方法
public static void method(Employee e) {  // Employee e = new Assistant();
    e.work();
}
method(new Teacher());
method(new Assistant());

引用类型转换/接口案例对象的向上转型向上转型: 子类对象提升为父类类型 (实现类对象提升为父接口类型)
        向上转型一定是安全的
        向上转型后, 无法使用子类特有方法 (编译看左边, 父类中没有子类特有方法)

    // 多态就是向上转型: 猫是一个动物
        Animal a = new Cat();
对象的向下转型
向下转型: 父类对象还原为子类类型 (父接口对象还原为实现类类型)

向下转型格式: (就是强制类型转换的格式)
        子类类名 对象名 = (子类类名)父类对象;
        实现类类名 对象名 = (实现类类名)父接口对象;

注意:
        对象本来是什么, 才能向下转型为什么.
    如果乱转会抛出类型转换异常ClassCastException

向下转型的应用场景:
        调用子类特有方法
补充:
[Java] 纯文本查看 复制代码
// 多态向上转型
Animal a = new Cat();
// 向下还原为Cat
Cat c = (Cat)a;
c.猫的特有方法();

5分钟练习: 多态的转型
需求:
定义抽象类: 动物 Animal
        具有抽象方法: 吃 eat()
定义类: 猫 Cat, 继承Animal类
        重写抽象方法: eat(), 方法内部打印"猫吃鱼"
        在猫类中定义特有方法: 抓老鼠 catchMouse(), 方法内部打印"猫抓老鼠"
定义测试类:
        使用多态形式, 创建Cat类对象用Animal类型变量接收, 调用吃的方法
        向下转型, 调用猫特有方法抓老鼠
代码:
[Java] 纯文本查看 复制代码
public abstract class Animal {
    public abstract void eat();
}

public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    // 特有方法: 抓老鼠
    public void catchMouse() {
        System.out.println("猫抓老鼠");
    }
}

public class Test {
    public static void main(String[] args) {
        // 多态创建对象: 向上转型
        Animal animal = new Cat();
        // 调用吃
        animal.eat();             // 猫吃鱼
        // 要调用子类特有方法, 需要向下转型
        Cat cat = (Cat) animal;
        cat.catchMouse();         // 猫抓老鼠
    }
}

用instanceof关键字进行类型判断
instanceof: 用于判断一个对象属于哪个类, 然后再向下转型, 避免发生类型转换异常

使用格式:
        对象 instanceof 类名
        结果是布尔类型
        
        Animal a = new Dog();
    if (a instanceof Dog) {
            // 强转为Dog
            Dog d = (Dog)a;
        d.特有方法();
    }
        
补充:
[Java] 纯文本查看 复制代码
public class Demo02Instanceof {

    public static void main(String[] args) {
        Animal animal = new Dog(); // 本来是一只狗
        animal.eat(); // 狗吃SHIT

        // 如果希望调用子类特有方法,需要向下转型
        // 判断一下父类引用animal本来是不是Dog
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }
        // 判断一下animal本来是不是Cat
        if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }

        giveMeAPet(new Dog());
    }

        // 通过多态接收参数
    public static void giveMeAPet(Animal animal) {
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }
        if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }
}

笔记本USB接口案例: 分析
需求:
定义笔记本类,实现笔记本使用USB鼠标、USB键盘

所需类和接口:
        USB 接口, 包含开启(open)功能, 关闭(close)功能
        笔记本Computer类, 包含运行(powerOn)功能, 关机(powerOff)功能, 使用USB设备功能(useDevice)
        鼠标Mouse类, 实现USB接口, 特有方法点击(click)
        键盘Keyboard类, 实现USB接口, 特有方法敲击(type)
        
运行效果:
        笔记本电脑开机
        
        打开鼠标
        鼠标点击
        关闭鼠标
        
        打开键盘
        键盘输入
        关闭键盘
        
        笔记本电脑关机

笔记本USB接口案例: 实现
方法参数为接口类型时, 实际传入的是接口实现类的对象, 利用到多态的原理
5分钟练习: 编写笔记本USB接口代码:
[Java] 纯文本查看 复制代码
/*
USB 接口, 包含开启(open)功能, 关闭(close)功能
 */
public interface USB {
    // 开启
    public abstract void open();
    // 关闭
    public abstract void close();
}

public class Mouse implements USB {
    @Override
    public void open() {
        System.out.println("开启鼠标");
    }

    @Override
    public void close() {
        System.out.println("关闭鼠标");
    }

    // 特有方法
    public void click() {
        System.out.println("点击鼠标");
    }
}

public class Keyboard implements USB {
    @Override
    public void open() {
        System.out.println("开启键盘");
    }

    @Override
    public void close() {
        System.out.println("关闭键盘");
    }

    // 特有方法
    public void type() {
        System.out.println("键盘输入");
    }
}

/*
笔记本Computer类, 包含运行(powerOn)功能, 关机(powerOff)功能, 使用USB设备功能(useDevice)
 */
public class Computer {
    // 开机
    public void powerOn() {
        System.out.println("笔记本开机");
    }

    // 关机
    public void powerOff() {
        System.out.println("笔记本关机");
    }

    // 使用USB设备
    public void useDevice(USB usb) {
        // 开启
        usb.open();
        // 调用特有方法
        if (usb instanceof Mouse) {
            Mouse mouse = (Mouse) usb;
            mouse.click();
        } else if (usb instanceof Keyboard) {
            Keyboard keyboard = (Keyboard) usb;
            keyboard.type();
        }
        // 关闭
        usb.close();
    }
}

/*
需求:
定义笔记本类,实现笔记本使用USB鼠标、USB键盘

所需类和接口:
        USB 接口, 包含开启(open)功能, 关闭(close)功能
        笔记本Computer类, 包含运行(powerOn)功能, 关机(powerOff)功能, 使用USB设备功能(useDevice)
        鼠标Mouse类, 实现USB接口, 特有方法点击(click)
        键盘Keyboard类, 实现USB接口, 特有方法敲击(type)

运行效果:
        笔记本电脑开机

        打开鼠标
        鼠标点击
        关闭鼠标

        打开键盘
        键盘输入
        关闭键盘

        笔记本电脑关机
 */
public class Test {
    public static void main(String[] args) {
        // 创建笔记本对象
        Computer computer = new Computer();

        // 开机
        computer.powerOn();

        // 使用鼠标
        computer.useDevice(new Mouse());
        // 使用键盘
        computer.useDevice(new Keyboard());

        // 关机
        computer.powerOff();
    }
}

补充练习: 综合练习接口需求:
定义接口USB, 其中包含:
        静态方法:
                显示USB规范 static void show(), 方法内部打印如下内容:
                        "USB用于规范电脑与外部设备的连接和通讯, 是应用在PC领域的接口技术"
    抽象方法:
                开启功能: open()
         关闭功能: close()
         传输功能: transfer()
定义接口USB2, 继承USB接口. 其中包含:
        常量:
                USB版本: 字符串类型的VERSION, 值为"2.0"
    默认方法:
                传输功能: transfer(), 方法内部打印"2.0中速传输"
定义接口USB3, 继承USB2, 其中包含:
        常量:
                USB版本: 字符串类型的VERSION, 值为"3.0"
    默认方法:
                传输功能: transfer(), 方法内部打印"3.0高速传输"
定义实现类: 鼠标 Mouse, 实现USB2接口
        实现open抽象方法: 内部打印"开启鼠标"
    实现close抽象方法: 内部打印"关闭鼠标"
定义实现类: 手机 Phone, 实现USB3接口
        实现open抽象方法: 内部打印"连接手机"
    实现close抽象方法: 内部打印"断开手机"
    重写transfer方法: 内部打印"通过USB线3.0高速传输数据"
定义测试类:
        通过USB接口调用静态show()方法
        创建鼠标Mouse对象, 调用open(), transfer(), close()方法
        创建手机Phone对象, 调用open(), transfer(), close()方法
查看打印效果:

USB用于规范电脑与外部设备的连接和通讯, 是应用在PC领域的接口技术
开启鼠标
2.0中速传输
关闭鼠标
连接手机
通过USB线3.0高速传输数据
断开手机


































2 个回复

倒序浏览
liuyaxiong 来自手机 初级黑马 2018-9-4 22:38:45
沙发
谢谢分享
回复 使用道具 举报
12345,上山打老虎,老虎没打到,打到刘亚雄!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马