硅基計劃2.0 學習總結 陸 抽象類與接口

圖 (878)


文章目錄

    • 一、抽象類
      • 1. 定義
      • 2. 示例代碼
      • 3. 特性
    • 二、接口初識
      • 1. 定義
      • 2. 命名與語法
      • 3. 示例代碼
      • 4. 常見特性
      • 5. 多接口實現
      • 6. 接口的繼承
    • 三、Object類初識
      • 1. equals方法
      • 2. hascode方法


一、抽象類

1. 定義

  • 請你假設這樣一個場景,我們定義一個人的類,這個類中我們定義一個成員方法void sleep(),然后我們派生出兩個子類Student()類和Teacher()類,然后再在子類中寫出各自的sleep()方法,也能實現多態
  • 但是,今天我們要講的是抽象類和抽象方法,因此在語法上,抽象類指的是被abstract關鍵字修飾的類,進而抽象方法就指的是被abstract關鍵字修飾的方法
  • 在抽象類和抽象方法中,沒有具體的實現,在人的類中,我們描述這個人的類很虛幻,很寬泛,因而我們的abstract void sleep();方法中并無具體的內容,就單純是個方法的展示,說明人具有睡覺的習性,而未定義具體怎么睡的
  • 那你可能就會問了,那我在此處不定義,難道在子類中再重新定義嗎(即重寫),是的,我們創建抽象方法的意義就是為了在子類中重寫
  • 那聰明的你也會問了,我都有繼承了我為什么還要定義抽象類呢,其實這樣主要是為了在子類繼承父類的時候,檢查你是否有重寫父類中的方法,便于校驗,以免造成遺漏,而且這樣規范性也更高了,進一步提高了代碼的穩定性

2. 示例代碼

//抽象類Person
public abstract class Person {String name;public Person(String name) {this.name = name;}abstract void sleep();
}
//Student類
public class Student extends Person{String classes;String stage;public Student(String name, String classes,String stage) {super(name);this.classes = classes;this.stage = stage;}@Overridevoid sleep() {System.out.println(name+classes+"學生在睡覺");}
}
//Teacher類
public class Teacher extends Person{String department;String subject;public Teacher(String name,String department, String subject) {super(name);this.department = department;this.subject = subject;}@Overridevoid sleep() {System.out.println(name+department+"老師在睡覺");}
}
//Test測試類
public class Test {public static void func(Person person){person.sleep();}public static void main(String[] args) {Person per1 = new Student("張三","計科二班","大一");Person per2 = new Teacher("王五","教務處","計算機科學與技術");func(per1);func(per2);}
}

3. 特性

  1. 可以和普通的類一樣擁有成員變量和成員方法,不一定要有抽象方法,但是反之一個類中有抽象方法那么這個類必須為抽象類
    image-20250606153842693
  2. 抽象類不可以被實例化,但是它可以被繼承,而且它的主要目的就是繼承
  3. 當一個普通的類繼承抽象類之后,這個普通類需要去重寫抽象類中所有的抽象方法,不過若你不想自己手動寫,直接Alt+回車查看,然后點擊重寫選項即可
    image-20250606154147307
  4. 可以有構造方法,跟普通方法一樣,通過子類去初始化抽象類(父類)中的成員變量,因此抽象類中成員變量和構造方法功能與普通的父類近乎沒有差別,剛剛的示例代碼中就有
  5. 抽象類也可以發生動態綁定、向上轉型、多態等,比如示例代碼的Test測試類中的func方法
  6. 因抽象類之間的可繼承性,若抽象類B繼承了抽象類A,那么在抽象類B中可以不用重寫抽象類A中的抽象方法,但是當一個普通類C去繼承抽象類B的時候,不光要重寫抽象類B中的抽象方法,還要重寫抽象類A中的抽象方法,因為普通類C間接繼承了抽象類A
  7. 抽象方法不能被private,final,static等關鍵字修飾,因為這樣會導致不可被繼承,進而就不能在子類中重寫抽象方法了

Tips:如果你細心,你會發現抽象類的圖標和普通類中的圖標不一樣


二、接口初識

1. 定義

  • 其實我也不好描述,你就把它理解為多個類的公共準則,即讓類具有某些屬性,一種引用的數據類型的規范,因此它可被多個類引用,就決定類其高度公有性與多個類之間共性
  1. 跟抽象類一樣,接口類指的是被interface關鍵字修飾的
  2. 成員變量默認都是被public static final修飾,當你寫入權限限定符時會顯示是冗余的,并且在定義的時候還需要初始化,這一點跟類不一樣
    image-20250606164402153
  3. 成員方法默認都是被public abstract,即都是抽象方法,因此接口類方法中不能有方法的具體定義,但是,凡事都有特殊情況,如果被static關鍵字或defeault關鍵字修飾后就可以
    image-20250606165030556
  4. 接口跟抽象類一樣,它不可以實例化
    image-20250606165127124

2. 命名與語法

  • 接口內部有一套新的語法體系,命名一般是大寫字母I+形容詞
  • 內部方法與成員變量不能添加任何修飾符比如public、private等

3. 示例代碼

  • 我們這里就以我們之前寫過的學生類的代碼做個演示,整體思路就是借助implement關鍵字去實現類,并且去重寫接口中所有抽象方法
//首先我們定義一個接口IPerosn
public interface IPerson {void Attendclass();//定義上課void Classisover();//定義下課
}
//其次我們再定義兩個子類Teacher和Student
//重寫接口中的抽象方法,使其上課和下課的方式各不同
public class Student implements IPerson{@Overridepublic void Attendclass() {System.out.println("學生在上課");}@Overridepublic void Classisover() {System.out.println("學生下課離開了");}void note(){//定義學生特有方法System.out.println("記筆記");}
}public class Teacher implements IPerson{@Overridepublic void Attendclass() {System.out.println("老師在上課");}@Overridepublic void Classisover() {System.out.println("老師下課離開了");}void blackboard(){//定義老師特有方法System.out.println("粉筆寫字");}
}
//再定義一個教室類Classroom用于把兩個子類集中起來,一邊協作
public class Classroom {//這個大類把兩個子類集中起來void openDoor(){System.out.println("打開教室門");}void closeDoor(){System.out.println("關機教室門");}void learn(IPerson person){//定義協作方法person.Attendclass();if(person instanceof Student){Student stu = (Student) person;stu.note();}else if(person instanceof Teacher){Teacher ter = (Teacher) person;ter.blackboard();}person.Classisover();}
}
//我們再定義一個測試類,分別實例化教室類Classroom、學生類Student和老師類Teacher
public class Test {public static void main(String[] args) {Classroom classroom = new Classroom();Student stu = new Student();Teacher ter = new Teacher();classroom.openDoor();classroom.learn(stu);//調用協作方法classroom.learn(ter);//調用協作方法classroom.closeDoor();}
}

我來解釋下這些代碼,以便讓你更加明白

  1. 首先我們在接口中定義兩個需要在實現類中重寫的兩個抽象方法,僅僅聲明,并無具體實現方法
  2. 我們再定義兩個類StudentTeacher,來實現接口,再重寫接口中的抽象方法,做后我們分別定義了當前類中特有方法note()blackboard()
  3. 我們再定義一個教室類Classroom去整合兩個類StudentTeacher,在教室類Classroom中先定義兩個特有方法openDoor()closeDoor(),再定義一個方法實現當前類Classroom和其他兩個類StudentTeacher的協作方法learn,雖然接口不可實例化,但是它也可以引用傳參,因此參數列表我們寫IPerson person
  4. 此時你想,如果我用向上轉型,是不是只能調用被重寫的方法,不能調用類中特有的方法(因為你形參是IPerson person,接口引用去引用類對象),那怎么辦呢,還記得向下轉型嗎,是的,我們就用向下轉型去訪問,但是不是說向下轉型有風險嗎,你可以看到在轉型前我們先是會檢查這個類實例化的是誰,即這個接口類引用到底引用的是哪個類,是Student還是Teacher呢,檢查完之后我們就可以用向下轉型去訪問類中特有的方法了
  5. 最后我們再定義個測試類Test,去new了三個對象,再把Student類和Teacher類的對象傳給Classroom類中的協作方法,這樣我們就完成了接口的實現,是不是很神奇呢,別介,后面還有終極大招等著
    image-20250606173618736

4. 常見特性

  1. 在重寫接口方法時候,不可使用默認的權限,要滿足重寫權限>=原抽象方法,但是呢又因為接口中方法默認是public修飾的,因此你重寫后只能用public修飾了,這也能理解,接口畢竟就是為了對外嘛
  2. 之前也說過,在接口中也可以定義變量,一般被隱式指定為public static final
  3. 接口中不可以有代碼塊類型,譬如靜態代碼塊、構造代碼塊、普通代碼塊等
  4. 接口中不可以有構造方法,畢竟你接口中的變量都要定義的時候就初始化,還要構造方法干嘛
  5. 如果你的類沒有重寫接口中的所有抽象方法,那你當前的類就要設置為抽象類
  6. 即使接口并不是class類,但是編譯后還是能產生class文件
  7. 在JDK8之后,可以在接口中定義被default修飾的方法了

5. 多接口實現

  • 原理:接口間也滿足繼承的關系
  • 痛點分析:繼承有個什么痛點,就拿我們之前寫過的Person類來舉例,我們把Person定義為抽象類,我們寫了個抽象方法睡覺,也寫了成員變量name,好,我們讓兩個類StudentTeacher類繼承了這個抽象類,重寫了sleep()抽象方法,之后再在測試類中去調用即可
  • 但是你想,我們可以把Teacher類和Student類中的特有方法寫在抽象類中嗎,不可以,學生上課記筆記老師上課一般會記筆記嗎,不會,一般是教書,那你如果寫進抽象類中,就會導致在Student類和Tracher類中去重寫這兩個方法,雖然語法上可以,但是邏輯上不可
  • 誒,你是不是想到了把各個特有的方法定義成一個類,再讓Teacher類和Student類去繼承唄,很遺憾,Java不支持多繼承,怎么辦呢!!!

解決方法:創建多個接口去實現

  • 我們把特有方法定義在接口中,再讓Teacher類和Student類去實現
  • 因此我們可以把記筆記這個特有方法“封裝”在一個個的接口,如果想實現多個接口,那么各個接口之間用逗號隔開即可,此時我們實現特定接口,我們再重寫接口中的方法即可
  • 同時再在測試類中定義兩個特有方法的接口public static void noting(INoteable noteable)public static void blackborading(IBlackboardable blackboardable),此時我們再把對應的對象傳給特定的方法,此時你會驚奇的發現,我們無需關心對象到底是什么類型,我們只關心他能做什么,能干什么就好,只要他具備了這些特定方法的功能,我們都可以去調用,不論你是什么類型,比如不管你是學生還是老師,只要你會記筆記,你直接調用特定方法即可,這樣就相當于給外界創建了個接口,只要你能連接上,甭管你是什么設備,都可以調用這個接口而無需知道其具體實現內容
  • 以下是示例代碼,我們新定義了兩個具有特有抽象類方法的接口INoteableIBlackboardable,在Test測試類中,我們定義了兩個配合接口的特有方法notingblackborading,我們直接傳具有各個特有方法的能力的對象就好noting(stu);
    blackborading(ter);
//其他代碼都沒變,這里只展示變了的代碼
//Noteable接口
public interface INoteable {void note();
}
//IBlackboardable接口
public interface IBlackboardable {void blackboard();
}
//Test測試類中
public class Test {public static void noting(INoteable noteable){noteable.note();}public static void blackborading(IBlackboardable blackboardable){blackboardable.blackboard();}public static void main(String[] args) {//究極大招Student stu = new Student();Teacher ter = new Teacher();noting(stu);blackborading(ter);}
}

image-20250606184510860

6. 接口的繼承

之前我們就講過接口是可以繼承的,具體是怎么樣的呢?說白了就是接口的擴展

  1. 比如有一名優秀的學生,他既會講課又會記筆記,那我們是不是可以再定義一個接口讓其具有這兩個接口的功能,因此我們用手extends關鍵字,在剛剛代碼中我們要寫兩個接口的實現,此時我們只需要寫一個接口的實現就好了,之后就是跟之前實現接口一樣
  2. 此時你會發現因為這個接口兼并了其他接口,因此在子類中實現的時候需要重寫所有被繼承接口的抽象方法,但是若這個兼并的接口里面還是有其他抽象方法,你還是要在接口實現的類中去重寫這個兼并接口中的抽象方法
  3. 代碼展示
//定義了IExcellent接口,暫時不寫IExcellen類中特有抽象方法
public interface IExcellent extends INoteable,IBlackboardable{
}
//定義了ExcleentStudent方法,可以發現重寫了繼承來的所有抽象方法
public class ExcleentStudent implements IExcellent {@Overridepublic void blackboard() {System.out.println("優秀學生在講課");}@Overridepublic void note() {System.out.println("優秀學生在記筆記");}
}
//我們再在Test測試類中定義一個特殊方法來調用這個接口
public static void excleenting(IExcellent iexcellent){iexcellent.blackboard();iexcellent.note();}
//再在main方法中創建對象,再傳對象,大功告成
public static void main(String[] args) {//究極大招ExcleentStudent stus = new ExcleentStudent();excleenting(stus);}

圖 (289)


三、Object類初識

  • 我們之前說過,object類是所有類的父類,因此類中有些方法默認繼承了Object中的方法

  • 我們可以通過雙擊shift鍵輸入Object來查看,點擊左上角結構(快捷鍵Alt+7)查看我們Object類中所有方法
    image-20250606212654423

  • 那既然它是所有類的父類,那么說明其是萬能的類型,比如之前的代碼Student student = new Student();我們就可以改寫為Object student = new Student(......);,甚至是int a = 10;改寫成object a = 10,既然都這樣了為什么不寫成object類型呢?

  • 因為其太寬泛了,不好易于區分不同類型數據

1. equals方法

  • 我們給個示例代碼,拿我們剛剛演示的代碼為例
public static void main(String[] args) {Person per1 = new Student("張三","計科二班","大一");Person per3 = new Student("張三","計科二班","大一");System.out.println(per1==per3);//結果為false
}

為什么結果是false,因為你不同對象即使內容一樣,地址卻是不一樣的,比較的主要是地址,你想我用equals不就行了嗎,System.out.println(per1.equals(per3));結果還是false,為什么,你轉到源碼去看看
image-20250606213755336

你發現返回的就過是當前對象,這不還是跟System.out.println(per1==per3);沒有半毛錢區別嗎,所以我們需要對equals方法進行重寫,我們以后也會經常碰到需要重寫的方法

  • 我們可以右鍵讓編譯器重寫
    image-20250606214358351

  • 也可我們自己重寫,這邊演示我們自己重寫,代碼有注釋,應該可以看懂

@Overridepublic boolean equals(Object obj){if(obj == null){return false;//如果你傳的是空對象直接返回}if(this == obj){//如果你傳的另外個對象是你本身的話return true;}if(!(obj instanceof Person)){//實例化的不是同個類return false;}//此時的情況就是同一個類的情況了,只需要比較內容是否一致就好Person temp = (Person) obj;//為什么此時向下轉型安全,因為之前已經檢查過了if(temp.name.equals(this.name)){//假設名字一樣就整體相等return true;}return false;}

當然也可以簡化下代碼

@Overridepublic boolean equals(Object obj){if(obj == null){return false;//如果你傳的是空對象直接返回}if(this == obj){//如果你傳的另外個對象是你本身的話return true;}if(!(obj instanceof Person)){//實例化的不是同個類return false;}//此時的情況就是同一個類的情況了,只需要比較內容是否一致就好Person temp = (Person) obj;//為什么此時向下轉型安全,因為之前已經檢查過了return temp.name.equals(this.name);//簡化了這里}

2. hascode方法

用于在散列表中獲取位置,底層是用C/C++寫的,看不到具體實現方法,但是如果你直接用object類的會有問題,通過per1.hashcode()per3.hashcode()不一樣,因此我們需要對hashcode進行重寫操作,我們直接右鍵生成,此時返回的是散列碼進行比較了,發現其散列碼就一樣了
image-20250606220200749
image-20250606220239340

  • 你是否會好奇為啥我通過對象直接使用.去訪問equals和hashcode方法呢,還記不記得之前說object類是所有類的父類,因此我們默認繼承了object類這些方法,難道我們不能訪問嗎,顯然可以訪問,畢竟每個類對位object的子類

這里補充一點:我們傳對象時候可以直接傳new的對象或者是定義一個傳對象的數組去傳

func(new Dog());
func(new horse());
Person [] person = {new Student(),new Teacher()};

文章難免會有錯誤,歡迎指正

Git碼云倉庫鏈接

END

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/83840.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/83840.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/83840.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux命令基礎(2)

su和exit命令 可以通過su命令切換到root賬戶 語法:su [-] 用戶名 -符號是可選的,表示是否在切換用戶后加載環境變量,建議帶上 參數:用戶名,表示要切換的用戶,用戶名可以省略,省略表示切換到ro…

C++算法訓練營 Day10 棧與隊列(1)

1.用棧實現隊列 LeetCode:232.用棧實現隊列 請你僅使用兩個棧實現先入先出隊列。隊列應當支持一般隊列支持的所有操作(push、pop、peek、empty): 實現 MyQueue 類: void push(int x)將元素x推到隊列的末尾 int pop(…

設計模式域——軟件設計模式全集

摘要 軟件設計模式是軟件工程領域中經過驗證的、可復用的解決方案,旨在解決常見的軟件設計問題。它們是軟件開發經驗的總結,能夠幫助開發人員在設計階段快速找到合適的解決方案,提高代碼的可維護性、可擴展性和可復用性。設計模式主要分為三…

【QT】自定義QWidget標題欄,可拖拽(拖拽時窗體變為normal大小),可最小/大化、關閉(圖文詳情)

目錄 0.背景 1.詳細實現 思路簡介 .h文件 .cpp文件 0.背景 Qt Linux;項目遇到問題,解決后特此記錄 項目需要,個性化的標題欄(是個widget),在傳統的三個按鈕(最大化、最小化、關閉&#xf…

如何用 pnpm patch 給 element-plus 打補丁修復線上 bug(以 2.4.4 修復 PR#15197 為例)

背景 在實際項目開發中,依賴的三方庫(如 element-plus)難免會遇到 bug。有時候官方雖然已經修復,但新版本升級成本高,或者有兼容性風險。這時,給依賴打補丁是最優雅的解決方案之一。 本文以 element-plus…

Spring AI 入門:Java 開發者的生成式 AI 實踐之路

一、Spring AI 簡介 在人工智能技術快速迭代的今天,Spring AI 作為 Spring 生態系統的新生力量,正在成為 Java 開發者擁抱生成式 AI 的最佳選擇。該框架通過模塊化設計實現了與主流 AI 服務(如 OpenAI、Anthropic)的無縫對接&…

優化電腦的磁盤和驅動器提高電腦性能和延長硬盤壽命?

磁盤優化 磁盤清理: 使用系統自帶的磁盤清理工具(如Windows的“磁盤清理”)刪除不必要的文件。清空回收站。刪除臨時文件和緩存。 磁盤碎片整理(針對機械硬盤): 定期進行磁盤碎片整理,以提高文…

EDA斷供危機下的冷思考:中國芯片設計軟件的破局之道優雅草卓伊凡

EDA斷供危機下的冷思考:中國芯片設計軟件的破局之道優雅草卓伊凡 一、EDA是什么?芯片行業的”隱形基石” 1.1 EDA技術解析 EDA(Electronic Design Automation,電子設計自動化)是用于設計和驗證集成電路的軟件工具鏈…

Jpackage

簡介 jpackage - 用于打包自包含 Java 應用程序的工具,是 JDK 14 引入的一個工具。 該工具將 Java 應用程序和 Java 運行時映像作為輸入,并生成包含所有必要依賴項的 Java 應用程序映像。它將能夠生成特定于平臺的格式的本機包,例如包括打包 …

CRM管理軟件的數據可視化功能使用技巧:讓數據驅動決策

在當今數據驅動的商業環境中,CRM管理系統的數據可視化功能已成為企業優化客戶管理、提升銷售效率的核心工具。據企銷客研究顯示,具備優秀可視化能力的CRM系統,用戶決策效率可提升47%。本文將深入解析如何通過數據可視化功能最大化CRM管理軟件…

智慧充電:新能源汽車智慧充電樁的發展前景受哪些因素影響?

全球能源結構轉型與碳中和目標的推進,新能源汽車產業迎來爆發式增長,而智慧充電樁作為其核心基礎設施,發展前景備受關注。智慧充電不僅關乎用戶充電體驗的優化,更是電網平衡、能源效率提升的關鍵環節。 然而,其發展并…

ABAP設計模式之---“簡單設計原則(Simple Design)”

“Simple Design”(簡單設計)是軟件開發中的一個重要理念,倡導以最簡單的方式實現軟件功能,以確保代碼清晰易懂、易維護,并在項目需求變化時能夠快速適應。 其核心目標是避免復雜和過度設計,遵循“讓事情保…

多模態大語言模型arxiv論文略讀(105)

UnifiedMLLM: Enabling Unified Representation for Multi-modal Multi-tasks With Large Language Model ?? 論文標題:UnifiedMLLM: Enabling Unified Representation for Multi-modal Multi-tasks With Large Language Model ?? 論文作者:Zhaowei…

SQLServer中的存儲過程與事務

一、存儲過程的概念 1. 定義 存儲過程(Stored Procedure)是一組預編譯的 SQL 語句的集合,它們被存儲在數據庫中,可以通過指定存儲過程的名稱并執行來調用它們。存儲過程可以接受輸入參數、輸出參數,并且可以返回執行…

使用UDP連接ssh

使用UDP連接ssh mosh簡介兩端安裝moshWindows安裝mosh 放行端口使用mosh登錄Linuxdebug mosh簡介 Mosh最大的特點是基于UDP方式傳輸,支持在服務端創建一個臨時的Key供客戶端一次性連接,退出后失效;也支持通過SSH的配置進行認證,但…

軟件功能模塊歸屬論證方法

文章目錄 **一、核心設計原則****二、論證方法****三、常見決策模式****四、驗證方法****五、反模式警示****總結** 在討論軟件功能點應該歸屬哪些模塊時,并沒有放之四海而皆準的固定方法,但可以通過系統化的論證和設計原則來做出合理決策。以下是常見的…

ServBay 1.13.0 更新,新增第三方反向代理/內網穿透

ServBay 作為一款簡化本地開發環境搭建與管理的強大工具,致力于打造一個開箱即用、穩定可靠的本地開發平臺,讓用戶專注于代碼編寫,提升開發效率。 ServBay 1.13.0 正式發布!本次更新聚焦于提升本地開發項目的外部可訪問性、增強國…

如何利用樂維網管進行IP管理

IP管理是網絡管理中的關鍵環節,對于保障網絡的正常運行、提升資源利用效率以及保障網絡安全等方面都具有不可忽視的重要性。樂維網管在IP管理方面具有多種實用功能,以下從IP規劃與分配、IP狀態監測、IP沖突處理、IP審計與報表生成四個方面,介…

Go語言學習-->go的跨平臺編譯

Go語言學習–>go的跨平臺編譯 默認我們go build的可執行文件都是當前操作系統可執行的文件,Go語言支持跨平臺編譯——在當前平臺下編譯其他平臺的可執行文件。 eg:在windows界面的代碼,編譯完成后在linux上面運行 實現方式&#…

SpringBoot自動配置原理深度解析

一、引言 SpringBoot的"約定優于配置"理念極大地簡化了Spring應用的開發流程,而其核心魔法就是自動配置(Auto-Configuration)。本文將深入剖析自動配置的實現原理,幫助開發者更好地理解和定制SpringBoot應用。 二、自動配置核心機制 1. Ena…