【接口、多态】
接口
接口概述和生活举例
生活中的接口举例:电源插座当中的电源接口。
接口就是一种公共的规范标准。
只要符合规范标准,就可以大家通用。
在计算机中,有USB接口。只要符合USB接口规范,那么各种设备都可以使用。
U盘、打印机、夜读灯、USB小风扇…
接口的定义基本格式
接口就是多个类的公共规范,是一种引用数据类型,最重要的内容就是其中的:抽象方法。
接口的格式:
public Interface 接口名称{
//接口内容
}
备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --> .class。
如果是Java 7,那么接口中可以包括的内容有:常量、抽象方法。
如果是Java 8,还可以额外包含有:默认方法、静态方法。
如果是Java 9,还可以额外包含有:私有方法。
接口的抽象方法
接口的抽象方法定义
在任何版本的Java中,接口都能定义抽象方法。
格式:
public abstract 返回值类型 方法名称(参数列表);
注意事项:
- 1.接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract。
- 2.这两个关键字修饰符,可以选择性地省略。
- 3.方法的三要素,可以随意定义。
public interface MyInterfaceAbstract {
//这是一个抽象方法
public abstract void methodAbstract1();
//这也是一个抽象方法
public String methodAbstract2();
//这也是一个抽象方法
abstract int methodAbstract3(int num);
//这也是一个抽象方法
boolean methodAbstract3(String str);
}
接口的抽象方法使用
接口使用的步骤:
-
1.接口不能直接使用,必须有一个“实现类”来“实现”该接口。
- 格式:
public class 实现类名称 implements 接口名称{
//…
}
-
2.接口的实现类必须覆盖重写(实现)接口中的所有抽象方法。
- 实现:去掉abstract关键字,加上方法体大括号及内容
-
3.创造实现类的对象,进行使用。
注意事项:
如果实现类中没有对接口中的所有抽象方法进行实现,那么这个实现类必须是抽象类,否则会报错。
public interface MyInterfaceAbstract {
//这是一个抽象方法
public abstract void methodAbstract1();
}
public class MyInterfaceAbstractImpl implements MyInterfaceAbstract {
@Override
public void methodAbstract1(){
System.out.println("抽象方法1实现");
}
}
public class Interface01 {
public static void main(String[] args) {
MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
impl.methodAbstract1();//抽象方法1实现
}
}
接口的默认方法
接口的默认方法定义
从Java 8开始,接口里允许定义默认方法。
格式:
public default 返回值类型 方法名称(参数列表){
方法体
}
备注:接口当中的默认方法,可以解决接口升级的问题。
接口的默认方法的使用
- 1.接口的默认方法,可以通过接口实现类对象,直接调用
- 2.接口的默认方法,也可以被接口实现类进行覆盖重写
接口的静态方法
接口的静态方法定义
从Java 8开始,接口里允许定义静态方法。
格式:
public static 返回值类型 方法名称(参数列表){
方法体
}
提示:就是将abstract或者default换成static即可,带上方法体。
接口的静态方法使用
**注意事项:**不能通过接口实现类的对象来调用接口当中的静态方法。
**正确用法:**通过接口名称,直接调用其中的静态方法。
格式:
接口名称.静态方法();
public interface MyInterfaceStatic {
public static void methodStatic(){
System.out.println("这是接口的静态方法!");
}
}
public class Interface02 {
public static void main(String[] args) {
MyInterfaceStatic.methodStatic();
}
}
接口的私有方法定义及内部使用
问题描述:
我们需要抽取一个公有方法,用来解决两个默认方法之间重复代码问题。
但是这个共有方法不应该让实现类使用,应该是私有化的。
解决方案:
从Java 9开始,接口当中允许定义私有方法。
1.**普通私有方法:**解决多个默认方法之间重复代码的问题
格式:
private 返回值类型 方法名称(参数类型){
方法体
}
2.**静态私有方法:**解决多个静态方法之间代码重复的问题
格式:
private static 返回值类型 方法名称(参数类型){
方法体
}
public interface MyInterfacePrivate {
private void methodPrivateForDefault() {
System.out.println("默认方法的私有方法");
}
public default void methodDefault1() {
System.out.println("默认方法1");
methodPrivateForDefault();
}
public default void methodDefault2() {
System.out.println("默认方法2");
methodPrivateForDefault();
}
private static void methodPrivateForStatic(){
System.out.println("静态方法的默认方法");
}
public static void methodStatic1(){
System.out.println("静态方法1");
methodPrivateForStatic();
}
public static void methodStatic2(){
System.out.println("静态方法2");
methodPrivateForStatic();
}
}
接口的常量定义和使用
接口当中也可以定义"成员变量",但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
格式:
public static final 数据类型 常量名称 = 数据值;
备注:一旦使用final关键字修饰,说明不可改变。
注意事项:
- 1.接口当中的常量,可以忽略public static final,注意:不写也照样是这样。
- 2.接口当中的常量,必须进行赋值,不能不赋值。
public interface MyInterfaceConst {
public static int NUM = 12;
}
public class Interface03 {
public static void main(String[] args) {
System.out.println(MyInterfaceConst.NUM);//12
}
}
接口内容小结
在Java 9+版本中,接口的内容可以有:
- 1.成员变量其实是常量,格式:
- public static final 数据类型 常量名称 = 数据值;
- 注意:
- 常量必须进行赋值,而且一旦赋值不能改变;
- 变量名称完全大写,用下划线将每个单词进行分隔。
- 2.接口中最重要的就是抽象方法,格式:
- public abstract 返回值类型 方法名称(参数列表);
- 注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。
- 3.接口里允许定义默认方法,格式:
- public default 返回值类型 方法名称(参数列表){方法体}
- 注意:默认方法也可以被覆盖重写。
- 4.接口里允许定义静态方法,格式:
- public static 返回值类型 方法名称(参数列表){方法体}
- 注意:应该通过接口名称进行赋值,不能通过实现类对象调用接口静态方法。
- 5.接口里允许定义私有方法,格式:
- 普通私有方法:private 返回值类型 方法名称(参数列表){方法体}
- 静态私有方法:private static 返回值类型 方法名称(参数列表){方法体}
- 注意:private的方法只有接口自己才能使用,不能被实现类或别人调用。
接口注意事项
-
1.接口是没有静态代码块或者构造方法的。
-
2.一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
-
格式:
public class 实现类名称 implements 接口名称1,接口名称2…{
//覆盖重写所有抽象方法
}
-
-
3.如果实现类所实现的多个接口当中,存在你重复的抽象方法,那么只需要覆盖重写一次即可。
-
4.如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类必须是抽象类。
-
5.如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
-
6.一个类如果直接父类当中的方法,与接口当中的默认方法产生了冲突,优先用父类当中的方法。
接口之间的多继承
- 1.类与类之间是单继承的,直接父类只有一个。
- 2.类与接口之间是多实现的。一个类可以实现多个接口。
- 3.接口与接口之间是多继承的。
- 注意事项:
- 1.多个父接口当中的抽象方法如果重复,没有关系。
- 2.多个父接口当中的默认方法如果重复,继承的子接口必须对默认方法覆盖重写。
- 注意事项:
public interface InterfaceFu1 {
public abstract void methodA();
public abstract void method();
public default void methodDefault(){
}
}
public interface InterfaceFu2 {
public abstract void methodB();
public abstract void method();
public default void methodDefault(){
}
}
public interface InterfaceZi extends InterfaceFu1,InterfaceFu2 {
// 现在,InterfaceZi接口中,有三个继承下来的抽象方法:methodA、methodB、method;
// 有一个自己创建的抽象方法:methodC;
// 对父接口中继承的重名的默认方法进行了覆盖重写。
public abstract void methodC();
@Override
default void methodDefault() {
}
}
多态
面向对象的三大特征:封装性、继承性、多态性。
extends继承或者implements实现,是多态性的前提。
(类与类之间的继承,接口与接口之间的继承,类与接口之间的实现等。)
小明是一个学生,但同时也是一个人。
小明是一个对象,这个对象既有学生形态,也有人类形态。
一个对象拥有多种形态,这就是:对象的多态性。
多态的格式和使用
代码当中体现多态性,其实就是一句话,父类引用指向子类对象。
格式:
父类名称 对象名 = new 子类名称();
或者:
父类接口 对象名 = new 实现类名称();
public class Fu {
public void mtehod(){
System.out.println("父类成员方法");
}
public void methodFu(){
System.out.println("父类成员方法");
}
}
public class Zi extends Fu {
@Override
public void mtehod() {
System.out.println("子类成员方法");
}
}
public class Polymorphism {
public static void main(String[] args) {
Fu obj = new Zi();//体现了多态性
obj.mtehod();//子类成员方法
obj.methodFu();//父类成员方法
}
}
多态中成员变量的使用特点
- 直接通过子类对象访问成员变量
- 创建时等号左边是谁,就优先用谁,没有则向上(向父类)找
- 间接通过成员方法访问成员变量
- 方法属于谁,就优先用谁,没有则向上找
多态中成员方法的使用特点
在多态的代码当中,访问成员方法的规则:
- 创建的对象是谁,就优先用谁的方法,没有就向上找
使用多态的好处
假设有员工类(Employee),其中有一个名叫work的抽象方法。
创建员工类的两个子类,一个子类叫讲师类(Teacher),对work抽象方法进行实现,方法体为:讲课。
另一个子类叫做助教类(Assistant),对work抽象方法进行实现,方法体为:辅导。
如果不用多态,只用子类,那么写法为:
Teacher teacher = new Teacher();
teacher.work();//讲课
Assistant assistant = new Assistant();
assistant.work();//辅导
我现在唯一想要做的事,就是调用work方法,其他的功能不关心。
如果使用多态的写法,对比一下:
Employee teacher = new Teacher();
teacher.work();//讲课
Employee assistant = new Assistant();
assistant.work();//辅导
好处,无论右边new的时候换成哪个子类对象,等号左边调用方法都不会改变。
对象的向上转型
对象的向上转型,其实就是多态写法:
格式:父类名称 对象名称 = new 子类名称(); 例如:Animal animal = new Cat();
含义:右侧创建了一个子类的对象,把它当做父类来看待使用 创建了一只猫,当做动物看待
注意事项:**向上转型一定是安全的。**从小范围转向了大范围,从小范围的猫,向上转换成为更大范围的动物。
对象的向下转型
对象的向上转型一定是安全的,没有问题的,正确的。但是也有一个弊端:
对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。
解决方案:用对象的向下转型【还原】。
对象的向下转型,其实是一个【还原】的动作。
格式:子类名称 对象名称 = (子类名称)父类对象;
含义:将父类对象,【还原】成为本来的子类对象。
public abstract class Animal {
abstract void eat();
}
public class Cat extends Animal {
@Override
public void eat(){
System.out.println("猫吃鱼!");
}
public void catchMouse(){
System.out.println("猫抓老鼠!");
}
}
public class Main01 {
public static void main(String[] args) {
Animal animalCat = new Cat();//本来是猫,向上转型成为动物。
animalCat.eat();//猫吃鱼!
Cat cat = (Cat)animalCat;//本来是猫,已经被当做动物了,还原回来成为本来的猫。
cat.catchMouse();//猫抓老鼠!
}
}
注意事项:
- 1.必须保证对象本来创建的时候,就是猫,才能向下转型成为猫;
- 2.如果对象创建的时候本来不是猫,现在非要向下转型为猫,就会报错。
关键字instanceof
如何才能知道 一个父类引用的对象,本来是什么子类?
格式:
对象 instanceof 类名称
这将会得到一个boolean值结果,也就是判断前面的对象能不能当做后面类型的实例。
public class Instanceof01 {
public static void main(String[] args) {
Animal animalCat = new Cat();//本来是一只猫
animalCat.eat();//猫吃鱼
//判断一下父类引用animalCat本来是不是Cat
if(animalCat instanceof Cat){
Cat cat = (Cat)animalCat;
cat.catchMouse();//猫抓老鼠
}
}
}
笔记本USB接口案例
//创建USB接口
public interface USB {
public abstract void openDevice();
public abstract void closeDevice();
}
//创建USB接口的键盘实现类
public class Keyboard implements USB {
@Override
public void openDevice() {
System.out.println("打开键盘设备");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘设备");
}
//键盘特有的方法
public void type(){
System.out.println("键盘打字输入");
}
}
//创建USB接口的鼠标实现类
public class Mouse implements USB {
@Override
public void openDevice() {
System.out.println("打开鼠标设备");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标设备");
}
//鼠标特有的方法
public void click(){
System.out.println("鼠标点击输入");
}
}
//创建笔记本类
public class Computer {
public void openComputer(){//电脑的开机,成员方法
System.out.println("开机");
}
public void closeComputer(){//电脑的关机,成员方法
System.out.println("关机");
}
public void useDevice(USB usb){//电脑的使用USB设备,成员方法
usb.openDevice();
if (usb instanceof Keyboard){//先判断
Keyboard keyboard = (Keyboard) usb;//向下转型为键盘类
keyboard.type();
}else if(usb instanceof Mouse){
Mouse mouse = (Mouse) usb;//向下转型为鼠标类
mouse.click();
}
usb.closeDevice();
}
}
//创建Computer类的对象
public class MyComputer {
public static void main(String[] args) {
Computer myComputer = new Computer();//创建电脑对象
myComputer.openComputer();//开机
USB mouse = new Mouse();//对象向上转型
myComputer.useDevice(mouse);
//打开鼠标设备
//鼠标点击输入
//关闭鼠标设备
USB keyboard = new Keyboard();//对象向上转型
myComputer.useDevice(keyboard);
//打开键盘设备
//键盘打字输入
//关闭键盘设备
myComputer.closeComputer();//关机
}
}