1.接口(interface)
接口是一种引用数据类型。
引用数据类型:数组,类,接口。
接⼝的使⽤,它不能创建对象,但是可以被实现(
implements
,类似于被继承)。
1.定义
public interface 接⼝名称 {
//
抽象⽅法
//
默认⽅法
//
静态⽅法
//
私有⽅法
}
2.抽象方法
抽象⽅法:使⽤
abstract
关键字修饰,可以省略,没有⽅法体。该⽅法供⼦类实现使⽤。
public interface InterFaceName {
public abstract
void
method
();
}
3.默认方法和静态方法
默认⽅法:使⽤
default
修饰,不可省略,供⼦类调⽤或者⼦类重写。
静态⽅法:使⽤
static
修饰,供接⼝直接调⽤。
public interface
InterFaceName
{
public default
void
method
() {
//
执⾏语句
}
public static
void
method2
() {
//
执⾏语句
}
}
4.私有方法和私有静态方法
私有⽅法:使⽤
private
修饰,供接⼝中的默认⽅法或者静态⽅法调⽤
public interface
InterFaceName
{
private
void
method
() {
//
执⾏语句
}
}
5.实现
类与接⼝的关系为实现关系,即
类实现接⼝
,该类可以称为接⼝的实现类,也可以称为接⼝的⼦
类。实现的动作类似继承,格式相仿,只是关键字不同,实现使⽤
implements
关键字。
⾮抽象⼦类实现接⼝:
1.
必须重写接⼝中所有抽象⽅法。
2.
继承了接⼝的默认⽅法,即可以直接调⽤,也可以重写。
class
类名
implements
接⼝名
{
//
重写接⼝中抽象⽅法【必须】
//
重写接⼝中默认⽅法【可选】
}
6.使用
- 抽象方法的使用:必须在实现类里重写所有的抽象方法,创建实现类对象后来调用重写后的方法。
- 默认方法的使用:可以继承也就是实现,可以重写,⼆选⼀,但是只能通过实现类的对象来调⽤。
- 静态方法的使用:静态与 .class ⽂件相关,只能使⽤接⼝名调⽤,不可以通过实现类的类名或者实现类的对象调⽤。
- 私有方法的使用:
私有⽅法:只有默认⽅法可以调⽤。私有静态⽅法:默认⽅法和静态⽅法可以调⽤。注意:如果⼀个接⼝中有多个默认⽅法,并且⽅法中有重复的内容,那么可以抽取出来,封装到私有⽅法中,供默认⽅法去调⽤。
代码说明
import day08.In01.Interface01;
import day08.In01.InterfaceTest01;
public class Main01 {
public static void main(String[] args) {
//主方法里创建的是InterfaceTest01的对象,调用的是实现接口Interface01,InterfaceTest01重写的m1抽象方法
InterfaceTest01 in01 = new InterfaceTest01();
//因为被重写,所以就可以直接调用抽象方法
in01.m1();
//默认方法可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。
in01.m2();
//要使用静态方法需 接口名.方法名 调用
Interface01.m3();
//私有方法只能通过默认方法或者静态方法调用
//1.私有调用 只有默认方法可以调用。
in01.m5();//重写后的默认私有
in01.m6();
//2.私有静态调用 默认方法跟静态方法都能调用
in01.m6();//里面调用了私有,还有静态私有
Interface01.m7();//静态私有 接口名.方法名
Interface01.m8();
}
}
//实现类
import day08.In01.Interface01;
//实现接口 就必须重写接口里的所有抽象方法
public class InterfaceTest01 implements Interface01 {
@Override//重写抽象方法
public void m1() {
System.out.println("InterfaceTest01实现接口Interface01中的抽象方法m1");
}
@Override//重写默认方法
public void m2() {
Interface01.super.m2();
System.out.println("InterfaceTest01重写接口Interface01中的默认方法m2");
}
@Override//也可以重写m5
public void m5() {
Interface01.super.m5();
System.out.println("开开心心快快乐乐健健康康生活");
}
}
//接口
/*
* 接口不是类(class)
* 是引用数据类型 :数组,类,接口
* 但可以把它看做一个抽象类
* 接口不能new对象,但可以被实现(implements,可以看做被继承,但它不是继承哦)
*
* */
public interface Interface01 {
//接口无法定义成员变量,只有静态常量默认使用public static final 修饰
public static final int a = 10;//因为是常量所以必须初始化
//因为看做是抽象类,所以含有抽象方法
//抽象方法没有方法体哦
public abstract void m1();
//接口没有构造方法,因为不能new对象
//jdk1.7之前 接口中含有抽象方法
//jdk1.8就添加了默认方法跟静态方法
//jdk1.9还添加了私有方法
//默认方法
public default void m2(){
System.out.println("Interface01 的默认方法m2");
}
//静态方法
public static void m3(){
System.out.println("Interface01 的静态方法m3");
}
//私有方法
private void m4(){
//默认方法中相同部分
System.out.println("开开心心快快乐乐健健康康");
System.out.println("Interface01 的私有方法m4");
}
//1.默认私有:只有默认方法能调用
// 当多个默认方法中有许多公共的属性,行为就可以提取到私有方法中,
// 再在接口中的默认方法里调用私有方法,然后去主方法里调用默认方法
//就可以使用接口中的默认私有方法了
public default void m5(){
//System.out.println("开开心心快快乐乐健健康康");
m4();//调用私有方法
}
public default void m6(){
// System.out.println("开开心心快快乐乐健健康康");
m4();
m9();//默认调用静态私有
}
//2.静态私有:
//默认方法跟静态方法都能调用
public static void m7(){
// System.out.println("2233");
m9();
}
public static void m8(){
// System.out.println("2233");
}
//私有静态方法
private static void m9(){
//静态方法相同部分
System.out.println("静态相同部分2233");
}
}
7.接口的多实现
之前学过,在继承体系中,⼀个类只能继承⼀个⽗类。⽽对于接⼝⽽⾔,⼀个类是可以实现多个
接⼝的,这叫做接⼝的
多实现
。并且,⼀个类能继承⼀个⽗类,同时实现多个接⼝。
class
类名
[
extends
⽗类名
]
implements
接⼝名
1
,
接⼝名
2
,
接⼝名
3
... {
//
重写接⼝中抽象⽅法【必须】
//
重写接⼝中默认⽅法【不重名时可选】
}
- 抽象方法:接⼝中,有多个抽象⽅法时,实现类必须重写所有抽象⽅法。如果抽象⽅法有重名的,只需要重 写⼀次。
- 默认方法:接⼝中,有多个默认⽅法时,实现类都可继承使⽤。如果默认⽅法有重名的,必须重写⼀次。
- 静态方法:接⼝中,存在同名的静态⽅法并不会冲突,原因是只能通过各⾃接⼝名访问静态⽅法
- 优先级:当⼀个类,既继承⼀个⽗类,⼜实现若⼲个接⼝时,⽗类中的成员⽅法与接⼝中的默认⽅法重 名,⼦类就近选择执⾏⽗类的成员⽅法。
8.接口的多继承
⼀个接⼝能继承另⼀个或者多个接⼝,这和类之间的继承⽐较相似。接⼝的继承使⽤
extends
关
键字,⼦接⼝继承⽗接⼝的⽅法。
如果⽗接⼝中的默认⽅法有重名的,那么⼦接⼝需要重写⼀
次。
代码说明
//类 接口 继承 实现的关系
public class Main {
public static void main(String[] args) {
//父类先实现接口A,子类再继承父类
Zi1 zi1 = new Zi1();
zi1.A1();//调用抽象方法
zi1.A2();//默认方法
//Afu1111
//InterfaceA的默认方法A2
//子类先继承父类,再实现接口A,B,上面的方式也能实现多个接口
Zi2 zi2 = new Zi2();
zi2.A1();
zi2.A2();
//Azi222
//InterfaceA的默认方法A2
zi2.B1();
zi2.B2();
//Bzi222
//InterfaceB的默认方法B2
zi2.AB();//调用重写后的AB接口相同抽象方法 必须重写
// 重写后的AB相同的抽象方法AB
zi2.AB2();//调用重写后的AB接口相同默认方法 此时必须重写 没有重复的就可以选择是否重写
//InterfaceA的默认方法AB2
//重写后的AB相同的默认方法AB2
}
}
//父类
//1.可以先让父类实现接口,再通过被继承,使子类也有接口的抽象方法跟默认方法
public class Fu1 implements InterfaceA{
@Override
public void A1() {
System.out.println("Afu1111");
}
@Override
public void AB() {
}
}
//子类继承父类,也有接口的方法
public class Zi1 extends Fu1 {
}
public interface InterfaceA {
//抽象方法
public abstract void A1();
//默认方法
public default void A2(){
System.out.println("InterfaceA的默认方法A2");
}
//AB接口相同抽象方法
public abstract void AB();
//AB接口相同默认方法
public default void AB2(){
System.out.println("InterfaceA的默认方法AB2");
}
}
//父类2
public class Fu2 {
}
//2.子类2是先继承父类2,再实现接口
public class Zi2 extends Fu2 implements InterfaceA,InterfaceB{
@Override
public void A1() {
System.out.println("Azi222");
}
@Override
public void B1() {
System.out.println("Bzi222");
}
@Override//AB接口有相同的抽象方法,就只用重写一次
public void AB() {
System.out.println("重写后的AB相同的抽象方法AB");
}
@Override//AB接口有相同的默认方法,就必须重写一次
public void AB2() {
InterfaceA.super.AB2();//前面的 InterfaceA也能改成B,因为方法都一样,可能内容不一样,看需求
System.out.println("重写后的AB相同的默认方法AB2");
}
}
public interface InterfaceB {
public abstract void B1();
public default void B2(){
System.out.println("InterfaceB的默认方法B2");
}
//AB接口相同抽象方法
public abstract void AB();
//AB接口相同默认方法
public default void AB2(){
System.out.println("InterfaceB的默认方法AB2");
}
}
9.当接口作为方法参数、返回值类型使用
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Test01 t01 = new Test01();
InterfaceAImpl in01 = new InterfaceAImpl();
t01.method01(in01);//使用的方法参数也是接口A的实现类
//重写的抽象方法A1
Test02 t02 = new Test02();
InterfaceA i = t02.method02();//用接口类型接收t02.method02();方法返回过来的数据
System.out.println(i);//day08.In03.InterfaceAImpl@f5f2bb7 这是接口A的实现类的地址值
}
}
//接口
public interface InterfaceA {
//抽象方法
public abstract void A1();
}
//实现类
public class InterfaceAImpl implements InterfaceA{
@Override
public void A1() {
System.out.println("重写的抽象方法A1");
}
}
//将接口作为方法参数
public class Test01 {
//成员方法
//此时方法参数是接口A类型
public void method01(InterfaceA a){
a.A1();//相当于InterfaceA a = new InterfaceAImpl();
}
}
import java.util.Arrays;
//将接口类型作为返回值
public class Test02 {
public InterfaceA method02(){
//返回的是一个接口A类型的 in02
InterfaceAImpl in02 = new InterfaceAImpl();
in02.A1();// 重写的抽象方法A1 调用实现类中的A1方法
return in02;//对于这个in02 其实返回的是InterfaceA的实现类
}
}
注意:
1.接⼝中,⽆法定义成员变量,但是可以定义常量,其值不可以改变,默认使⽤
public
static final
修饰。
2.接⼝中,没有构造⽅法,不能创建对象。
3.接⼝中,没有静态代码块。
4.接口和类的关系:
类 和 类(单继承): 1 v 1 extends 继承
接口 和 类: 没有此关系
类 和 接口(多实现): 1 v n implements 实现
接口 和 接口(多继承): 1 v n extends 继承
类 和 类(单继承): 1 v 1 extends 继承
接口 和 类: 没有此关系
类 和 接口(多实现): 1 v n implements 实现
接口 和 接口(多继承): 1 v n extends 继承
5.接口作为方法参数和返回值使用的情况:
1).接口作为方法参数使用:
可以传所有实现类对象,所以具体使用的都是接口的实现类对象。
2)接口作为返回值类型使用:
可以返回所有实现类对象,所以具体返回的都是接口的实现类对象。
1).接口作为方法参数使用:
可以传所有实现类对象,所以具体使用的都是接口的实现类对象。
2)接口作为返回值类型使用:
可以返回所有实现类对象,所以具体返回的都是接口的实现类对象。
2.多态
多态是继封装、继承之后,⾯向对象的第三⼤特性。
1.定义
是指统一行为,具有多个不同表现形式。
2.多态出现的前提
继承:必须存在父子类关系;实现;二选一
向上造型:在多要中需要将子类型的引用赋值给父类型对象
重写:必须 子类重写父类中的方法
3.多态的实现
⽗类类型 变量名
=
new
⼦类对象
;
变量名
.
⽅法名
();
⽗类类型:指⼦类对象继承的⽗类类型,或者实现的⽗接⼝类型。
当使⽤多态⽅式调⽤⽅法时,⾸先检查⽗类中是否有该⽅法,如果没有,则编译错误;如果有,
执⾏的是⼦类重写后⽅法。
4.多态的好处
实际开发的过程中,⽗类类型作为⽅法形式参数,传递⼦类对象给⽅法,进⾏⽅法的调⽤,更能
体现出多态的扩展性与便利。
5.引用类型转换
分为向上造型和向下转型两种:
向上造型
:多态本身是⼦类类型向⽗类类型向上转换的过程,这个过程是默认的。
⽗类类型 变量名 = new ⼦类类型();
向下转型:⽗类类型向⼦类类型向下转换的过程,这个过程是强制的。
⼦类类型 变量名 = (⼦类类型) ⽗类变量名;
6.转型的异常
为了避免 ClassCastException(类型转换异常)的发⽣,Java
提供了
instanceof
关键字,给引⽤变量做类型的校验。
变量名
instanceof
数据类型
如果变量属于该数据类型,返回
true
。
如果变量不属于该数据类型,返回
false
。
代码说明
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();//这样的是创建一个对象,实例化
dog.age = 2;
dog.eat();
//null吃饭
//骨头真好吃
//多态
Animal d = new Dog();
//向上造型
//父类 变量名 = new 子类();
d.name = "小狗欢欢";
d.eat();//调用的是父类,但是运行的是被子类重写过的
//小狗欢欢吃饭
//骨头真好吃
System.out.println(d.age);//4 父类的成员变量
//向下转型(强制转型)
//int a = (int)2.1;
if(d instanceof Dog) {//判断引用名是否是Dog类型,避免了强转出现ClassCastException ,类型转换异常这个错误
Dog dd = (Dog) d;
System.out.println(dd.age);//5子类的age
}
}
}
//父类
public class Animal {
//成员变量
int age = 4;
String name;
//成员方法
public void eat(){
System.out.println(name+"吃饭");
}
}
//子类
public class Dog extends Animal {
int age = 5;
@Override
public void eat() {
super.eat();
System.out.println("骨头真好吃");
}
}
注意:
成员变量:编译看左边,运行看左边
成员方法:编译看左边,运行看右边 --> 主要是看有没有被重写,没有就运行原来的,反之运行之后的。
静态方法:编译看左边,运行看左边