文章目录
前言
接口的概述:接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
如图:
多态的概述:
同一个对象,在不同时刻表现出来的不同形态
一、接口
1.1 概述
- 当一个类中所有方法都是抽象方法时,我们可以将这个类定义为接口
- 接口比抽象类还要抽象
- Java中接口存在的两个意义
- 用来定义规范
- 用来做功能的拓展
-
接口用关键字interface修饰
public interface 接口名 {}
-
类实现接口用implements表示
public class 类名 implements 接口名 {}
-
接口不能实例化
我们可以创建接口的实现类对象使用
-
接口和类之间是实现关系,类实现接口用implements关键字表示
public class 类名 implements 接口名 {}
- 注意: 在接口的实现关系中,我们可以看成接口是实现类的父类,但是严格意义上来讲接口和实现类不是父子类关系,只有通过extends连接的两个类才是真正意义上的父子类关系
接口的子类(实现类)
-
要么重写接口中的所有抽象方法
-
要么子类也是抽象类
1.2接口的成员特点
-
成员特点
-
成员变量
只能是常量
默认修饰符:public static final
-
-
构造方法(含有大括号—进行具体实现内容)
没有,因为接口主要是用来扩展功能的,而没有具体存在,里边全都是抽象方法;而构造方法必须存在具体的实现内容,因此接口类中不能出现其构造方法
-
成员方法
只能是抽象方法
默认修饰符:public abstract
-
-
关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解
- 在JDK1.8(JDK8)之前,接口中只能允许存在抽象方法
- 在JDK1.8、JDK1.9之后,接口中允许存在有方法体的非抽象方法(后续)
-
-
代码演示
- 接口
public interface Inter { public static final int NUM = 10; public abstract void show(); }
- 实现类
class InterImpl implements Inter{ public void method(){ // NUM = 20; System.out.println(NUM); } public void show(){ } }
- 测试类
public class TestInterface { /* 成员变量: 只能是常量 系统会默认加入三个关键字 public static final 构造方法: 没有 成员方法: 只能是抽象方法, 系统会默认加入两个关键字 public abstract */ public static void main(String[] args) { System.out.println(Inter.NUM); } }
1.3类和接口的关系
-
类与类的关系
继承关系,只能单继承,但是可以多层继承
-
类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
-
接口与接口的关系
继承关系,可以单继承,也可以多继承
1.4 接口组成更新
1.4.1 接口组成更新概述
-
常量
public static final
-
抽象方法
public abstract
-
默认方法(Java 8)
-
静态方法(Java 8)
-
私有方法(Java 9)
1.4.2 接口中默认方法
-
格式(使用default关键字,并且方法体中)
public default 返回值类型 方法名(参数列表) { }
-
作用
- 解决接口升级的问题
- 应用场景概述:
- 现存在一个接口类,里边定义了不同的抽象方法,然后它有多个实现类(按照实现类的规定,必须对接口类中的所有抽象方法进行一个强制重写)
- 在使用中,现又需要对接口类进行新增某个功能的这样一个修改操作
- 通常来说,新增该功能就是在我们的接口类中再编写一个抽象方法
- 但是此时会发现该接口类的所有实现类都会报错,因为由于实现关系中对抽象方法必须进行一个强制重写,而要解决这个报错只能在每个实现类中都对新功能方法进行重写,修改起来会很麻烦
- 此时就可以利用接口中的默认方法来完成
- 在接口类中定义一个实现新功能的默认方法,并完成其方法体的编写,在主函数中可以用实现类的对象直接调用新方法即可
-
范例
public default void show3() { }
-
注意事项
- 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字(此时与之前的硬性条件有所不同,之前要求方法重写时方法声明必须一模一样)
- 在接口中,public可以省略,default不能省略
- 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字(此时与之前的硬性条件有所不同,之前要求方法重写时方法声明必须一模一样)
-
如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
-
一个实现类可以与多个接口类存在实现关系
-
但如果多个接口类中存在相同默认方法(即存在各自的方法体)
-
实现类中就必须对该方法进行强制重写
-
重写后按照就近原则,就会优先访问实现类中该方法的方法体
-
避免出现实现类不知道应该继承出哪个接口的方法的情况
-
1.4.3 接口中静态方法
-
格式
public static 返回值类型 方法名(参数列表) { }
-
范例
-
应用场景:想要新增功能,并固定此功能(写死),即规定不想要实现类对该功能进行重写,此时就可以利用接口中的静态方法应用
public static void show() {//public可以省略 }
-
注意事项
- 静态方法只能通过接口名调用,不能通过实现类的类名或者对象名调用
- 静态方法不能被实现类重写
- public可以省略,static不能省略
-
代码示例
//接口类与实现类
package com.wedu.JieKou.StudyDemo;
public interface Inter {
void showA();
public abstract void showB();
//想新增一个功能,利用接口的默认方法直接定义
public default void showC(){
System.out.println("接口中的新增方法");
}
static void showD(){
System.out.println("接口中的新增方法======D=====");
}
}
class InterImpL1 implements Inter{
@Override
public void showC(){
System.out.println("实现类1的新功能重写");
}
@Override
public void showA(){
System.out.println("实现类1的showA方法重写");
}
@Override
public void showB(){
System.out.println("实现类1的showB方法重写");
}
}
class InterImpL2 implements Inter{
@Override
public void showA(){
System.out.println("实现类2===的showA方法重写");
}
@Override
public void showB(){
System.out.println("实现类2====的showB方法重写");
}
}
//测试类
package com.wedu.JieKou.StudyDemo;
public class StudyTestDemo {
public static void main(String[] args) {
InterImpL1 interImpL1 = new InterImpL1();
interImpL1.showA();
interImpL1.showB();
InterImpL2 interImpL2 = new InterImpL2();
interImpL2.showA();
interImpL2.showB();
//InterImpL1.showC();//不知道是不是因为JDK版本问题导致无法实现直接调用
Inter.showD();
}
}
1.4.4 接口中私有方法
-
私有方法产生原因
- Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。
- 这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码时
- 程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的
- 因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
-
定义格式
-
格式1:私有的非静态方法
-
场景:接口类中有多个默认方法,且当中存在共性,此时提取它们的共性并利用接口的“私有的非静态方法”应用,编写一个方法,并可以在默认方法中直接调用该“共性”的方法
-
最后在测试类中使用接口类的类名调用默认方法即可
-
-
private 返回值类型 方法名(参数列表) { }
-
范例1
private void show() { }
-
格式2:私有的静态方法
private static 返回值类型 方法名(参数列表) { }
-
范例2
-
注意事项1
-
默认方法可以调用私有的静态方法和非静态方法
-
静态方法只能调用私有的静态方法
-
注意事项2
- 私有的非静态方法:外部测试类无法直接访问(不论是通过实现类的类名和对象,还是接口类的类名都无法直接访问),只能通过实现接口类中默认方法来间接调用该私有方法
- 私有的静态方法
二、多态
2.1 多态的前提
-
要有 继承(亲爹关系)或 实现关系(干爹关系)
-
要有方法的重写
-
要有父类引用指向子类对象
- 解释:
- 解释:
2.2多态中的成员访问特点
-
成员访问特点
-
成员变量
编译看父类(左边),运行看父类(左边)
-
成员方法
编译看父类(左边),运行看子类(右边)
- 要达到多态的目的,运行出子类中的方法,因此在多态中子类最好是重写父类的方法
-
2.3 多态的好处和弊端
-
好处
-
提高程序的扩展性
-
定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
- 定义方法参数时,使用父类型作为形参,在调用方法时,使用具体的子类型作为实参
package com.wedu.DuoTai.StudyDemo; public class PolyTest { public static void main(String[] args) { Dog dog = new Dog(); dog.show(); Cat cat = new Cat(); cat.show(); Animals animals=new Cat();//编译看父类(左边),运行看子类(右边) animals.show();//控制台: 给猫喂食 System.out.println("============="); Person person = new Person(); person.feed(dog); System.out.println("============="); Animals animals1 = new Animals(); animals1.show(); } } class Person{ //使用父类型作为形参,在调用方法时,使用具体的子类型作为实参 比如上面的person.feed(dog);语句 public void feed(Animals animals){//相当于 Animals animals=new Dog();,即父类引用指向子类对象 System.out.println("人给宠物喂食+++++"); animals.show(); } } class Animals{ public void show(){ System.out.println("给宠物喂食"); } } class Dog extends Animals { @Override public void show(){ System.out.println("给狗喂食"); } } class Cat extends Animals{ @Override public void show(){ System.out.println("给猫喂食"); } }
- 定义方法返回值时,使用父类型作为返回值类型,具体return时,返回子类的对象
-
-
弊端
不能使用子类的特有成员
(因为父类引用指向子类对象时,编译是看父类,先在父类中寻找有没有对应的成员,因此这个时候访问不到子类的特有成员,但是可以使用多态中的向上转型实现访问子类的特有成员)
3.4多态中的转型
-
向上转型(类比自动类型转换)
父类引用指向子类对象就是向上转型
格式:父类型 对象名=子类对象;
-
向下转型(类比强制类型转换)
格式:子类型 对象名 = (子类型)父类引用;
3.5多态中转型存在的风险和解决方案
-
风险
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException (类型转换异常)
-
解决方案
-
因为对应的实际类型与目标类型不是一种类型,不是同类型的无论如何都不能进行转换,但是我们可以在转换之前加一个判断,如果类型不一致就直接不转换,反之进行转换。来更好规避这在种风险
-
关键字
instanceof
-
使用格式
变量名 instanceof 类型
通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
-