首頁 >Java >java教程 >帶你去搞清楚Java的介面(實例詳解)

帶你去搞清楚Java的介面(實例詳解)

WBOY
WBOY轉載
2022-04-19 17:55:003792瀏覽

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

帶你去搞清楚Java的介面(實例詳解)

推薦學習:《java影片教學

介面

##一圖流


帶你去搞清楚Java的介面(實例詳解)

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

介面(英文: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口,電源插座等
帶你去搞清楚Java的介面(實例詳解)
帶你去搞清楚Java的介面(實例詳解)
那麼這些介面在實作意義上以及使用標準上也有所不同

  • #電腦的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();
    }}

輸出:
帶你去搞清楚Java的介面(實例詳解)

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是抽象的,无法实例化

帶你去搞清楚Java的介面(實例詳解)

  1. 介面中每一個方法都是public的抽象方法,即介面中的方法會被隱含地指定為public abstract(只能是public abstract,其他修飾符都會報錯)
public interface USB {
    //编译出错:此处不允许使用修饰符private
    //或者是java: 缺少方法主体, 或声明抽象
    private void openDevice();
    void closeDevice();
    //不同JDK版本编译器的标准是不一样的,报错也是不一样的}
  1. 介面中的方法是不能在介面中實作的,只能由實作介面的類別來實作
public interface USB {
    void openDevice();
    
    //编译失败:因为接口中的方法默认为抽象方法
    //Error:接口抽象方法不能带有主体}

帶你去搞清楚Java的介面(實例詳解)
但這裡如果我們加上一個default,那就可以實作方法體了。
帶你去搞清楚Java的介面(實例詳解)

  1. 重寫介面中的方法時,不能使用default作為存取權限修飾
public interface USB {void openDevice();//默认为publicvoid closeDevice();//默认为public}public class Mouse implements USB {
    @Override
    void openDevice(){
        System.out.println("打开鼠标");
    }
    
    //...}//这里编译会报错,重写USB中的openDevice方法时,不能使用默认修饰符

帶你去搞清楚Java的介面(實例詳解)
實作這個接口,重寫這個介面的方法的存取限定修飾符範圍要比介面中的更大

  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属性
    }}

帶你去搞清楚Java的介面(實例詳解)

  1. #介面中不能有靜態程式碼區塊與建構方法
public interface USB {
    public USB(){
    
    }//编译失败
    
    {
    
    }//编译失败
    
    void openDevice();
    void closeDevice();}

帶你去搞清楚Java的介面(實例詳解)

#
  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();}

帶你去搞清楚Java的介面(實例詳解)
那么接下来我们创建几个具体的动物类来接受并实现这些接口
比如,猫会跑

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);
    }}

输出结果
帶你去搞清楚Java的介面(實例詳解)
只要是会跑的,带有跑这个属性特征的,都可以接受相应的对象

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);
    }}

帶你去搞清楚Java的介面(實例詳解)
帶你去搞清楚Java的介面(實例詳解)
故输出结果为
帶你去搞清楚Java的介面(實例詳解)

接口之间的继承

在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)

帶你去搞清楚Java的介面(實例詳解)
我们可以看到这里程序报错了,这里的意思是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><p>那么我们在这里重写了compareTo的方法,自己定义了比较的规则,我们就自己再去写一个sort的方法,去调用这个compareTo方法,真正意义上实现对 对象数组的排序<br>我们使用冒泡排序法</p>
<pre class="brush:php;toolbar:false">    public static void sort(Comparable[] array){//        这里要注意,虽然接口不能实例化对象,//        但是接口类型的引用变量可以指向它的实现类对象//        这里的实现类对象就是实现了这个接口的对象//        例如Comparable[] comparable = new Student[3];//        所以这里的参数就可以用Comparable[] array来接收
        for (int bound = 0;bound<array.length>bound;cur--){
                if (array[cur-1].compareTo(array[cur])>0){
                    //这里就说明顺序不符合要求,交换两个变量的位置
                    Comparable tmp = array[cur-1];
                    array[cur-1] = array[cur];
                    array[cur] = tmp;
                }
            }
    }}</array.length>

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));
    }}</student></student></student></student>

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视频教程

以上是帶你去搞清楚Java的介面(實例詳解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除