当前位置: 代码迷 >> 综合 >> day11【接口、多态】
  详细解决方案

day11【接口、多态】

热度:26   发布时间:2023-12-24 22:47:03.0

1.接口

1.1 接口概述

在这里插入图片描述
不是类,而是另外一种引用数据类型。
引用数据类型:数组,类,接口。
接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做
是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。

1.2 定义格式

package cn.itcast.day10.demo01;/* 接口就是多个类的公共规范。 接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。如何定义一个接口的格式: public interface 接口名称 {// 接口内容 }备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --> .class。接口使用步骤: 1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。 格式: public class 实现类名称 implements 接口名称 {// ... } 2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。 实现:去掉abstract关键字,加上方法体大括号。 3. 创建实现类的对象,进行使用。注意事项: 如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。*/
public class Demo01Interface {
    public static void main(String[] args) {
    // 错误写法!不能直接new接口对象使用。
// MyInterfaceAbstract inter = new MyInterfaceAbstract();// 创建实现类的对象使用MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();impl.methodAbs1();impl.methodAbs2();}}

如果是Java 7,那么接口中可以包含的内容有:
1. 常量
2. 抽象方法

如果是Java 8,还可以额外包含有:
3. 默认方法
4. 静态方法

如果是Java 9,还可以额外包含有:
5. 私有方法

1.3基本的实现

抽象方法的使用

package cn.itcast.day10.demo01;/* 在任何版本的Java中,接口都能定义抽象方法。 格式: public abstract 返回值类型 方法名称(参数列表);注意事项: 1. 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract 2. 这两个关键字修饰符,可以选择性地省略。(今天刚学,所以不推荐。) 3. 方法的三要素,可以随意定义。*/
public interface MyInterfaceAbstract {
    // 这是一个抽象方法public abstract void methodAbs1();// 这也是抽象方法abstract void methodAbs2();// 这也是抽象方法public void methodAbs3();// 这也是抽象方法void methodAbs4();}
package cn.itcast.day10.demo01;public class MyInterfaceAbstractImpl implements MyInterfaceAbstract {
    @Overridepublic void methodAbs1() {
    System.out.println("这是第一个方法!");}@Overridepublic void methodAbs2() {
    System.out.println("这是第二个方法!");}@Overridepublic void methodAbs3() {
    System.out.println("这是第三个方法!");}@Overridepublic void methodAbs4() {
    System.out.println("这是第四个方法!");}
}

默认方法的使用

package cn.itcast.day10.demo01;/* 从Java 8开始,接口里允许定义默认方法。 格式: public default 返回值类型 方法名称(参数列表) {方法体 }备注:接口当中的默认方法,可以解决接口升级的问题。*/
public interface MyInterfaceDefault {
    // 抽象方法public abstract void methodAbs();// 新添加了一个抽象方法
// public abstract void methodAbs2();// 新添加的方法,改成默认方法public default void methodDefault() {
    System.out.println("这是新添加的默认方法");}}
package cn.itcast.day10.demo01;public class MyInterfaceDefaultA implements MyInterfaceDefault {
    @Overridepublic void methodAbs() {
    System.out.println("实现了抽象方法,AAA");}
}
package cn.itcast.day10.demo01;public class MyInterfaceDefaultB implements MyInterfaceDefault {
    @Overridepublic void methodAbs() {
    System.out.println("实现了抽象方法,BBB");}@Overridepublic void methodDefault() {
    System.out.println("实现类B覆盖重写了接口的默认方法");}
}
package cn.itcast.day10.demo01;/* 1. 接口的默认方法,可以通过接口实现类对象,直接调用。 2. 接口的默认方法,也可以被接口实现类进行覆盖重写。*/
public class Demo02Interface {
    public static void main(String[] args) {
    // 创建了实现类对象MyInterfaceDefaultA a = new MyInterfaceDefaultA();a.methodAbs(); // 调用抽象方法,实际运行的是右侧实现类。// 调用默认方法,如果实现类当中没有,会向上找接口a.methodDefault(); // 这是新添加的默认方法System.out.println("==========");MyInterfaceDefaultB b = new MyInterfaceDefaultB();b.methodAbs();b.methodDefault(); // 实现类B覆盖重写了接口的默认方法}}

默认方法可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。

静态方法的使用

package cn.itcast.day10.demo01;/* 从Java 8开始,接口当中允许定义静态方法。 格式: public static 返回值类型 方法名称(参数列表) {方法体 } 提示:就是将abstract或者default换成static即可,带上方法体。*/
public interface MyInterfaceStatic {
    public static void methodStatic() {
    System.out.println("这是接口的静态方法!");}}
package cn.itcast.day10.demo01;public class MyInterfaceStaticImpl implements MyInterfaceStatic {
    
}
package cn.itcast.day10.demo01;/* 注意事项:不能通过接口实现类的对象来调用接口当中的静态方法。 正确用法:通过接口名称,直接调用其中的静态方法。 格式: 接口名称.静态方法名(参数);*/
public class Demo03Interface {
    public static void main(String[] args) {
    // 创建了实现类对象MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl();// 错误写法!
// impl.methodStatic();// 直接通过接口名称调用静态方法MyInterfaceStatic.methodStatic();}}

静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用

私有方法的使用

package cn.itcast.day10.demo01;/* 问题描述: 我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。 但是这个共有方法不应该让实现类使用,应该是私有化的。解决方案: 从Java 9开始,接口当中允许定义私有方法。 1. 普通私有方法,解决多个默认方法之间重复代码问题 格式: private 返回值类型 方法名称(参数列表) {方法体 }2. 静态私有方法,解决多个静态方法之间重复代码问题 格式: private static 返回值类型 方法名称(参数列表) {方法体 }*/
public interface MyInterfacePrivateA {
    public default void methodDefault1() {
    System.out.println("默认方法1");methodCommon();}public default void methodDefault2() {
    System.out.println("默认方法2");methodCommon();}private void methodCommon() {
    System.out.println("AAA");System.out.println("BBB");System.out.println("CCC");}}
package cn.itcast.day10.demo01;public interface MyInterfacePrivateB {
    public static void methodStatic1() {
    System.out.println("静态方法1");methodStaticCommon();}public static void methodStatic2() {
    System.out.println("静态方法2");methodStaticCommon();}private static void methodStaticCommon() {
    System.out.println("AAA");System.out.println("BBB");System.out.println("CCC");}}
package cn.itcast.day10.demo01;public class Demo04Interface {
    public static void main(String[] args) {
    MyInterfacePrivateB.methodStatic1();MyInterfacePrivateB.methodStatic2();// 错误写法!
// MyInterfacePrivateB.methodStaticCommon();}}

常量的定义和使用

package cn.itcast.day10.demo01;/* 接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。 从效果上看,这其实就是接口的【常量】。 格式: public static final 数据类型 常量名称 = 数据值; 备注: 一旦使用final关键字进行修饰,说明不可改变。注意事项: 1. 接口当中的常量,可以省略public static final,注意:不写也照样是这样。 2. 接口当中的常量,必须进行赋值;不能不赋值。 3. 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)*/
public interface MyInterfaceConst {
    // 这其实就是一个常量,一旦赋值,不可以修改public static final int NUM_OF_MY_CLASS = 12;}

1.4 接口问题小结

在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.5 接口的多实现

package cn.itcast.day10.demo02;/* 使用接口的时候,需要注意:1. 接口是没有静态代码块或者构造方法的。 2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。 格式: public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB {// 覆盖重写所有抽象方法 } 3. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。 4. 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。 5. 如果实现类锁实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。 6. 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。 7. 7接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。*/
public class Demo01Interface {
    public static void main(String[] args) {
    Zi zi = new Zi();zi.method();}}

1.6接口的多继承

package cn.itcast.day10.demo03;/* 1. 类与类之间是单继承的。直接父类只有一个。 2. 类与接口之间是多实现的。一个类可以实现多个接口。 3. 接口与接口之间是多继承的。注意事项: 1. 多个父接口当中的抽象方法如果重复,没关系。 2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,【而且带着default关键字】。*/
public class Demo01Relations {
    
}

接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
接口中,没有构造方法,不能创建对象。
接口中,没有静态代码块。

2.多态

2.1 多态的概念

在这里插入图片描述

2.2 多态的格式

package cn.itcast.day10.demo04;/* 代码当中体现多态性,其实就是一句话:父类引用指向子类对象。格式: 父类名称 对象名 = new 子类名称(); 或者: 接口名称 对象名 = new 实现类名称();*/
public class Demo01Multi {
    public static void main(String[] args) {
    // 使用多态的写法// 左侧父类的引用,指向了右侧子类的对象Fu obj = new Zi();obj.method();obj.methodFu();}
}

2.3 多态中成员变量和成员方法的使用特点

package cn.itcast.day10.demo05;public class Fu /*extends Object*/ {
    int num = 10;public void showNum() {
    System.out.println(num);}public void method() {
    System.out.println("父类方法");}public void methodFu() {
    System.out.println("父类特有方法");}}
package cn.itcast.day10.demo05;public class Zi extends Fu {
    int num = 20;int age = 16;@Overridepublic void showNum() {
    System.out.println(num);}@Overridepublic void method() {
    System.out.println("子类方法");}public void methodZi() {
    System.out.println("子类特有方法");}
}
package cn.itcast.day10.demo05;/**/
public class Demo01MultiField {
    public static void main(String[] args) {
    // 使用多态的写法,父类引用指向子类对象Fu obj = new Zi();System.out.println(obj.num); // 父:10
// System.out.println(obj.age); // 错误写法!System.out.println("=============");// 子类没有覆盖重写,就是父:10// 子类如果覆盖重写,就是子:20obj.showNum();}}

访问成员变量的两种方式:

  1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
  2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
package cn.itcast.day10.demo05;/**/
public class Demo02MultiMethod {
    public static void main(String[] args) {
    Fu obj = new Zi(); // 多态obj.method(); // 父子都有,优先用子obj.methodFu(); // 子类没有,父类有,向上找到父类// 编译看左边,左边是Fu,Fu当中没有methodZi方法,所以编译报错。
// obj.methodZi(); // 错误写法!}}

在多态的代码当中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找。

口诀:编译看左边,运行看右边。

对比一下:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。

2.4 多态的好处

在这里插入图片描述

2.5 对象的上下转型

在这里插入图片描述

package cn.itcast.day10.demo06;public abstract class Animal {
    public abstract void eat();}
package cn.itcast.day10.demo06;public class Cat extends Animal {
    @Overridepublic void eat() {
    System.out.println("猫吃鱼");}// 子类特有方法public void catchMouse() {
    System.out.println("猫抓老鼠");}
}
package cn.itcast.day10.demo06;public class Dog extends Animal {
    @Overridepublic void eat() {
    System.out.println("狗吃SHIT");}public void watchHouse() {
    System.out.println("狗看家");}
}
package cn.itcast.day10.demo06;/* 向上转型一定是安全的,没有问题的,正确的。但是也有一个弊端: 对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。解决方案:用对象的向下转型【还原】。*/
public class Demo01Main {
    public static void main(String[] args) {
    // 对象的向上转型,就是:父类引用指向之类对象。Animal animal = new Cat(); // 本来创建的时候是一只猫animal.eat(); // 猫吃鱼// animal.catchMouse(); // 错误写法!// 向下转型,进行“还原”动作Cat cat = (Cat) animal;cat.catchMouse(); // 猫抓老鼠// 下面是错误的向下转型// 本来new的时候是一只猫,现在非要当做狗// 错误写法!编译不会报错,但是运行会出现异常:// java.lang.ClassCastException,类转换异常Dog dog = (Dog) animal;}}

为什么要转型
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。

package cn.itcast.day10.demo06;/* 如何才能知道一个父类引用的对象,本来是什么子类? 格式: 对象 instanceof 类名称 这将会得到一个boolean值结果,也就是判断前面的对象能不能当做后面类型的实例。*/
public class Demo02Instanceof {
    public static void main(String[] args) {
    Animal animal = new Dog(); // 本来是一只狗animal.eat(); // 狗吃SHIT// 如果希望掉用子类特有方法,需要向下转型// 判断一下父类引用animal本来是不是Dogif (animal instanceof Dog) {
    Dog dog = (Dog) animal;dog.watchHouse();}// 判断一下animal本来是不是Catif (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();}}}

2.6多态小例子

package cn.itcast.day10.demo07;public interface USB {
    public abstract void open(); // 打开设备public abstract void close(); // 关闭设备}
package cn.itcast.day10.demo07;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(); // 关闭设备}}
package cn.itcast.day10.demo07;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(); // 关闭设备}}
package cn.itcast.day10.demo07;// 键盘就是一个USB设备
public class Keyboard implements USB {
    @Overridepublic void open() {
    System.out.println("打开键盘");}@Overridepublic void close() {
    System.out.println("关闭键盘");}public void type() {
    System.out.println("键盘输入");}
}
package cn.itcast.day10.demo07;public class DemoMain {
    public static void main(String[] args) {
    // 首先创建一个笔记本电脑Computer computer = new Computer();computer.powerOn();// 准备一个鼠标,供电脑使用
// Mouse mouse = new Mouse();// 首先进行向上转型USB usbMouse = new Mouse(); // 多态写法// 参数是USB类型,我正好传递进去的就是USB鼠标computer.useDevice(usbMouse);// 创建一个USB键盘Keyboard keyboard = new Keyboard(); // 没有使用多态写法// 方法参数是USB类型,传递进去的是实现类对象computer.useDevice(keyboard); // 正确写法!也发生了向上转型// 使用子类对象,匿名对象,也可以
// computer.useDevice(new Keyboard()); // 也是正确写法computer.powerOff();System.out.println("==================");method(10.0); // 正确写法,double --> doublemethod(20); // 正确写法,int --> doubleint a = 30;method(a); // 正确写法,int --> double}public static void method(double num) {
    System.out.println(num);}}