【Java】嚼烂基础之接口


前言

我们对Java对象和多态的概念了一定的了解,今天我们就来深入学习Java面向对象魅力之“接口”。本篇文章将详细介绍Java语言中的接口interface概念,包括使用方法、特性、使用实例、抽象类与接口的区别,话不多说,让我们开始吧!!
在这里插入图片描述


一、接口

1.1 接口的概念

一说到接口,相比会很自然地联想到现实生活中的[接口],比如:笔记本上的USB口,耳机孔,电源插座等等。
在这里插入图片描述
在这里插入图片描述

电脑的USB口上,可以插:U盘、鼠标、键盘…所有符合USB协议的设备
电源插座插孔上,可以插:冰箱、电脑电源线、电视机…所有符合规范的设备
总结下来不难发现:接口就是公共的行为规范标准,大家在实现时,只要符合规范,就可以通用。

而今天我们Java语言中的接口也异曲同工。
在JAVA中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

1.2 接口的语法规则

接口的定义格式和定义的格式基本相同,将class关键字换成interface关键字就成功定义了一个接口。

public interface 接口名称{
	//抽象方法
	public abstract void method1();
	//public abstract是默认搭配,可以不写
	void method2();
	void method3();
}

注意:

  1. 创建接口时,接口的命名一般以大写字母I开头
  2. 接口的命名一般使用形容词词性的单词
  3. 阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性

1.3 接口的使用

接口不能直接使用,必须要有一个“实现类”来“实现”该接口,实现接口中的所有抽象方法。

规则如下所示:

public class 子类名 implements 接口名称{
	// ...
}

子类和父类之间是extends 继承关系
类和接口之间是implements 实现关系

1.3.1 接口运用具体样例

请实现笔记本电脑使用USB鼠标、USB键盘的例子

  1. USB接口:包含打开设备、关闭设备功能
  2. 笔记本类:包含开机功能、关机功能、使用USB设备功能
  3. 鼠标类:实现USB接口,并具备点击功能
  4. 键盘类:实现USB接口,并具备输入功能

代码如下:

interface IUSB{
    void OpenDevice();
    void CloseDevice();
}
class Mouse implements IUSB{
    @Override
    public void OpenDevice() {
        System.out.println("打开鼠标!");
    }

    public void click(){
        System.out.println("鼠标点击!");
    }
    @Override
    public void CloseDevice() {
        System.out.println("关闭鼠标!");
    }
}

class KeyBoard implements IUSB{

    @Override
    public void OpenDevice() {
        System.out.println("打开键盘!");
    }
    public void input(){
        System.out.println("键盘输入!");
    }
    @Override
    public void CloseDevice() {
        System.out.println("关闭键盘!");
    }
}
class Computer{
    public void PowerOn() {
        System.out.println("打开计算机!");
    }
    public void PowerOff() {
        System.out.println("关闭计算机!");
    }
    public void UseDevice(IUSB iusb){
        iusb.OpenDevice();
        if (iusb instanceof Mouse){
            Mouse mouse = (Mouse)iusb;
            mouse.click();
        }else if (iusb instanceof KeyBoard){
            KeyBoard keyBoard = (KeyBoard) iusb;
            keyBoard.input();
        }
        iusb.CloseDevice();
    }
}

public class test {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.PowerOn();
        //使用鼠标
        computer.UseDevice(new Mouse());
        computer.UseDevice(new KeyBoard());
        //使用电脑
        computer.PowerOff();
    }
}

编译结果如下:
在这里插入图片描述

1.4 接口的特性

  • 接口类型是一种引用类型,但是不能直接new接口的对象
    在这里插入图片描述

  • 接口中的每一个方法都是public修饰的抽象方法,即在接口中的方法会被隐式指定为public abstract(只能是public abstract,其他的会报错)
    在这里插入图片描述

  • 接口中的方法是不能在接口中实现的,只能实现接口的类实现
    在这里插入图片描述

  • 重写接口中方法时,不能使用普通类中方法的默认访问权限

  • 接口中可以含有变量,但是会被隐式的指定为public static final变量

  • 接口中不能有静态代码块构造方法
    在这里插入图片描述

  • 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
    在这里插入图片描述

  • 如果类没有实现接口中的所有抽象方法,则该类必须设置为抽象类

  • jdk8中:接口还可以包含default方法。

1.4.1 实现多个接口

在Java中,类与类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承
但是!一个类可以实现多个接口
下面来看一段关于动物类的代码:

先创建一个动物类

class  Animal{
    protected String name;
    public Animal(String name){
        this.name = name;
    }
}

再创建相关的接口

 //有的会游
public interface ISwim {
    void swim();
}
//有的会跑
public interface IRun {
    void run();
}
//有的会飞
public interface IFly {
    void fly();
}

创建一个会跑的狗类

class Dog extends Animal implements IRun{
    public Dog(String name){
        super(name);
    }
    @Override
    public void run(){
        System.out.println(this.name+"正在奔跑.....");
    }
}

创建一个海陆空都会的鸭鸭类

class Duck extends Animal implements ISwim,IRun,IFly{
    public Duck(String name) {
        super(name);
    }

    @Override
    public void fly() {
        System.out.println(this.name+"正在飞.....");
    }

    @Override
    public void run() {
        System.out.println(this.name+"正在跑.....");
    }

    @Override
    public void swim() {
        System.out.println(this.name+"正在游.....");
    }
}

上面的代码展示了Java面向对象编程中最常见的用法:一个类继承一个父类,同时实现多个接口

根据上面的例子我们发现:继承表达的含义是is-a的语义,而接口表达的含义是具有xxx特性

狗是动物,具备奔跑的能力
鸭子是动物,会,会,也能游

这样设计非常的巧妙!有了接口之后,类的使用者就不用关注具体类型,只需关注这个类是否具备某种能力。

比如上面的动物类,有跑的接口,有飞的接口。
我们换个角度想,飞机!他也能飞,omg那是不是也可以实现IFly的接口?!
这就是巧妙所在!

1.5 接口间的继承

在Java中,类和类之间是单继承的,一个类可以实现多个接口,而接口与接口之间可以多继承。即:用接口可以间接达到多继承的目的

我们试试优化下上面的代码:

public interface ISwim {
    void swim();
}
//有的会跑
public interface IRun {
    void run();
}
//有的会飞
public interface IFly {
    void fly();
}
//鸭子能海陆空都可以
interface IAmphibious extends IRun,IFly,ISwim{

}//使用extends关键字实现接口复用的效果

class Duck extends Animal implements IAmphibious{
    public Duck(String name) {
        super(name);
    }

    @Override
    public void fly() {
        System.out.println(this.name+"正在飞.....");
    }

    @Override
    public void run() {
        System.out.println(this.name+"正在跑.....");
    }

    @Override
    public void swim() {
        System.out.println(this.name+"正在游.....");
    }
}

通过接口继承创建一个新的接口IAmphibious(两栖类的),此时实现接口创建的Duck类,要把两栖类中的所有抽象类方法全部重写,但接口选择更为明确。

接口间的继承相当于把多个接口合并到一个中

1.6 Clonable接口和深拷贝

Java中内置了一些很有用的接口,比如Clonable。
clonable接口是Object类中的方法,功能是完全复制拷贝一个类的对象,但是实现的过程中有些注意的地方,其中就有浅拷贝与深拷贝

1.6.1Clonable接口

Object类中存在一个clone方法,调用这个方法可以创建一个对象的“拷贝”,但想要合法调用clone方法,必须要先实现Clonable接口,否则就会抛出 CloneNotSupportedException异常。

举个例子:

class Money{
    public double money = 12.5;
}

class Person implements Cloneable{
    public String name;
    public int age;
    public Money m;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        m = new Money();
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


public class test1  {
    public static void main(String[] args) throws CloneNotSupportedException {
    									    ⬆ 这里属于异常章节内容,暂且不详细介绍,先用着^^
        Person person1 = new Person("zhangsan",13);
        Person person2 = (Person)person1.clone();
        System.out.println(person1.toString());
        System.out.println(person2.toString());
    }
}

输出结果:
在这里插入图片描述

1.6.2浅拷贝与深拷贝

下面我们通过观察一段代码来了解【浅拷贝与深拷贝】

package demo1;

class Money{
    public double money = 12.5;
}

class Person implements Cloneable{
    public String name;
    public int age;
    public Money m;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        m = new Money();
    }
    

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


public class test1  {
    public static void main(String[] args) throws CloneNotSupportedException  {
        Person person1 = new Person("zhangsan",13);
        Person person2 = (Person)person1.clone();
        System.out.println("修改之前" + person1.m.money);
        System.out.println("修改之前" + person2.m.money);
        person2.m.money = 99;
        System.out.println("修改之后" + person1.m.money);
        System.out.println("修改之后" + person2.m.money);

    }
}

输出结果如下:
在这里插入图片描述

在其中我们对person2的money进行了修改,预期中我们应该输出12.5和99
但我们发现person1的money也被修改了,这是为什么呢?

在这里插入图片描述

我们person中的money在被克隆clone的时候,被克隆的其实是用来指向money对象的地址,不会重新给person2创建克隆一个money对象。
最后实现的效果就是person1与person2的m指向的是同一块money空间,所以会出现多个更改的情况。

那么我们想要实现只对一个对象进行更改需要做的就是:给money也实现克隆方法

进行代码更新:

class Money implements Cloneable{
    public double money = 12.5;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable{
    public String name;
    public int age;
    public Money m;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person)super.clone();
        tmp.m = (Money)this.m.clone();
        return tmp;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        m = new Money();
    }


    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


public class test1  {
    public static void main(String[] args) throws CloneNotSupportedException  {
        Person person1 = new Person("zhangsan",13);
        Person person2 = (Person)person1.clone();
        System.out.println("修改之前" + person1.m.money);
        System.out.println("修改之前" + person2.m.money);
        person2.m.money = 99;
        System.out.println("修改之后" + person1.m.money);
        System.out.println("修改之后" + person2.m.money);

    }

运行结果:
在这里插入图片描述

PS:可以观察到,是不是深拷贝,就看程序员实现的怎么样了🥹🥹

【总结】:

如果一个类实现了Clonable这个空接口/标记接口,那么证明这个类是可以被克隆的
深拷贝:完全克隆出一个独立于原来对象的对象
浅拷贝:克隆一个原来对象的对象,但可能不完整

1.7 抽象类与接口的区别

核心区别
抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不用重写!)。
接口中不能包含普通方法,子类必须重写所有的抽象方法。
在这里插入图片描述


结语

好了以上就是本篇“【Java】嚼烂基础之接口”博客的全部内容啦,感谢各位的阅读=v=,如有不足之处欢迎在评论区指出哦!!
觉得可以的话别忘了点赞三连支持一下欧!拜托啦这对我真的很重要o(>ω< )o!!!

请添加图片描述

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值