本帖最后由 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高速传输数据
断开手机
|
|