生活中:
接口就是一种公共的规范标准
只要符合规范标准,就可以大家通用。
在代码中,也是如此.......
接口是一种引用数据类型
数据类型有两种:基本数据类型(4类8种),引用数据类型(接口、字符串、)
其中接口中最重要的内容就是其中的:抽象方法
如何定义一个接口的格式:
public interface 接口名称{
//接口内容
}
接口名称取名方式和类一样
备注: 换成了关键字interface之后,编译生成的字节码文件仍然是:.java-->.class
不会编程什么 .interface
接口的源代码还是.java,字节码文件还是.class
如果是Java7,那么接口中可以包含的内容有:
1.常量(不可改变的量)
2.抽象方法
如果是Java8,还可以额外包含有:
3.默认方法
4.静态方法
如果是Java9,还可以额外包含有:
5.私有方法
在任何版本的Java中,接口都能定义抽象方法
public absctract 返回值类型 方法名称(参数列表);
注意事项:
(1)、接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
如上图,public abstract是一种颜色,说明是定义抽象方法的一套关键字。
(2)、其实这两个关键字修饰符,可以选择性地省略(但是刚学的时候尽量不要省略,后面学会了再省略)
(1)、方法的三要素,可以随意定义。
接口使用步骤:
1.接口不能直接使用,必须有一个“实现类”来“实现”该接口。(这里的"实现类"相当于子类,"实现"相当于继承extends)
public class 实现类名称 implements 接口名称{
//....
}
2.接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
实现:去掉abstract关键字,加上方法体大括号。
3.创建实现类的对象,进行使用。
注意事项:
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。
抽象方法所在的类,必须是抽象类。
从Java 8 开始,接口里允许定义默认方法。
格式:
public default 返回值类型 方法名称(参数列表){
//方法体
}
备注:接口中的默认方法,可以解决接口升级的问题。
默认方法解决了接口升级问题,接口升级后,接口里的抽象方法增加了,此时如果接口的实现类不实现新的方法,就会报错,但是想想,接口的实现类太多了,如果每个实现类都去改,会很麻烦的。如果不想修改实现类,那么就必须再接口中升级时换用默认方法,而不是一般抽象方法。
我们可以看到,public 是灰色的,可以不用谢。但是default 不能不写。
默认方法会被你的实现类继承下去的。
由如上图可以看出,默认方法已经得以使用,但是我们自从再接口中写了默认方法后,并没有到实现类当中去实现,而是再测试类当中直接调用。
1. 接口的默认方法,可以通过接口实现类对象,直接调用。
2. 接口的默认方法,也可以被接口实现类进行覆盖重写。(这个时候覆盖重写)
从Java 8开始,接口当中允许定义静态方法。
格式:
public static 返回值类型 方法名称(参数列表) {
方法体
}
提示:就是将abstract或者default换成static即可,带上方法体。
一个类可以实现多个接口,所以呢,一个类使用这么多个接口当中的静态方法有可能会产生冲突。所以不能通过接口实现类的对象来调用接口当中的静态方法。
正确用法:接口名称.静态方法名(参数);
想想,再驱动类中,用类对象并不能去调用类中的静态方法,而是要用类名调用,同理,接口中,调用接口中的静态方法,并不能通过接口的实现类对象去调用接口中的静态方法,而是要通过接口名直接去调用接口中的静态方法。
如果由方法体之间重复的方法体太多了,我们就需要抽取
抽取出来
问题描述:
我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。
但是这个共有方法不应该让实现类使用,应该是私有化的。
解决方案:
从Java 9开始,接口当中允许定义私有方法。
1. 普通私有方法,解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表) {
方法体
}
2. 静态私有方法,解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表) {
方法体
}
故,如上方法亦是错的。如下方法是对的。让第三个方法只在前两个方法通用,而不在外界使用。
另外,还有就是静态的方法
如上,静态方法,这个时候就不能在驱动类中利用对象去调用这个静态方法,或者在驱动类中直接用接口调用此方法,都不行。
所以达到很好的替换,只是在接口中使用。
接口当中也可以定义“成员变量”,但是必须使用 public static finnal 三个关键字进行修饰
从效果上看,这其实就是接口的【常量】
格式:public static final 数据类型 常量名称 = 数据值;
其中public 代表大家都能随便用的意思,不管是接口里面还是接口外面,或者实现类当中,都可以用。
既然static 都用上了,说明和对象是没有关系的。所以直接使用接口调用。其中final指常量不可变。
如果不赋值,就会报错。
原则上,成员变量如果不复制,是有默认值。当然,对于int 来说,默认值就是0
但是你想想,有final 存在,说明就必须要有赋值,且不能为零。如果赋初值为0,那final 就没什么用了。所以一旦写上 final 就必须手动进行赋值。
常量使用方法:System.out.println(接口名.常量名 );
在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.接口是没有静态代码块或构造方法。
你想,如果你能写构造方法,说明可以new 来创建对象,但事实上,接口并不能new
2.一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
格式:
public class MyInterfaceImpl implement MyInterfaceA,MyInterfaceB{
//覆盖重写所有的抽象方法
}
如上说明了,Java中任何类都是Object直接或者间接的子类,没有写,就好比省略了 extends Object
所以接口也是直接父类只能有一个。
3. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
就好比,你爸让你去吃饭,你妈也让你去吃饭,所以你去吃饭就好了(一次就好),没必要吃两次饭。
4. 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。(前面加上 abstract)
5. 如果实现类所实现的多个接口当中,存在重复的默认方法。
显然,两个默认方法中方法体不一样,所以到底应该选哪个呢?
所以,实现类一定要对冲突的默认方法进行覆盖重写。
6. 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。
首先刚刚说了,接口中方法冲突,一定要覆盖重写。但是现在如下:
上面接口和父类方法冲突,但是在子类继承父类并且实现接口,如下,并没有报错
没有错,那是因为在这种情况下,是优先父类
在Java当中,继承是要优先于接口实现
所以这是有优先级
接口之间的多继承
1. 类与类之间是单继承的,直接父类只有一个。
2. 类于接口之间是多实现的,一个类可以实现多个接口。
3. 接口与接口之间是多继承的。
注意事项:
1. 多个父接口当中的抽象方法如果重复,没关系。
2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法覆盖重写。【而且带着 default 关键字】
接口当中的 default 关键字是不能省略的。
public interface MyInterfaceA { public abstract void methodA(); public abstract void methodCommon(); public default void methodDefault() { System.out.println("AAA"); } }
public interface MyInterfaceB { public abstract void methodB(); public abstract void methodCommon(); public default void methodDefault() { System.out.println("BBB"); } }
/* 这个子接口当中有几个方法?答:4个。 methodA 来源于接口A methodB 来源于接口B methodCommon 同时来源于接口A和B method 来源于我自己 */
public interface MyInterface extends MyInterfaceA,MyInterfaceB{ @Override default void methodDefault() { } }
public class MyInterfaceImpl implements MyInterface { @Override public void methodDefault() { System.out.println("Default"); } @Override public void methodA() { System.out.println("AAA"); } @Override public void methodB() { System.out.println("BBB"); } @Override public void methodCommon() { System.out.println("Common"); } }
public class Demo01Interface { public static void main(String[] args) { Zi zi = new Zi(); zi.method(); } }
多态的概述(面向对象的第三个特性):
多态,不是说有多种状态,而是说有多种形态。
Polymorphism 多态的英文单词,这个单词太长了,所以我们一般就叫 Multi 就得了
代码当中体现多态性,其实就是一句话:父类引用指向子类对象。
格式:
父类名称 对象名 = new 子类名称 ( ) ; //父类引用指向子类对象
当然,如果没有父子类关系,而是接口与接口实现类关系。那就如下:
接口名称 对象名 = new 实现类名称 ( ) ;
extends 和 implement 都能提现多态性的前提。
和之前一样,我们看右边 new 的谁,就调用谁的成员方法。
所以:
其中在多态中,右侧子类对象被当作父类使用,如同一只猫被当作动物来看待,没有什么问题,因为子类就是父类,这也体现了面向对象的多态性。
只有方法能够覆盖重写,成员变量不能够覆盖重写。
多态中成员变量的使用特点:
多态中成员方法的使用特点 :
使用多态的好处:
为什么我们推荐用多态的写法呢??
对象的上下转型:
父类引用指向子类对象
类转换异常
向上转型一定是安全的,没有问题的,正确的。但是也有一个弊端: 对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。 解决方案:用对象的向下转型【还原】。 */ 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; } }
用 instanceof 关键字进行类型判断:
/* 如何才能知道一个父类引用的对象,本来是什么子类? 格式: 对象 instanceof 类名称 这将会得到一个boolean值结果,也就是判断前面的对象能不能当做后面类型的实例。 */ public class Demo02Instanceof { public static void main(String[] args) { Animal animal = new Dog(); animal.eat(); // 如果希望掉用子类特有方法,需要向下转型 // 判断一下父类引用animal本来是不是Dog // if(animal instanceof Dog){ // Dog dog = (Dog) animal; // dog.watchHouse(); // } // 判断一下animal本来是不是Cat // if(animal instanceof Cat){ // Cat cat = (Cat)animal; // cat.catchMouse(); // } giveMePet(new Dog()); } public static void giveMePet(Animal animal){ if(animal instanceof Dog){ Dog dog = (Dog) animal; dog.watchHouse(); } if(animal instanceof Cat){ Cat cat = (Cat)animal; cat.catchMouse(); } } }
笔记本电脑案例分析:
Computer.java
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();//关闭设备 } }
Mouse.java
// 鼠标就是一个USB 设备 注意,笔记本电脑并不是USB设备,笔记本电脑只是会用到USB设备,而鼠标键盘才是USB设备 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("鼠标点击"); } }
Keyboard.java
// 键盘就是一个USB 设备 注意,笔记本电脑并不是USB设备,笔记本电脑只是会用到USB设备,而鼠标键盘才是USB设备 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("键盘输入"); } } // 笔记本电脑和 键盘、鼠标 中间是通过接口交互的
USB 接口
package cn.itcast.day10.demo07; public interface USB { public abstract void open();//打开设备 public abstract void close();//关闭设备 }
DemoMain.java
public class DemoMain { public static void main(String[] args) { //首先创建一个笔记本电脑 Computer computer = new Computer(); computer.powerOn(); //准备一个鼠标供电脑使用 // Mouse mouse = new Mouse(); //首先进行向上转型 // USB usb = (USB) mouse; USB usbmouse = new Mouse(); computer.useDevice(usbmouse); //准备一个键盘供电脑使用 // Keyboard keyboard = new Keyboard(); //首先向上转型 //USB usb1 = (USB) keyboard; // USB usbkeyboard = new Keyboard(); // computer.useDevice(usbkeyboard); computer.powerOff(); } }
首先 主要是电脑发挥功能,
电脑基础功能:开机 powerOn ( ) 关机 powerOff ( )
电脑控制USB设备功能:useDevice (USB usb)
其次是USB接口:
public abstract void open();//打开设备
public abstract void close();//关闭设备
最后是,鼠标和键盘
鼠标:public class Mouse implements USB
键盘:public class Keyboard implements USB
在鼠标和键盘中都拥有打开和关闭功能。这两个功能是覆盖重写了接口的打开和关闭功能。
另外,鼠标和键盘各自又有其独特的功能,鼠标点击,键盘输入
鼠标点击:click()
键盘输入:type()
各功能都介绍了
那么现在就来到,DemoMain.java 主驱动类
首先,我们是要用电脑来控制
所以我们是需要创建电脑对象
Computer computer = new Computer();
这个时候可以直接对电脑进行打开及关闭 computer.powerOn(); computer.powerOff();
现在我们要去控制键盘、鼠标,怎么去控制呢?我们需要通过电脑用接口去控制鼠标和键盘。
电脑 -- > USB接口 --> USB设备
所以我们在电脑上,写了这么一个方法:
//使用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();//关闭设备
}
在 useDevice(USB usb) 方法中,参数是接口
其实如果进来的不是USB类型,也会强制转换为USB类型,也就是相当于 Mouse -- > USB(Mouse)
因为在进行判断是时,已经被强制转换为USB类型,这个时候,就需要判断原来到底是什么类型
通过 if(usb instanceof Mouse) 或者 if(usb instanceof Keyboard) 通过这两个判断,就可以判断出,进来之前的引用类型是什么。
比如进来之前,是Mouse 进来后,通过useDevice(USB usb) 之后,就变成 USB 类型的了。
所以检测出来原身后,我们就用相应的对象去调用
比如,原身是Mouse,我们就直接 向下(转回去)转型:Mouse mouse = (Mouse) usb;
转型回来后,我们就直接调用他的独特方法。
然而 useDevice(USB usb) 在我的理解中(个人理解),可以当封装来看,还有就是本身有一个向上转型 Mouse --> USB
从 鼠标、键盘 到 USB 类型的转换,作为向上转换。
就好比:从猫转换到动物,更抽象,但是后面要去调用猫独特的方法,还必须得从动物向下转型回到猫。