帶你搞懂Java的介面(範例詳解)

2022-04-19 19:00:36
本篇文章給大家帶來了關於的相關知識,其中主要介紹了關於介面的相關問題,包括了介面的概念以及一些知識點彙總、語法規則、介面的使用以及介面的特性等等,下面一起來看一下,希望對大家有幫助。

推薦學習:《》

介面

一圖流
image.png

介面的概念以及一些知識點彙總

介面(英文:Interface),在JAVA程式語言中是一個抽象型別,是抽象方法的集合,介面通常以interface來宣告。一個類通過繼承介面的方式,從而來繼承介面的抽象方法。
介面並不是類,編寫介面的方式和類很相似,但是它們屬於不同的概念。類描述物件的屬性和方法。介面則包含類要實現的方法
除非實現介面的類是抽象類,否則該類要定義介面中的所有方法
介面無法被範例化,但是可以被實現。一個實現介面的類,必須實現介面內所描述的所有方法,否則就必須宣告為抽象類。另外,在 Java 中,介面型別可用來宣告一個變數,他們可以成為一個空指標,或是被繫結在一個以此介面實現的物件。

介面與類的相同處

  • 一個介面中可以有多個
  • 介面檔案儲存在.Java結尾的檔案中,檔名使用介面名
  • 介面的位元組碼檔案儲存在.class結尾的檔案中
  • 介面相應的位元組碼檔案必須在與包名稱相匹配的目錄結構中

介面與類的不同處

  • 介面不能用於範例化物件
  • 介面沒有構造方法
  • 介面中所有的方法必須是抽象方法,在Java8之後介面中可以使用default關鍵字修飾的非抽象方法
  • 介面不能包含成員變數,除了static和final變數
  • 介面被類繼承這個概念不準確,準確來說應該是要被類實現
  • 介面可以實現我們所說的多繼承

介面的一些特點

  • 介面中每一個方法也是隱式抽象的,所以介面中的方法會被隱式得指定為public abstract (只可以是public abstract,其他修飾符都會報錯)
  • 介面中含有變數,但是介面中得變數會被隱式的指定為public static final 變數(並且只能是public,用private修飾會報編譯錯誤)
  • 介面中的方法是不能在介面中實現的,只能由實現介面的類來實現介面中的方法

抽象類和介面的區別

在JDK1.8以前,它們有如下區別

  • 抽象類中的方法可以有具體可執行的語句,即方法體,就是能實現方法的具體功能,但是介面中的方法就不行(比如:System.out.println(「I’m super corn!!」);
  • 抽象類中的成員變數可以是各種型別的,而介面中的成員變數只能是public static final型別的
  • 介面中不能含有靜態程式碼塊以及靜態方法的使用(用static修飾的方法),而抽象類可以有靜態程式碼塊和靜態方法
  • 一個類只能繼承一個抽象類,而一個類卻可以實現多個介面

那麼這裡要注意的是:
JDK1.8以後,介面中允許含有靜態方法和方法體,允許包含具體實現的方法,該方法我們稱之為「預設方法」,這種方法使用default關鍵字來修飾
JDK1.9以後,允許將方法定義為private,使某些複用的程式碼不會將方法暴露出去
抽象類存在的意義是為了讓編譯器更好地校驗,一般抽象類我們不會直接使用,而是使用它的子類,如果不小心通過抽象類建立了物件,編譯器就會及時提醒我們。

注意:以上內容大致瀏覽一遍即可,看不懂沒關係,下面我將為你一一講解,然後回頭再來看這些知識點將會有一種大夢初醒的感覺


那麼在現實生活中,介面是什麼呢?它可以是筆電上的USB口,電源插座等
image.png
image.png
那麼這些介面在實現意義上以及使用標準上也有所不同

  • 電腦的USB口上,可以插:U盤、滑鼠、鍵盤…所有符合USB協定的裝置
  • 電源插座插孔上,可以插:電腦、電視機、電飯煲…所有符合規範的裝置

通過上述的例子我們就可以看出:介面就是公共的行為規範標準,大家在實現時,只要符合規範標準,就可以通用。在Java中,介面可以看成是:多個類的公共規範,是一種參照資料型別

語法規則

介面的定義格式與定義類的格式基本上相同,將class關鍵字換成interface關鍵字就定義了一個介面。

public interface 介面名稱{
    //抽象方法
    public abstract void method1();
    //public abstract是固定搭配,可以不寫
    public void method2();
    abstract void method3();
    void method4();
    
    //注意:在介面中上述的寫法都是抽象方法,所以method4這樣寫程式碼更整潔}

提示:

  1. 建立介面時,介面的命名一般以大寫字母I(讀ai)開頭
  2. 介面的命名一般使用形容詞詞性的單詞
  3. 阿里編碼規範中約定,介面中的方法和屬性不要加任何修飾符,保持程式碼的整潔性

介面的使用

介面不能直接範例化使用,必須要有一個類去實現它,實現介面中所有的抽象方法

public class 類名稱 implements 介面名稱{
    //...}

注意:子類和父類別之間是extends繼承關係,類與介面之間是implements實現關係。

筆記型電腦中使用USB滑鼠,USB鍵盤的類和介面實現功能

  1. USB介面:包含開啟裝置、關閉裝置的功能
  2. 筆電類:包含開關機功能、使用USB裝置功能
  3. 滑鼠類:實現USB介面,並具備點選功能
  4. 鍵盤類:實現USB介面,並具備輸入功能
//USB介面public interface USB{
    void openDevice();
    void closeDevice();}//滑鼠類,實現USB介面public class Mouse implements USB{
    @Override
    public void openDevice(){
        System.out.println("開啟滑鼠");
    }
    
    @Override
    public void closeDevice(){
        System.out.println("關閉滑鼠");
    }
    public void click(){
        System.out.println("滑鼠點選");
    }}//鍵盤類,實現USB介面public class KeyBoard implements USB {
    @Override
    public void openDevice(){
        System.out.println("開啟鍵盤");
    }
    
    @Override
    public void closeDevice(){
        System.out.println("關閉鍵盤");
    }
    
    public void inPut(){
        System.out.println("鍵盤輸入");
    }}//筆電類:使用USB裝置public class Computer {
    public void powerOn(){
        System.out.println("開啟筆記型電腦");
    }
    
    public void powerOff(){
        System.out.println("關閉筆記型電腦");
    }
    public void useDevice(USB usb){
        usb.openDevice();
        if(usb instanceof Mouse){
            Mouse mouse = (Mouse)usb;
            mouse.click();
        }else if(usb instanceof KeyBoard){
            KeyBoard keyBoard = (KeyBoard)usb;
            keyBoard.inPut();
        }
        usb.closeDevice();
    }}//測試類:public class TestUSB{
    public static void main(String[] args){
        Computer computer = new Computer();
        computer.powerOn();
   
    //使用滑鼠裝置
    computer.useDevice(new Mouse());
    
    //使用鍵盤裝置
    computer.useDevice(new KeyBoard());
    
    computer.powerOff();
    }}

輸出:
image.png

instanceof

上面的程式碼範例中,提到了instanceof,可能有小夥伴不太理解,我在前面的部落格中有介紹,這裡再重新為大家講解一下
instanceof是Java的一個保留關鍵字,左邊為物件,右邊為類,返回型別是Boolean型別。
它的具體作用是測試左邊的物件是否是右邊類或者右邊類的子類建立的範例化物件
如果是,則返回true,否則返回false
【instanceof使用注意事項】
現有繼承關係,再有instanceof的使用(包括介面的實現)

【instanceof應用場景】
需要用到物件的強制型別轉換時,需要使用instanceof進行判斷

介面的特性

  1. 介面型別是一種參照型別,但是不能直接new介面的物件
public class TestUSB {
    public static void main(String[] args){
        USB usb = new USB();
    }}//編譯會出錯:USB是抽象的,無法範例化

image.png

  1. 介面中每一個方法都是public的抽象方法,即介面中的方法會被隱式地指定為public abstract(只能是public abstract,其他修飾符都會報錯)
public interface USB {
    //編譯出錯:此處不允許使用修飾符private
    //或者是java: 缺少方法主體, 或宣告抽象
    private void openDevice();
    void closeDevice();
    //不同JDK版本編譯器的標準是不一樣的,報錯也是不一樣的}
  1. 介面中的方法是不能在介面中實現的,只能由實現介面的類來實現
public interface USB {
    void openDevice();
    
    //編譯失敗:因為介面中的方法預設為抽象方法
    //Error:介面抽象方法不能帶有主體}

image.png
但這裡如果我們加上一個default,那麼就可以實現方法體了。
image.png

  1. 重寫介面中的方法時,不能使用default作為存取許可權修飾
public interface USB {void openDevice();//預設為publicvoid closeDevice();//預設為public}public class Mouse implements USB {
    @Override
    void openDevice(){
        System.out.println("開啟滑鼠");
    }
    
    //...}//這裡編譯會報錯,重寫USB中的openDevice方法時,不能使用預設修飾符

image.png
實現這個介面,重寫這個介面的方法的存取限定修飾符範圍要比介面中的更大

  1. 介面中可以含有變數,但是介面中的變數會被編譯器自動隱式指定為public static final變數
public interface USB {
    double brand = 3.0;//預設為:final public static修飾
    void openDevice();
    void closeDevice();}public class TestUSB {
    public static void main(String[] args){
        System.out.println(USB.brand);
        //可以直接通過介面名存取,說明變數時靜態的
        
        //下面寫法會報錯 Java:無法為最終變數brand分配值
        USB.brand = 2.0;
        //說明brand具有final屬性
    }}

image.png

  1. 介面中不能有靜態程式碼塊和構造方法
public interface USB {
    public USB(){
    
    }//編譯失敗
    
    {
    
    }//編譯失敗
    
    void openDevice();
    void closeDevice();}

image.png

  1. 介面雖然不是類,但是介面編譯完成之後的位元組碼檔案的字尾格式也是.class
  2. 如果類沒有實現介面中的所有抽象方法,則類必須設定為抽象類
  3. JDK8中規定了介面中可以包含上面所說的default方法

實現多個介面

在Java中,類和類之間是單繼承的,一個類只能由一個父類別,即Java中不支援多繼承,但是一個類可以實現多個介面。下面用程式碼來演示

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

然後我們再寫一組介面,分別來表示「會飛的」「會跑的」「會游泳的」.

public interface IFlying {
    void fly();}public interface IRunning {
    void run();}public interface ISwimming {
    void swim();}

image.png
那麼接下來我們建立幾個具體的動物類來接受並實現這些介面
比如,貓會跑

public class Cat extends Animal implements IRunning{
    public Cat(String name) {
        super(name);
    }
    
    @Override
    public void run() {
        System.out.println("小貓"+this.name+"正在跑");
    }}

魚會游泳

public class Fish extends Animal implements ISwimming{
    public Fish(String name){
     super(name);   
    }
    
    @Override
    public void swim() {
        System.out.println("小魚"+this.name+"正在游泳");
    }}

而青蛙即會跑又會游泳

public class Frog extends Animal implements IRunning,ISwimming{
    public Frog(String name){
        super(name);
    }
    
    @Override
    public void run() {
        System.out.println("青蛙"+this.name+"正在跑");
    }

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

注意:一個類實現多個介面的時候,每個介面中的抽象方法都要去實現,除非類用abstract修飾,為抽象類

提示IDEA中使用ctrl + i 可以快速實現介面

還有一種動物水陸空三棲,它是大白鵝

public class Goose extends Animal implements IRunning,ISwimming,IFlying{
    public Goose(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的特性

貓是一種動物,具有會跑的特性
青蛙是一種動物,即能跑也能有用
大白鵝也是一種動物,技能跑,也能遊,還能飛

有了介面之後,類的使用者就不需要去關注具體的類的屬性是否符合,而只需要關心某個類是否具有某個特性/功能,如果有,就可以實現對應的介面
那麼我們現在實現一個走路的方法

public class TestDemo1 {
    public static void walk(IRunning iRunning){
        System.out.println("我帶著小夥伴去散步");
        iRunning.run();
    }

    public static void main(String[] args) {
        Cat cat = new Cat("小貓");
        walk(cat);
        
        Frog frog = new Frog("小青蛙");
        walk(frog);
    }}

輸出結果
image.png
只要是會跑的,帶有跑這個屬性特徵的,都可以接受相應的物件

public class Robot implements IRunning{
    private String name;
    public Robot(String name){
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println(this.name+"正在用輪子跑");
    }

    public static void main(String[] args) {
        Robot robot = new Robot("機器人");
        walk(robot);
    }}

image.png
image.png
故輸出結果為
image.png

介面之間的繼承

在Java中,類和類之間是單繼承的,一個類可以實現多個介面,介面與介面之間可以多繼承。
即:用介面可以達到多繼承的目的
介面可以繼承一個介面,達到複用的效果。這裡使用extends關鍵字

interface IRunning {
    void run();}interface ISwimming {
    void swim();}//兩棲的動物,即能跑,也能游泳interface IAmphibious extends IRunning ISwimming {}class Frog implements IAmphibious {
    ...}

通過介面繼承建立一個新的介面IAmphibious表示「兩棲的」。
建立的Frog類就實現了這個兩棲的介面

介面之間的繼承就相當於把多個介面合併到了一起

介面使用的例子

我們在之前的陣列中講解過給陣列排序,那麼我們該如何給物件陣列排序呢?
首先我們定義一個Student的類,然後重寫一下String方法

public class Student {
    private String name;
    private int score;
    public Student(String name,int score){
        this.name = name;
        this.score = score;
    }

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

我們再給定一個學生物件陣列,根據這個物件陣列中的元素進行排序
這裡我們按照分數降序排序

public class Student {
    private String name;
    private int score;
    public Student(String name,int score){
        this.name = name;
        this.score = score;
    }

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

    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("A",95),
                new Student("B",96), 
                new Student("C",97),
                new Student("D",98),
        };
    }}

那麼按照我們之前的理解,陣列中有一個可以供我們使用的sort方法,我們能否直接使用呢?

Arrays.sort(students);System.out.println(students);//執行結果:Exception in thread "main" java.lang.ClassCastException: class ClassArray.Student cannot be cast to class java.lang.Comparable (ClassArray.Student is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
	at java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
	at java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
	at java.base/java.util.Arrays.sort(Arrays.java:1041)
	at ClassArray.Student.main(Student.java:36)

image.png
我們可以看到這裡程式報錯了,這裡的意思是Student並沒有實現Comparable的介面
那麼這裡的sort是進行普通數位的比較,大小關係明確,而我們指定的是兩個學生物件的參照變數,這樣的大小關係的指定是錯誤的,我們需要額外去人為規定物件中的比較元素
那麼怎麼實現呢?

我們可以用Student類實現Comparable介面,並實現其中的compareTo方法

public class Student implements Comparable<Student>{
    private String name;
    private int score;

    public Student(String name,int score){
        this.name = name;
        this.score = score;
    }

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

    @Override
    public int compareTo(Student o) {
        if (this.score>o.score){
            return -1;//      如果當前物件應排在引數物件之前,則返回小於0的數位
        } else if(this.score<o.score){
            return 1;//      如果當前物件應排在引數物件之後,則返回大於0的數位
        } else{
            return 0;//      如果當前物件和引數物件不分先後,則返回0      
        }
        
    }}

那麼我們在這裡重寫了compareTo的方法,自己定義了比較的規則,我們就自己再去寫一個sort的方法,去呼叫這個compareTo方法,真正意義上實現對 物件陣列的排序
我們使用氣泡排序法

    public static void sort(Comparable[] array){//        這裡要注意,雖然介面不能範例化物件,//        但是介面型別的參照變數可以指向它的實現類物件//        這裡的實現類物件就是實現了這個介面的物件//        例如Comparable[] comparable = new Student[3];//        所以這裡的引數就可以用Comparable[] array來接收
        for (int bound = 0;bound<array.length;bound++){
            for (int cur = array.length-1;cur>bound;cur--){
                if (array[cur-1].compareTo(array[cur])>0){
                    //這裡就說明順序不符合要求,交換兩個變數的位置
                    Comparable tmp = array[cur-1];
                    array[cur-1] = array[cur];
                    array[cur] = tmp;
                }
            }
    }}

sort方法寫好了,我們寫一個main函數來測試一下

    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("A",95),
                new Student("B",91),
                new Student("C",97),
                new Student("D",95),
        };
        System.out.println("sort前:"+Arrays.toString(students));
        sort(students);
        System.out.println("sort後:"+Arrays.toString(students));
    }

執行結果

E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=65257:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\Interface\out\production\Interface ClassArray.Studentsort前:[Student{name='A', score=95}, Student{name='B', score=91}, Student{name='C', score=97}, Student{name='D', score=95}]sort後:[Student{name='C', score=97}, Student{name='A', score=95}, Student{name='D', score=95}, Student{name='B', score=91}]

那麼我們如果想要按照名字排序呢?也是可以的

import java.util.Arrays;import java.util.Comparator;/**
 * Created with IntelliJ IDEA.
 * Description: Hello,I would appreciate your comments~
 * User:Gremmie
 * Date: -04-13
 * Destination:利用Comparable的介面實現對 物件陣列 選擇性排序的功能
 */class Student implements Comparable<Student>{
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public int compareTo(Student o) {
        return this.name.compareTo(o.name);
    }}class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age-o2.age;
    }}class NameComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }}public class TestDemo {

    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",19);
        students[1] = new Student("lisi",8);
        students[2] = new Student("abc",78);
        AgeComparator ageComparator = new AgeComparator();
        NameComparator nameComparator = new NameComparator();
        
        
        //這裡的方法sort是Array裡面自帶的,非常方便,
        //只需將我們寫好的比較器傳過去就好了
        System.out.println("排序前:"+Arrays.toString(students));
        Arrays.sort(students,nameComparator);
        System.out.println("排序後:"+Arrays.toString(students));
        Comparable<Student>[] studentComparable =students;
    }

    public static void main2(String[] args) {
        /*Student students1 = new Student("zhangsan",19);
        Student students2 = new Student("abc",78);
        if(students2.compareTo(students1) > 0) {
            System.out.println("fafaa");
        }*/


    }
    public static void main1(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",19);
        students[1] = new Student("lisi",8);
        students[2] = new Student("abc",78);
        System.out.println("排序前:"+Arrays.toString(students));
        Arrays.sort(students);
        System.out.println("排序後:"+Arrays.toString(students));
    }}

Clonable介面以及深拷貝

其作用如其名,是用來進行克隆的,Clonable是個很有用的介面。
Object類中存在一個clone方法,呼叫這個方法可以建立出一個物件,實現「拷貝」。
但是我們想要合法呼叫clone方法,就要先實現Clonable介面,
否則就會丟擲CloneNotSupportedException異常

/**
 * Created with IntelliJ IDEA.
 * Description: Hello,I would appreciate your comments~
 * User:Gremmie
 * Date: -04-13
 * Destination:利用Clonable的介面實現clone方法,克隆含物件的物件
 */class Money implements Cloneable{
    public double money = 19.9;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }}class Person implements Cloneable{
    public int id = 1234;
    public Money m = new Money();

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

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

    public static void main(String[] args) {
        Object o = new Person();

        Object o2 = new Money();


    }

    public static void main1(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person)person1.clone();
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
        System.out.println("=========================");
        person2.m.money = 99.99;
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
    }}

我們如果只是通過clone,那麼就只是拷貝了Person的物件,但是Person中的money物件我們並沒有拷貝下來,只是單純拷貝下來一個地址,那麼我們在這裡就要進行深拷貝,講Money類也接受Clonable介面,這樣在呼叫clone方法的時候,money也會進行克隆

推薦學習:《》

以上就是帶你搞懂Java的介面(範例詳解)的詳細內容,更多請關注TW511.COM其它相關文章!