• 万字详解java接口


    在这里插入图片描述

    一、初始接口

    1.1 什么是接口?

    接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
    在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
    在这里插入图片描述

    1.2 接口定义规则

    Interface关键字用来声明一个接口。

    interface 接口名称 {
            // 声明变量
            // 抽象方法
    }
    
    • 1
    • 2
    • 3
    • 4

    代码如下(示例):

    interface IInterface {
        //任何类型为 public static final 字段
        //任何类型位 public abstract 方法
    }
    
    • 1
    • 2
    • 3
    • 4

    1.接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
    2.接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
    3.接口中的方法都是公有的。
    4.接口中的方法和属性不要加任何修饰符号,保持代码简洁性.

    二、接口的使用

    接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
    定义一个Animal接口:

    //定义Animal接口
    interface Animal {
        void eat();
    }
    
    • 1
    • 2
    • 3
    • 4

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

    class Dog implements Animal {
    
        @Override
        public void eat() {
            System.out.println("吃狗粮!");
        }
    }
    class Cat implements Animal {
    
        @Override
        public void eat() {
            System.out.println("吃猫粮!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    测试一下接口是否支持多态:

    public static void func(Animal animal) {
            animal.eat();
        }
        public static void main(String[] args) {
            func(new Dog());
            func(new Cat());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    二、接口的特性

    1.接口类型是一种引用类型,但是不能直接new接口的对象

    public static void main(String[] args) {
            Animal animal = new Animal();
        }
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    2.接口中的每一个方法都是隐式指定为public abstract,如果设定为其他修饰符都会报错

    interface IInterface {
        protected void func();
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    3.接口中的方法不能有具体的实现

    interface Animal {
        void eat(){
            System.out.println("吃饭!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    4.重写接口方法时只能使用public访问限权修饰

    class Dog implements Animal {
    
        @Override
         void eat() {
            System.out.println("吃狗粮!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    5.接口中的变量默认为public static final变量

    interface Animal {
        int age = 18;
        void eat();
    }
    public static void main(String[] args) {
            System.out.println(Animal.age);//可以用接口名访问,证明是静态的
            Animal.age = 20;//无法修改,说明被final修饰
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    6.接口中不能有静态代码块和构造方法
    在这里插入图片描述
    在这里插入图片描述7.接口虽然不是类,但是在编译后生成的字节码文件后缀也是.class
    在这里插入图片描述
    8.如何一个类没有实现接口中的所有抽象方法,该类必须设置为抽象类.
    在这里插入图片描述
    在这里插入图片描述
    9.JDK1.8中,接口中可以包含default方法.
    在这里插入图片描述
    10.接口间的继承
    在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。
    接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字.

    interface IRunning {
        void running();
    }
    interface ISwimming {
        void swimming();
    }
    //两栖动物,既有跑,又有游的功能
    interface IAmphibious extends IRunning,ISwimming {
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    接口间的继承相当于把多个接口合并在一起.

    三、实现多接口

    java中,不支持多继承,但一个类可以实现多个接口.

    //定义一个Animal类
    class Animal {
        public String name;
        
        public Animal(String name) {
            this.name = name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    //定义一组会飞的,会跑的,会游泳的接口
    interface IFlying {
        void fly();
    }
    interface IRunning {
        void run();
    }
    interface ISwimming {
        void swim();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    我们创建几个具体的动物

    class Cat extends Animal implements IRunning {
        //猫是会跑的
        public Cat(String name) {
            super(name);
        }
        @Override
        public void run() {
            System.out.println(this.name+"正在跑");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    class Fish extends Animal implements ISwimming {
        //鱼会游泳
        public Fish(String name) {
            super(name);
        }
        @Override
        public void swim() {
            System.out.println(this.name+"正在游");
        }
    }
    ``
    
    ```java
    class Frog extends Animal implements IRunning,ISwimming {
        //青蛙会跑也会游
    
        public Frog(String name) {
            super(name);
        }
        @Override
        public void run() {
            System.out.println(name+"正在跑");
        }
    
        @Override
        public void swim() {
            System.out.println(name+"正在游");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
    有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.

    //我们实现一个方法,我们不用管到底是什么动物,只要会跑就可以调用.
    public static void run(IRunning iRunning) {
            iRunning.run();
        }
    
    • 1
    • 2
    • 3
    • 4
    //这里我们写一个机器人类
    class Robot implements IRunning {
        public String name;
        public Robot(String name) {
            this.name = name;
        }
    
        @Override
        public void run() {
            System.out.println(name + "正在跑");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
     public static void main(String[] args) {
            run(new Cat("喵喵"));
            run(new Robot("机器人"));
        }
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    在这里我们不用管是不是动物,只要具有这个功能即可调用.

    四、接口实现实例

    我们对Person类型数组进行排序.

    class Person {
        public String name;
        public int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
     public static void main(String[] args) {
            Person[] person = {new Person("张三",18),new Person("lisi",20),new Person("wangwu",21)};
            Arrays.sort(person);
            System.out.println(Arrays.toString(person));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    我们这里对Person数组进行排序,根据的是name,还是age?
    在这里插入图片描述在这里插入图片描述
    在系统的排序的方法中会将数组类型强制转换为Comparable,但我们的Person未实现Comparable接口,所以会报类转换异常.
    那让母后我们来让Person类实现Comparable功能.
    在这里插入图片描述
    我们在实现Comparable功能时,系统提示我们必须实现compareTo方法.

    @Override
        public int compareTo(Person o) {
            if(this.age > o.age) {
                return 1;
            }else if(this.age < o.age) {
                return -1;
            }else {
                return 0;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    现在我们试着排序一些Person数组
    在这里插入图片描述
    我们可以发现Person数组已经能够根据age进行排序了.
    这时候有同学就要问了:
    在这里插入图片描述
    这里为什么要这样书写,其实这个的Person是一个泛型,但大家不用去研究它,我们来看一下String的源码.
    在这里插入图片描述
    我们可以发现也是这样写的,我们只需要模仿就行了.
    大家想一下要是不想按age排序了,现在想实现按照name排序,那么我们该怎么去写?
    直接去修改CompareTo方法吗?这里CompareTo已经被其他程序调用过了,我们直接修改会造成程序紊乱,我们可以这样写:

    class NameComparator implements Comparator<Person> {
        @Override
        public int compare(Person o1, Person o2) {
            return o1.name.compareTo(o2.name);
        }
    }
    public static void main(String[] args) {
            Person[] person = {new Person("zhangsan",18),new Person("lisi",20),new Person("wangwu",21)};
            NameComparator nameComparator = new NameComparator();
            Arrays.sort(person,nameComparator);
            System.out.println(Arrays.toString(person));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    我们可以写一个类去实现Comparator接口去指定比较对象的内容.
    我们这里自己写一个冒泡排序,去按照Person的age去排序

    public static void BubbleSort(Comparable[] arr) {
            for (int i = 0; i < arr.length-1; i++) {
                for (int j = 0; j < arr.length-1-i; j++) {
                    if(arr[j].compareTo(arr[j+1]) > 0) {
                        Comparable tmp = arr[j];
                        arr[j] = arr[j+1];
                        arr[j+1] = tmp;
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            Person[] person = {new Person("zhangsan",18),new Person("lisi",20),new Person("wangwu",21)};
            BubbleSort(person);
            System.out.println(Arrays.toString(person));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    五、Clonable实现深浅拷贝

    当我们想实现对象的拷贝时,我们就需要实现Clonable接口.

    //定义一个学生类
    class Student implements Cloneable {
        public String name;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    我们可以发现Clonable接口是一个空接口,也可以称作标记接口.

    在这里插入图片描述
    我们发现Student类实现了Clonable接口但还是不能克隆.
    在这里插入图片描述
    我们在底层发现,clone()方法是protected修饰的,不同包只能通过子类super.调用,所以我们在Student必须重写这个方法.

    class Student implements Cloneable {
        public String name;
    
         @Override
         protected Object clone() throws CloneNotSupportedException {
             return super.clone();
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    我们重写之后发现,还是不能调用clone()方法,我们继续看底层代码.
    在这里插入图片描述
    我们可以发现父类抛了一个异常,那么我们也必须加上.
    在这里插入图片描述
    因为它返回的是一个Object类型的,我们必须强制转换为Student类型.

    public static void main(String[] args) throws CloneNotSupportedException {
            Student student = new Student();
            student.name = "张三";
            Student student1 = (Student) student.clone();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public static void main(String[] args) throws CloneNotSupportedException {
            Student student = new Student();
            student.name = "张三";
            Student student1 = (Student) student.clone();
            System.out.println(student);
            System.out.println(student1);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    我们打印了一下对象,这时候Student对象已经克隆成功,但这里只是浅拷贝.
    在这里插入图片描述

    class IScore {
        double score;
    }
     class Student implements Cloneable {
        public String name;
        public IScore iScore = new IScore();
    
         @Override
         protected Object clone() throws CloneNotSupportedException {
             return super.clone();
         }
    
         @Override
         public String toString() {
             return "Student{" +
                     "name='" + name + '\'' +
                     '}';
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    public static void main(String[] args) throws CloneNotSupportedException {
            Student student = new Student();
            student.name = "张三";
            Student student1 = (Student) student.clone();
            student.iScore.score = 100;
            System.out.println("student: "+student.iScore.score);
            System.out.println("student1: "+student1.iScore.score);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    我们可以发现在克隆后,修改student的score,student1的score也被修改了.
    在这里插入图片描述
    我们发现,两个对象指向了同一块Score,所以我们要将Score也进行克隆.

    class IScore implements Cloneable{
        double score;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
     class Student implements Cloneable {
        public String name;
        public IScore iScore = new IScore();
    
         @Override
         protected Object clone() throws CloneNotSupportedException {
             Student student = (Student)super.clone();
             student.iScore = (IScore)this.iScore.clone();
             return student;
         }
         }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述
    在这里插入图片描述
    这样就可以实现深拷贝.

    六、抽象类与接口的区别

    1、抽象类中可以包含普通方法,但接口中只能包含public与abstract方法(JDK 1.8之前),JDK1.8之后允许接口中出现default方法;
    2、抽象类中的成员变量没有访问权限的限制,但接口中的变量只能被public static final修饰;
    3、一个接口可以继承多个接口,但一个类只能有一个父类,类可以实现多个接口;
    4、抽象类是对一类事物的抽象,接口则是对行为的抽象。一个类继承一个抽象类代表“是不是”的关系,而一个类实现一个接口则表示“有没有”的关系。
    核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.

  • 相关阅读:
    FFmpeg编译安装(windows环境)以及在vs2022中调用
    Git—版本控制系统
    java---Stream流
    6.6 - Windows网络相关命令
    编写Android.mk / Android.bp 引用三方 jar 包,aar包,so 库
    了解公司开发项目的容器化部署,会这一招就够
    考研算法题练习2022.11.16
    【双指针+简化去重操作】Leetcode 15 三数之和
    Java基础知识&面试题总结(下)
    mathlab 数据维度调换函数permute()
  • 原文地址:https://blog.csdn.net/buhuisuanfa/article/details/126528537