Java学习之接口与多态篇

Java学习之接口与多态篇

0x00 前言

在前面写的几篇文章里,讲了封装和继承,那么这篇文章就来讲讲第三大面向对象的特性多态吧,在此之前先接着上篇文章的抽象先来看看Java里面接口的应用。

0x01 接口

接口在java里面属于引用数据类型,是方法的集合。如果说的类的内部封装了成员变量、构造方法和成员方法,那么接口内部主要封装了方法,包括抽象方法,默认方法和静态方法,私有方法。

接口的定义和定义类差不多,定义接口需要使用到interface关键字。虽然说他也会被编译为class文件,但是他并不是一个类,而是另一种引用数据类型。

引用数据类型:数组、接口、类

接口的使用,他和抽象类一样不能直接被创建,但是他可以被实现使用implements关键字。
一个实现接口的类可以看作是接口的子类,需要实现接口里面所有的抽象方法,创建该类的对象,就可以调用方法了,否则他必须是抽象的子类。

定义格式:

public interface 接口名{
    
}

抽象方法使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。

public interface InterFaceName {
    public abstract void method();
}

接口里的默认方法和静态方法

默认方法:使用default进行修饰,不能省略,提供给子类或者子类重写使用。

静态方法:使用static修饰,提供给接口直接调用。

public interface MyInterface {
    public static void method(){
        //执行语句
    }
    public default void method2(){
        //执行语句
    }
}

接口里的私有方法和静态方法

私有方法和定义私有变量一样,使用private修饰,提供给接口的默认方法和静态方法调用。

pubilc interface InterfaceName{
    
}

实现接口

类和接口的关系为实现关系,这被称为类实现接口,这类可以称为接口的实现类,基本上和抽象类有点类似,也可以称为接口的子类。实现的动作蕾仕于继承,格式几乎一致,但是是使用implements关键字,而不是extends。

实现接口必须重写所有的抽象方法

集成了接口的默认方法,可以直接调用也可以重写

接口代码:
public interface MyInterface {
    void eat();
    void sleep();

}
实现类代码:
public class Zi implements MyInterface {


    @Override
    public void eat() {
        System.out.println("吃");
    }

    @Override
    public void sleep() {
        System.out.println("睡");

    }
}

main方法代码:

public static void main(String[] args) {
        Zi zi = new Zi();
        zi.eat();
        zi.sleep();

    }

接口默认方法代码实现:

定义接口:

public interface MyInterface {
    default void eat() {
        System.out.println("吃");
        
    }
    public default void sleep(){
        System.out.println("睡");
    }

}
实现类代码:

public class Zi implements MyInterface {



}

main方法代码:

public static void main(String[] args) {
        Zi zi = new Zi();
        zi.sleep();
        zi.eat();

    }

这里接口定义的第一个方法直接defalut使用这个关键字定义默认方法,第二个加上了public这两种写法都可以,前者是省略式的写法。

实现类的任何代码都没写,只写了一个继承,默认方法是默认就定义好的,可以不用写,从实例化对象直接调用,当然也可以在实现类里面进行重写。

接口静态方法使用

静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用。

接口定义:
public interface MyInterface {
    static void eat() {
        System.out.println("吃");
    }
}

实现类定义:

public class Zi implements MyInterface {

    
}

main方法代码:

public static void main(String[] args) {
        MyInterface.eat();

    }
    

这里可以看到在main方法里面,实例化一个对象,然后去访问eat方法是报错的,定义为静态的方法必须使用接口名进行访问。

私有方法的使用

私有方法:只有默认方法可以访问。

私有静态方法:只有默认方法和静态方法可以调用。

如果一个接口有多个默认方法并且重复,那么就可以提取出来封装到私有方法里面,供默认方法去调用他。

public interface LiveAble {
default void func(){
func1();
func2();
} p
rivate void func1(){
System.out.println("跑起来~~~");
} p
rivate void func2(){
System.out.println("跑起来~~~");
}
}

这种写法是jdk9的新特性,jdk8没有,此段描述纯属复制。

0x02 接口多实现

在前面说到因为java只支持单继承,所以一个类只能继承一个父类,但是对于接口来说,一个类是可以实现多个接口的,这就叫做接口的多实现,并且一个类里面可以继承一个父类,同时也实现多个接口。

格式:

class 类名 extends 父类名 impls 接口1,接口2{
    ...
}
或者
class 父类名 impls 接口1,接口2{
    ...
}

接口多实现多抽象方法

如果在多个接口中有重名的方法,该重名的方法只需重写一次。

接口多实现多个默认方法

接口中多个默认方法,实现类都可以继承使用。但是如果有重名的方法就必须重写一次。

接口1:

public interface MyInterface {
    default void method1(){
        System.out.println("我是一个没有感情的默认方法");
    }
    }
接口2:
public interface MyInterface2 {
    default void method1(){
        System.out.println("我是一个没有感情的默认方法");
    }
    }
实现类:

public class Zi implements MyInterface,MyInterface1 {
    @Override
    public void method1() {
        System.out.println("没有感情的方法");
    }
}

main方法代码:

    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.method1();
    }

而接口中的静态方法就不会存在重名的问他,静态方法访问可以直接使用接口名访问静态方法。

优先级问题

一个类中如果计策了一个父类又实现了多个接口时,父类中的成员方和接口中的默认方法重名,子类就近选择执行的父类成员方法。

注意事项:

接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。

接口中,没有构造方法,不能创建对象。

接口中,没有静态代码块。

0x03 多态

多态是面向对象的第三大特性。

多态:指同一种行为,具有不同的表现形式。

多态前提:

1.继承或者实现

2.方法的重写

3.父类引用指向子类[格式]

格式:

父类类型 变量名 = new 子类对象;
变量名.方法名();

父类类型:指向子类对象继承的父类类型,或者是实现的父类接口类型。

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写
后方法。

父类代码:

public abstract class Animal {
    public abstract void play();
}


子类1代码:
public class Cat extends Animal {


    @Override
    public void play() {
        System.out.println("撸猫");
    }
}

子类2 代码:
public class Dog extends Animal{

    @Override
    public void play() {
        System.out.println("撸狗");
    }
}

main方法代码:

public static void main(String[] args) {
        Animal cat = new Cat();
        Animal dog = new Dog();
        cat.play();
        dog.play();
    }

0x04 多态引用类型转换

多态的转型分为向上转型喝向下转型两种:

我们经常用到的就是向上转型,多态本身就是子类类型向父类类型向上转换的过程。

当父类引用指向子类对象的时候,这就是向上转型。
代码:

Antmal cat = new Cat();

向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。

一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

格式:

子类类型 变量名 =(子类类型)父类变量名;

在这里来说一下为什么要使用转型

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥
有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子
类特有的方法,必须做向下转型。

总结:如果要调用子类拥有而父类没有的方法的时候就要使用到向下转型。

父类代码:
public abstract class Animal {
    public abstract void eat();
}

子类:
public class Cat extends Animal {


    @Override
    public void eat() {
        System.out.println("吃鱼");
    }
    public void catchMouse(){
        System.out.println("抓鱼");
    }

}

main方法代码:

public static void main(String[] args) {
        Animal cat = new Cat();
        cat.eat();
        Cat c =(Cat)cat;
        c.catchMouse();
    }

为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:


量名 instanceof 数据类型
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。

所以,转换前,我们最好先做一个判断。

public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}

0x05 结尾

接口与多态篇章更新完成,检测每天一个小总结。

posted @ 2020-08-02 12:14  nice_0e3  阅读(632)  评论(0编辑  收藏  举报