【 接口、多态 】
迪米特法则:主要解决重复代码问题。(了解即可)
解决的问题:
接口升级,解决实现类需要覆盖重写。
接口的格式:
public interface 接口名称{
//接口内容
}
虽然使用了interface关键字
,但是编译后产生的的字节码文件仍然是.class文件
接口当中的组成部分。
定义接口的基本格式(外层框架)
public interface 接口名称{
1、常量
2、抽象方法
3、默认方法 // java8
4、静态方法 // java8
5、私有方法 // java9
}
抽象方法的定义格式:
public abstract 返回值类型 方法名称(参数列表);
备注:
1、public abstract两个关键字在接口当中可以省略,就算不写,也照样是public abstract
2、对于方法的三要素,没有要求。
使用接口当中的抽象方法:
- 1、不能直接new接口
- 2、必须有一个实现类,去实现接口
public class 实现类名称 implements 接口名称{
// .....
}
- 3、实现类当中必须覆盖重写所有的抽象方法。
- 4、创建实现类对象进行使用。
从java8开始,接口里面可以定义默认方法:
public defalut 返回值类型 方法名称(参数列表){
// 方法体
}
备注:
- 1、public关键字可以省略,就算不写,也照样是public的。但是default关键字不能省略
- 2、这是Java 8特性之一。
- 3、接口当中的默认方法一般不强制要求覆盖重写。
- 4、然而实现类也是可以对默认方法进行覆盖重写的。
java 8还允许接口当中定义静态方法:
如果一个方法和对象无关,那么就可以定义成静态方法。
格式:
public static 返回值类型 方法名称(参数列表){
方法体
}
备注:
public可以省略,static不能省略。
使用格式:
接口名称.静态方法名(参数);
注意:
接口当中的静态方法使用,不能通过实现类对象调用。
从java9开始,接口当中允许定义私有方法:
public interface 接口名称{
// 普通的私有方法:
private 返回值类型 方法名称(参数列表){
方法体
}
// 静态私有方法
private static 返回值类型 方法名称(参数列表){
方法体
}
}
接口当中可以包含常量。
接口中常量的格式:
public static final 数据类型 常量名称 = 数据值;
注意事项:
- 1、public static final三个关键字都可以省略,也可以省略其中的任意多个。
- 2、final关键字代表“最终”,不可改变的意思。
- 3、接口当中的常量,必须进行赋值,不能不赋值。
- 4、接口当中的常量,命名规则为:所有的字母一律大写,中间使用下划线分割。
- 5、使用接口中常量的格式:
接口名称.常量名
格式总结:
常量:
public static final 数据类型 常量名称 = 数据值;
抽象方法:
public abstract 返回值类型 方法名称(参数列表);
默认方法:
public default 返回值类型 方法名称(参数列表){
//方法体
}
静态方法:
public static 返回值类型 方法名称(参数列表){
//方法体
}
私有方法;
private static 返回值类型 方法名称(参数列表){
//方法体
}
一个类在继承父类的同事,实现多个接口的格式:
public class 类名称 extends 父类名称 implements 接口A,接口B{
//...
}
- 1、这个类必须覆盖重写所有的抽象方法。
- 2、如果父类/或多个接口之前存在重复的方法抽象方法,只需要重写覆盖一个即可。
- 3、如果这个类没有做到覆盖重写所有的重写方法,那么这个类自己也必须是一个抽象类。
- 4、对于多个接口存在冲突的默认方法,实现类必须进行覆盖重写。
- 5、父类的方法,优先于接口中的默认方法。
public interface 接口名 extends 父接口A, 父接口B {
// ...
}
- 1、类与类之间是单继承关系。
- 2、类与接口之间是实现关系。
- 3、接口与接口之间是多继承关系。
子接口的实现类,必须覆盖重写来自于所有接口的所有抽象方法
。
如果多个父接口当中存在重复的抽象方法,无所谓实现哪一个。
如果多个父接口当中存在冲突的默认方法,那么子接口必须覆盖重写默认方法,而且带着default关键字。
继承性是多态的前提:
1、要么存在父子类的继承
2、要么存在接口,实现类之间的实现关系。
对象多态性的特性:
- 一个对象拥有多种类的形态,就叫做多态性。
多态性在代码当中的体现就是:左父右子,或者左接口右实现。
在继承关系当中使用多态:
父亲 对象名 = new 子类();
在实现关系当中使用多态:
接口 对象名 = new 实现类();
使用多态的时候,【重要】特点是:
- 编译看左边,运行看右边。
- “由左边决定能不能调用,由右边决定运行的是谁。”
多态当中成员变量的`访问特点`:看new的是谁,优先用谁;如果没有,向上找父类。
直接通过对象名称`访问成员变量`: 看等号左边是谁,优先用谁;如果没有,向上找父类。
间接通过成员方法访问成员变量: 该方法属于谁,优先用谁,如果没有,向上找父类。
多态当中成员方法的访问特点: 看new 的是谁,优先用谁,如果没有,向上找父类。
向上转型:将本来一个子类对象,向上转换成为父类;
向下转型:将一个父类对象,向下转型成为子类。
对象的向上转型:格式:
父类 对象名 = new 子类();
接口 对象名 = new 实现类();
备注:
1、对象的向上转型一定是安全的。
2、有点类似于基本类型的强转。由小到大
注意:对象向上转型之后,就不能调用子类特有的方法了。
对象向下转型:还原
格式:
子类 对象 = (子类)父类对象;
实现类 对象名 = (实现类)接口类型的对象;
Cat cat = (Cat)animal;
备注:
- 1、有点类似于基本类型的强制转换。
- 2、对象的向下转型不一定安全,很可能出现问题。
- 3、本来是什么类型,就向下转型成什么类型。
Animal animal = new Cat();
Cat cat = (Cat)animal;
Dog dog = (Dog) animal;// 错误,ClassCastException报错类转换异常。
通过关键字【intanceof】可以判断一个对象是不是指定类型的实例:
对象 instanceof 类名称;
这将会得到一个boolean值,如果是true代表可以转换成后者类型。
注意:
一定要保证向下转型之前,首先通过instanceof判断。绝对不能直接向下转型。
// 如果animal对象确实可以转换成为Cat类的话
if (animal instanceof Cat) {
System.out.println("喵喵喵");
Cat cat = (Cat) animal;
cat.eat();
cat.catchMouse();
}
// 如果animal对象确实可以转换成为Dog类的话
if (animal instanceof Dog) {
System.out.println("汪汪汪");
Dog dog = (Dog) animal;
dog.eat();
dog.watchHouse();
}
笔记本案例分析:
此处笔记本的向上转型,第二种是隐式向上转型:
定义一个compter类和一个Usb接口,鼠标和键盘实现类,在compter类中调用usb接口。
public class Computer {
public void powerOn() {
System.out.println("开机");
}
public void powerOff() {
System.out.println("关机");
}
// 使用设备
public void useDevice(USB usb) {
usb.open();
if (usb instanceof Mouse) {
Mouse mouse = (Mouse) usb;
mouse.click();
}
if (usb instanceof Keyboard) {
Keyboard key = (Keyboard) usb;
key.type();
}
usb.close();
}
}
测试类:
public static void main(String[] args) {
Computer computer = new Computer();
computer.powerOn();
USB mouse = new Mouse(); // 向上转型
computer.useDevice(mouse);
Keyboard key = new Keyboard(); // 没有多态,这一行没有向上转型
computer.useDevice(key); // 照样正确!照样也是向上转型
computer.powerOff();
}