Java抽象類(lèi)和接口使用梳理
抽象類(lèi)
什么是抽象類(lèi)呢?在現(xiàn)實(shí)生活中,我們說(shuō)“人類(lèi)”,我們無(wú)法對(duì)應(yīng)到具體某個(gè)人,同樣的,“動(dòng)物類(lèi)”、“圖形類(lèi)”這些無(wú)法映射到具體的對(duì)象的類(lèi)就是抽象類(lèi)。
抽象類(lèi)是普通類(lèi)的超集,意思就是普通類(lèi)有的抽象類(lèi)也有,只是它比普通類(lèi)多了一些抽象方法而已。這些抽象方法可以有一個(gè),也可以有多個(gè)。
它存在的意義就是讓子類(lèi)來(lái)覆寫(xiě)它的抽象方法,抽象類(lèi)和抽象方法的實(shí)現(xiàn)使用 abstract 關(guān)鍵字實(shí)現(xiàn):
此時(shí)Sharp類(lèi)就是抽象類(lèi),可以看到它的圖標(biāo)和普通類(lèi)都有所不同。
抽象方法
示例:
public abstract void print();
抽象方法所在的類(lèi)一定是抽象類(lèi),抽象方法只有方法聲明,沒(méi)有方法體{}
注意區(qū)分沒(méi)有方法體和方法體為空的情況
public abstract void print();//這是沒(méi)有方法體
public abstract void print(){ } //這是方法體為空,它是普通方法
抽象類(lèi)三大原則
1.抽象類(lèi)無(wú)法直接實(shí)例化對(duì)象。例如上面的 Sharp 類(lèi),Sharp sharp = new Sharp();//這是錯(cuò)誤的
2.子類(lèi)繼承抽象類(lèi),必須覆寫(xiě)抽象類(lèi)中的所有抽象方法(前提是子類(lèi)是普通類(lèi))
Triangle是一個(gè)普通類(lèi),此時(shí)沒(méi)覆寫(xiě)方法就報(bào)錯(cuò)了,這時(shí)我們使用alt + enter 快捷鍵,點(diǎn)擊第一行,再點(diǎn)擊ok,就覆寫(xiě)了父類(lèi)的抽象方法
但是當(dāng)子類(lèi)依然是抽象類(lèi)時(shí),可以選擇不覆寫(xiě)抽象方法
3.final 和 abstract 不能同時(shí)使用、private 和 abstract 不能同時(shí)使用
抽象類(lèi)存在的最大意義就是被繼承,而且它仍然滿足繼承關(guān)系的 is a 原則
abstract class Person
class Chinese extends Person √
class Dog extends Person × //因?yàn)镈og not is a Person
同時(shí)抽象類(lèi)任然受到單繼承的局限,此時(shí)我們就引出了接口來(lái)打破這兩個(gè)局限
接口
Java使用 interface 關(guān)鍵字定義接口,接口中只有全局常量和抽象方法(JDK之前)JDK8擴(kuò)展了default方法。子類(lèi)使用 implements 實(shí)現(xiàn)接口
一般接口的命名使用大寫(xiě) ' I ' 字母開(kāi)頭,子類(lèi)命名以 Impl 結(jié)尾
全局常量:
public static final int NUM = 10;
抽象方法:
public abstract String msg( );
接口使用原則
1.接口中只有 public 權(quán)限,且只有全局常量和抽象方法,所以 abstract、static、final、public這些關(guān)鍵字在接口內(nèi)部都可以省略
public interface IMassage { int NUM = 10;//全局常量 String msg();//抽象方法 }
2.接口沒(méi)有單繼承限制,子類(lèi)可以同時(shí)實(shí)現(xiàn)多個(gè)父接口,多個(gè)接口之間使用逗號(hào)分隔。此時(shí)子類(lèi)必須實(shí)現(xiàn)父類(lèi)接口中所有的抽象方法
public interface IMassage { int NUM = 10;//全局常量 String msg();//抽象方法 } interface INews{ void getNews(); } //子類(lèi) public class MessageImpl implements IMassage,INews{ public String msg() { return null; } public void getNews() { } }
接口可以使用 extends 繼承多個(gè)父接口,下面的示例若類(lèi)要實(shí)現(xiàn)接口 C ,就必須覆寫(xiě) A、B、C中所有的抽象方法
interface A{ void testA(); } interface B{ void testB(); } interface C extends A,B{ }
3.接口依然不能直接實(shí)例化對(duì)象,需要通過(guò)向上轉(zhuǎn)型實(shí)現(xiàn)
public class MessageImpl implements IMassage,INews{ public String msg() { return "hello JAVA"; } public void getNews() { System.out.println("hello n~"); } public static void main(String[] args) { IMassage m = new MessageImpl(); System.out.println(m.msg()); } }
//輸出:hello JAVA
m只能調(diào)用msg方法,不能調(diào)用INews接口類(lèi)定義的方法,需要用父類(lèi)間的相互轉(zhuǎn)換實(shí)現(xiàn)調(diào)用
INews n = (INews)m; n.getNews();
//輸出:hello n~
4.子類(lèi)若既要繼承類(lèi)又要實(shí)現(xiàn)接口,就先繼承,后實(shí)現(xiàn)接口
public class D extends A implements X,Y{ }
JDK兩大內(nèi)置接口
java.lang.Comparable 比較接口
引入示例:使用排序方法比較Student類(lèi)中的對(duì)象
public class Student { private String name; private int age; public Student(String name,int age){ this.name = name; this.age = age; } public String toString(){ return "Student{" + "name=" + name+'\''+ ",age="+ age + '}'; } public static void main(String[] args) { Student s1 = new Student("張三",18); Student s2 = new Student("李四",20); Student s3 = new Student("王五",30); Student[] students = {s3,s1,s2}; Arrays.sort(students); System.out.println(Arrays.toString(students)); } }
運(yùn)行結(jié)果:
程序報(bào)錯(cuò),因?yàn)镾tudent類(lèi)是自定義類(lèi)型,當(dāng)使用Arrays.sort方法對(duì)自定義類(lèi)型進(jìn)行排序時(shí),自定義類(lèi)型需要實(shí)現(xiàn) Comparable 才具有可比較的能力
因此我們將上述示例做如下改動(dòng):
public class Student implements Comparable<Student>{ private String name; private int age; public Student(String name,int age){ this.name = name; this.age = age; } public String toString(){ return "Student{" + "name=" + name+'\''+ ",age="+ age + '}'; } @Override public int compareTo(Student o) { if (this.age == o.age){ return 0; }else if (this.age < o.age){ return -1; } return 1; } public static void main(String[] args) { Student s1 = new Student("張三",18); Student s2 = new Student("李四",20); Student s3 = new Student("王五",30); Student[] students = {s3,s1,s2};//亂序放入數(shù)組 Arrays.sort(students); System.out.println(Arrays.toString(students)); } }
//輸出結(jié)果:
[Student{name=張三',age=18}, Student{name=李四',age=20}, Student{name=王五',age=30}]
可以看到數(shù)組按照年齡的升序排序,達(dá)到了預(yù)期效果。如果想要按照年齡降序排列,只需要修改 compareTo 方法中的一和負(fù)一
實(shí)現(xiàn)Comparable接口,必須覆寫(xiě)它的compareTo方法,該方法返回的數(shù)字:
- =0 表示當(dāng)前對(duì)象等于目標(biāo)對(duì)象 o
- >0 表示當(dāng)前對(duì)象等于目標(biāo)對(duì)象 o
- <0 表示當(dāng)前對(duì)象等于目標(biāo)對(duì)象 o
java.lang.Cloneable 克隆接口
在程序中,克隆是指復(fù)制一個(gè)新的對(duì)象,而這個(gè)新對(duì)象的屬性值是從舊對(duì)象中拷貝過(guò)來(lái)的
Cloneable 接口是一個(gè)標(biāo)記接口,本身沒(méi)有任何抽象方法,當(dāng)一個(gè)類(lèi)實(shí)現(xiàn)了 Cloneable 接口,就表示該類(lèi)具備了克隆的能力,這個(gè)能力是JVM賦予的,要知道在堆上開(kāi)辟空間和對(duì)象創(chuàng)建都由JVM實(shí)現(xiàn)。
我們需要覆寫(xiě) Object 類(lèi)的 clone 方法,點(diǎn)擊向上轉(zhuǎn)型的圖標(biāo)我們就能看到 Object 提供的該方法
可以看到,clone 方法沒(méi)有方法體,用 native 關(guān)鍵字修飾,叫做本地方法,clone方法不是Java語(yǔ)言實(shí)現(xiàn)的,而是C++實(shí)現(xiàn)的,要知道JVM就是由C++實(shí)現(xiàn)的。所以本地方法就表示Java調(diào)用了C++的同名方法,此處只是方法聲明,具體的方法實(shí)現(xiàn)是在C++中。所以雖然它沒(méi)有方法體,但它并不是抽象方法。
這里我們就能知道一個(gè)小知識(shí)點(diǎn):沒(méi)有方法體的方法不一定就是抽象方法
代碼示例:
public class Cat implements Cloneable{ private String name; @Override protected Cat clone() throws CloneNotSupportedException { return (Cat) super.clone(); } public static void main(String[] args) throws CloneNotSupportedException { Cat c1 = new Cat(); c1.name = "喵喵"; Cat c2 = c1.clone(); System.out.println(c1 == c2); System.out.println(c2.name); } }
輸出結(jié)果:
可以看到,輸出 false表示c1和c2不是同一個(gè)地址,也就是說(shuō)在堆上為c2開(kāi)辟了一個(gè)新空間,將c1的值拷貝給了c2
對(duì)象的深淺拷貝
我們先看一個(gè)示例:
class A{ int num; } public class B implements Cloneable{ A a = new A(); @Override protected B clone() throws CloneNotSupportedException { return (B)super.clone(); } public static void main(String[] args) throws CloneNotSupportedException { B b1 = new B(); B b2 = b1.clone(); System.out.println(b1 == b2); System.out.println(b2.a.num); b1.a.num = 100; System.out.println(b2.a.num); } }
輸出:false
0
100
根據(jù)結(jié)果我們看到,將b1.a的值改為100后,b2.a也隨之變化,就是說(shuō)b1.a 和 b2.a 指向了相同的對(duì)象
這就是淺拷貝,拷貝出的 b1 對(duì)象只是拷貝了 b1 自身, 而沒(méi)有拷貝內(nèi)部包含的 a 對(duì)象. 此時(shí) b1 和 b2 中包含的 a 引用仍然是指向同一個(gè)對(duì)象. 此時(shí)修改一邊, 另一邊也會(huì)發(fā)生改變.
我們將代碼做如下修改:
class A implements Cloneable{ int num; @Override protected A clone() throws CloneNotSupportedException { return (A)super.clone(); } } public class B implements Cloneable{ A a = new A(); @Override protected B clone() throws CloneNotSupportedException { B b = new B(); b.a = a.clone(); return b; } public static void main(String[] args) throws CloneNotSupportedException { B b1 = new B(); B b2 = b1.clone(); System.out.println(b1 == b2); System.out.println(b2.a.num); b1.a.num = 100; System.out.println(b2.a.num); } }
結(jié)果:false
0
0
我們讓A類(lèi)也實(shí)現(xiàn)克隆接口,可以看到b1.a的修改沒(méi)有影響b2.a,說(shuō)明此時(shí)b1和b2內(nèi)部包含的a對(duì)象也是不同的,這種拷貝就叫做深拷貝
在Java中,實(shí)現(xiàn)深拷貝的方式有兩種:一種是遞歸實(shí)現(xiàn)Cloneable,上述的例子就是遞歸實(shí)現(xiàn)的;另外一種就是通過(guò)序列化(Serializable 接口)來(lái)進(jìn)行拷貝。這兩種方法現(xiàn)在已經(jīng)不常用了,現(xiàn)在實(shí)現(xiàn)深拷貝是將對(duì)象轉(zhuǎn)為json字符串
抽象類(lèi)和接口的區(qū)別
到此這篇關(guān)于Java抽象類(lèi)和接口使用梳理的文章就介紹到這了,更多相關(guān)Java 抽象類(lèi)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)Linux下雙守護(hù)進(jìn)程
這篇文章主要介紹了Java實(shí)現(xiàn)Linux下雙守護(hù)進(jìn)程的思路、原理以及具體實(shí)現(xiàn)方式,非常的詳細(xì),希望對(duì)大家有所幫助2014-10-10淺談Java中的集合存儲(chǔ)數(shù)據(jù)后,輸出數(shù)據(jù)的有序和無(wú)序問(wèn)題
這篇文章主要介紹了淺談Java中的集合存儲(chǔ)數(shù)據(jù)后,輸出數(shù)據(jù)的有序和無(wú)序問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09Spring定時(shí)任務(wù)@Scheduled注解(cron表達(dá)式fixedRate?fixedDelay)
這篇文章主要為大家介紹了Spring定時(shí)任務(wù)@Scheduled注解(cron表達(dá)式fixedRate?fixedDelay)使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11Java實(shí)現(xiàn)的決策樹(shù)算法完整實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)的決策樹(shù)算法,簡(jiǎn)單描述了決策樹(shù)的概念、原理,并結(jié)合完整實(shí)例形式分析了java實(shí)現(xiàn)決策樹(shù)算法的相關(guān)操作技巧,代碼中備有較為詳盡的注釋便于理解,需要的朋友可以參考下2017-11-11spring?security?自定義Provider?如何實(shí)現(xiàn)多種認(rèn)證
這篇文章主要介紹了spring?security?自定義Provider實(shí)現(xiàn)多種認(rèn)證方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12MybatisPlus中插入數(shù)據(jù)后獲取該對(duì)象主鍵值的實(shí)現(xiàn)
這篇文章主要介紹了MybatisPlus中插入數(shù)據(jù)后獲取該對(duì)象主鍵值,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Spring引入外部屬性文件配置數(shù)據(jù)庫(kù)連接的步驟詳解
這篇文章主要介紹了Spring引入外部屬性文件配置數(shù)據(jù)庫(kù)連接的步驟詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01