【Java】JDK8开始后接口中新增的方法

一、概述

在JDK7以前:接口中只能定义抽象方法。

到了JDK8:接口中可以定义有方法体的方法。这个方法分为两种,一种是默认方法,另一种是用 static 修饰的 静态方法

到了JKD9:接口中就可以定义用 private 修饰的私有方法。

学习这些知识点的时候,很多同学只知道语法,却不知道Java为什么这么去设计。


二、Java为什么这么去设计?

在开发当中,公司的一些大佬,它会提前把一些接口去定义好,定义完了后,就会通知下面的小弟们去用。

于是有个小弟就说:遵命,大哥。于是它就写了一个 InterImpl1实现类,实现了 Inter 接口,重写了 Inter接口 中所有的抽象方法。

小弟2号也不甘示弱,也实现了 Inter接口,按照里面的格式去重写了所有的抽象方法。

image-20240418112104747


过了几天,需求发生了改变,要新增一些其他的方法了,即在接口中新增一些其他的规则。

因此在 Inter接口 中,又添加了一个抽象方法 method2(),写完后,大哥会说:“小弟们,我又添加了一个规则,你们用的时候记得加进去。”

此时小弟1号脾气不好,就开始发飙了。小弟2号说:“我也一样”。

image-20240418112402537

因此,如果一直按照 JDK7 以前的规则来操作,那么就一定会遇到这么尴尬的事情:只要接口里面发生了变化,那么所有的这些实现类,就不得不跟着一起去改变。如果说不跟着一起改变,那么这些实现类立马会报错。


此时接口就自己琢磨:这些家伙真难伺候,但是我又想加新的规则,又想让他们的代码不报错怎么办呢?

此时它就想出了:我是不是在接口中写那种有方法体的方法就可以了。

因此,JDK8和JDK9新增的特性就是这么出现的。

image-20240418112729755


三、接口升级

接口中有方法体的方法,它是在接口升级时,为了兼容性而使用的。

为了理解 接口升级,我们来看一个场景。

有一个程序员开发一个项目,在项目版本1.0的版本中,定义了一个 Inter接口,下面有两个实现类去实现 Inter接口

此时 项目Vsersion1.0 成功上线,没有任何问题

image-20240418113030180

但是呢,过了一段时间,项目2.0版本 开始发布了,功能比 1.0 要多,所以说需要在 Inter接口 中加入 10个新的方法,这个就叫做接口升级。

当接口在升级的时候,此时就可以使用有方法体的方法。

此时实现类就不需要立马修改了,代码是不会报错的。等以后用到某个规则了,再去重写就可以了。


这种 有方法体的方法 分为两种:① 默认的(用 default 修饰);② 静态的(用 static 修饰)

接下来我们来挨个学习。


四、JDK8开始接口中新增的默认方法

1)介绍

JDK8版本后,允许在接口中定义默认方法,需要使用关键字 default 修饰。

作用:解决接口升级的问题。

接口中默认方法的定义格式:在 public 后面加上一个 default 就可以了。

// 格式
public default 返回值类型 方法名(参数列表){}
// 示例
public default void show(){}

接口中默认方法的注意事项

  • 默认方法不是抽象方法,所以不强制被重写。如果被重写,重写的时候需要去掉 default关键字
  • public 是默认修饰符,可以省略。但是 default 不能省略,一定要加上。
  • 如果实现了多个接口,多个接口中有相同名字的默认方法,此时子类不管用不用,必须要对 相同名字的默认方法 来进行重写。

2)代码实现

默认方法不是抽象方法,所以不强制被重写。

Inter.java

package com.itheima.a06interfacedemo6;

public interface InterA {
    // 为了演示示例效果,这里额外写一个抽象方法
    public abstract void method();

    public default void show(){
        System.out.println("Inter接口中的默认方法 ---- show");
    }
}

InterImpl.java

利用IDEA的快捷方式:用鼠标点击红色波浪线,alt + 回车 选择第一个,可以发现IDEA帮我默认选择的是抽象方法。

image-20240418114441992

最后选择 OK 即可。

package com.itheima.a06interfacedemo6;

public class InterImpl implements InterA,InterB{
    @Override
    public void method() {
        System.out.println("实现类重写的抽象方法");
    }
}

写完后,可以发现代码并没有报错,接下来在测试类中,就可以来创建实现类的对象。

Test.java

//创建实现类的对象
InterImpl ii = new InterImpl();
li.method(); // 调用重写后的method方法
ii.show(); // 调用默认方法

打印结果

实现类重写的抽象方法
Inter接口中的默认方法 ---- show

但如果想重写 Inter 中的默认方法怎么办?

我们不需要自己一个个去写,直接在实现类中写方法名,然后回车就好了。

image-20240418154406573
@Override
public void show() {
    System.out.println("重写接口中的默认方法");
}

如果被重写,重写的时候需要去掉 default关键字

在重写的默认方法中,就不能再去前面加 default 了。如果强行书写就会报错:扩展方法只能在接口内使用。

image-20240418154551901

public 是默认修饰符,可以省略

在IDEA中可以直接看见,像这种灰色的,都是可以直接省略的。

image-20240418154825120

但是 default 不能省略,一定要加上。

因为如果 default 你省略了,Java会把这个方法当做是抽象方法。因为接口中的方法会默认加上 public abstract 修饰符。

当你删掉 default 关键字后,就会报错:接口中的抽象方法不能拥有方法体。

image-20240418155301087


如果实现了多个接口,多个接口中有相同名字的默认方法,此时子类不管用不用,必须要对 相同名字的默认方法 来进行重写

InterA.java

package com.itheima.a06interfacedemo6;

public interface InterA {
    public abstract void method();

    public default void show(){
        System.out.println("InterA接口中的默认方法 ---- show");
    }
}

InterB.java

InterB.java接口 中也有默认方法 show()

package com.itheima.a06interfacedemo6;

public interface InterB {
    public default void show(){
        System.out.println("InterB接口中的默认方法 ---- show");
    }
}

然后让实现类分别实现 InterA接口InterB接口,并实现了InterA接口 中的抽象方法。

InterImpl.java

package com.itheima.a06interfacedemo6;

public class InterImpl implements InterA,InterB{
    @Override
    public void method() {
        System.out.println("实现类重写的抽象方法");
    }
}

但是,代码报错。

image-20240418155653838

这是因为,如果你没有重写,在测试类中创建实现类的对象,去调用 show() 方法,那Java怎么知道它是调用 InterA 中的 show() 还是 InterB 中的 show() 呢?

Test.java

//创建实现类的对象
InterImpl ii = new InterImpl();
ii.show(); // 调用默认方法

因此在这种情况下,实现类强制重写。那么当我再在测试类中调用 show() 方法的时候,一定是重写之后的 show() 方法。

InterImpl.java

package com.itheima.a06interfacedemo6;

public class InterImpl implements InterA,InterB{
    @Override
    public void method() {
        System.out.println("实现类重写的抽象方法");
    }

    @Override
    public void show() {
        System.out.println("重写接口中的默认方法");
    }
}

五、JDK8开始接口中新增的 静态方法

1)介绍

静态方法跟之前一样,直接使用 static 来修饰就行了。

接口中静态方法的定义格式:在 public 后面加一个 static关键字 就行了

// 格式
public static 返回值类型 方法名(参数列表){ }
// 范例
public static void show(){ }

接口中静态方法的注意事项

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
  • public 可以省略,static 不能省略

static不能省略default不能省略 是一样的,如果省略了,Java就会将这个方法当成抽象方法。


2)代码实现

Inter.java

package com.itheima.a07interfacedemo7;

public interface Inter {

    public abstract void method();

    public static void show(){
        System.out.println("Inter接口中的静态方法");
    }
}

InterImpl.java

当你 alt + 回车 ,可以发现它只需要你去重写抽象方法就行了,静态方法不需要你重写。

image-20240418160834951

package com.itheima.a07interfacedemo7;

public class InterImpl implements Inter{
    @Override
    public void method() {
        System.out.println("InterImpl重写的抽象方法");
    }
}

但是当你强行重写静态方法,就会报错:静态方法不能用@Override进行注释

image-20240418161019224

因为静态方法是不能被重写的。

如果你将 @Override 删掉,然后强行加一个 static,此时这个东西不叫重写。它只是刚好在实现类中跟接口有一个重名的方法而已

package com.itheima.a07interfacedemo7;

public class InterImpl implements Inter{
    @Override
    public void method() {
        System.out.println("InterImpl重写的抽象方法");
    }

    //不叫重写
    public static void show() {
        System.out.println("InterImpl重写的抽象方法");
    }
}

在调用的时候,如果你使用的是 InterImpl.show() ,那么调用的就是实现类中的静态方法。

如果你使用的是 Inter.show(),那么调用的就是接口中的静态方法。

Test.java

package com.itheima.a07interfacedemo7;


public class Test {
    public static void main(String[] args) {
        //调用接口中的静态方法
        Inter.show();

        //调用实现类中的静态方法
        InterImpl.show();

        // 此时这个东西不叫重写。它只是刚好在实现类中跟接口有一个重名的方法而已。
        // 在之前我们曾经说过:什么叫做重写?子类把从父类继承下来的虚方法表里面的方法进行覆盖了,这才叫重写。
        // 一定要有一个这样覆盖的过程,它才叫做重写。但是private、static、final修饰的,是不会添加到虚方法表中的。
        // 因此这里的写法不叫做重写,只是刚好在实现类中跟接口有一个重名的方法而已。
    }
}

六、JDK9新增的私有方法

1)推导

Java添加私有方法肯定是有它的原因的。

来看下面的这段代码,这段代码是我在接口中定义的两个默认方法:startend

这两段代码你发现,它会有个问题:有两句代码重复了。

image-20240418162801150

既然重复了,就需要将重复的代码抽取出来。

在JDK9以前,我们只能再写一个 default 方法,将重复的代码抽取到 log() 方法中。

image-20240418162931607

此时在左边的代码中直接调用对应的方法就可以了。

image-20240418163129411

这么操作虽然可以,但是会有个小问题:抽取出来的方法,它只是抽取了重复代码而已,这些代码它是为本接口其他方法去服务的,不想要让外类去访问,因为外类访问它是没有意义的,访问完里面的代码,它仅仅只是一些重复的代码而已。

当一个方法不想让别人去使用时,最好的办法就是把前面的权限修饰符变成 private

因此到到JDK9的时候,就提出了私有的方法,像这种抽取出来的重复代码,我们就可以定义在私有方法中。

私有方法它是分为两类的:

一个是大家看到的下图中,普通的私有方法。

image-20240418163835400

还有一个是加静态关键字去修饰的 静态的私有方法


2)接口中私有方法的定义格式

1、普通的私有方法的定义格式

PS:在 private返回值类型 之间,是不加 default 关键字的。

这个方法它是给默认方法服务的。

// 格式
private 返回值类型 方法名(参数列表){ }
// 范例
private void show(){ }

2、静态的私有方法的定义格式

这个方法它是给静态方法去服务的

// 格式
private static 返回值类型 方法名(参数列表){ }
// 范例
private static void method(){ }

3)代码实现

InterA.java

package com.itheima.a08interfacedemo8;

public interface InterA {
    public default void show1(){
        System.out.println("show1方法开始执行了");
        System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
    }

    public default void show2(){
        System.out.println("show2方法开始执行了");
        System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
    }
}

在JDK9以前,它只能重新写一个 默认方法 show3(),然后将重复的代码写到 show3() 中。

然后 show1()show2() 直接调用 show3() 方法即可。

package com.itheima.a08interfacedemo8;

public interface InterA {
    public default void show1(){
        System.out.println("show1方法开始执行了");
        show3();
    }

    public default void show2(){
        System.out.println("show2方法开始执行了");
        show3();
    }

    public default void show3(){
        System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
    }
}

但是有个小弊端,show3() 方法此时还是可以被外界调用的,但是这个方法被外界调用其实是没有任何意义的,因为它只是记录了一些重复的代码。

因此Java就觉得这种设计方案不好,在JDK9以后就提出来对应的解决方案:将权限修饰符写成 private 即可,然后将前面的 default 删掉即可,如果不删掉就会报错。

image-20240418165843833

这个就是普通的私有方法,它是给默认方法去服务的。

package com.itheima.a08interfacedemo8;

public interface InterA {
    public default void show1(){
        System.out.println("show1方法开始执行了");
        show3();
    }

    public default void show2(){
        System.out.println("show2方法开始执行了");
        show3();
    }

    private void show3(){
        System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
    }
}

但如果 show1()show2() 并不是默认方法,而是静态方法,那么下面就不能再去写这种普通的私有方法了。

而是书写静态的私有方法。静态的私有方法,是给静态方法服务的

package com.itheima.a08interfacedemo8;

public interface InterA {
    public static void show1(){
        System.out.println("show1方法开始执行了");
        show4();
    }


    public static void show2(){
        System.out.println("show2方法开始执行了");
        show4();
    }

   //普通的私有方法,是给默认方法服务的
    private void show3(){
        System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
    }

    //静态的私有方法,是给静态方法服务的
    private static void show4(){
        System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
    }
}

七、总结

1、JDK7以前:接口中只能定义抽象方法。

2、JDK8:它要考虑接口升级,因此在接口中可以定义有方法体的方法(这个方法分为两种:① 默认的(用 default 修饰);② 静态的(用 static 修饰))

3、JDK9:接口中可以定义私有方法。

私有方法的目的其实就是解决上面这两种(① 默认的(用 default修饰);② 静态的(用static 修饰)) 有重复的代码,需要抽取出来,但是抽取出来的代码又不想让外界提供,就可以使用 私有方法 解决。

4、私有方法分为两种:① 普通的私有方法;② 静态的私有方法。

普通的私有方法 它是给 默认方法 服务的。

静态的私有方法 它是给 静态方法 服务的。

image-20240418170922918

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值