java中抽象類(lèi)和接口的相同和不同點(diǎn)介紹
前言
本文簡(jiǎn)單介紹抽象類(lèi),接口以及它們的異同點(diǎn),另附簡(jiǎn)單的代碼舉例。
一、抽象類(lèi)是什么?
在 Java 語(yǔ)言中使用 abstract class 來(lái)定義抽象類(lèi)。抽象類(lèi)是不能被實(shí)例化的,即不能用new關(guān)鍵字來(lái)實(shí)例化對(duì)象。包含抽象方法的一定是抽象類(lèi),但抽象類(lèi)不一定包含抽象方法。如果一個(gè)子類(lèi)實(shí)現(xiàn)了父類(lèi)(抽象類(lèi))的所有抽象方法,那么該子類(lèi)可以不必是抽象類(lèi),否則就是抽象類(lèi)。抽象類(lèi)中的抽象方法的修飾符只能為public或者protected。
為什么要在抽象類(lèi)中定義抽象方法呢?下面我們來(lái)看一個(gè)代碼實(shí)例:
package example;
import java.util.Date;
public abstract class GeometricObject {
private String color="white";
private boolean filled;
private java.util.Date dateCreated;
protected GeometricObject() {
dateCreated=new java.util.Date();
}//構(gòu)建默認(rèn)的幾何對(duì)象
protected GeometricObject(String color,boolean filled) {
dateCreated=new java.util.Date();
this.color=color;
this.filled=filled;
}
public String getColor() {
return color;
}//返回顏色
public void setColor(String color) {
this.color=color;
}//設(shè)置新的顏色
public boolean getFilled() {
return filled;
}
public void setFilled(boolean filled) {
this.filled=filled;
}
public java.util.Date getDateCreated(){
return dateCreated;
}
public abstract double getArea();//抽象方法定義得到面積
public abstract double getPerimeter();//抽象方法得到周長(zhǎng)
}
我們假設(shè)自定義的Circle類(lèi)和Rectangle類(lèi)均為GeometricObject的子類(lèi),且均有求各自面積的getArea()方法和各自周長(zhǎng)的getPerimeter()方法,但這些方法并不能在父類(lèi)中定義,因?yàn)榫唧w的計(jì)算方法要取決于幾何對(duì)象的具體類(lèi)型。所以采取抽象方法進(jìn)行定義,這樣可以保證父類(lèi)中的抽象方法可以在子類(lèi)中被重寫(xiě)。
抽象類(lèi)只有定義沒(méi)有實(shí)現(xiàn)。
下面是抽象類(lèi)值得注意的幾點(diǎn):
1.抽象方法不能包含在非抽象類(lèi)中。如果抽象父類(lèi)的子類(lèi)不能實(shí)現(xiàn)所有的抽象方法,那么子類(lèi)也必須定義為抽象的。也就是說(shuō),在繼承自抽象類(lèi)的非抽象子類(lèi)中,必須實(shí)現(xiàn)所有的抽象方法。還要注意到,抽象方法是靜態(tài)的。
2.抽象類(lèi)不能使用new操作符來(lái)初始化。但仍然可以定義它的構(gòu)造方法,這個(gè)構(gòu)造方法在它的子類(lèi)的構(gòu)造方法中進(jìn)行調(diào)用。
3.包含抽象方法的類(lèi)必須是抽象的。但是是可以定義一個(gè)不包含抽象方法的抽象類(lèi),這個(gè)抽象類(lèi)用于作為定義新子類(lèi)的基類(lèi)。
4.即使子類(lèi)的父類(lèi)是具體的,這個(gè)子類(lèi)也可以是抽象的。
5.不能使用new操作符從一個(gè)抽象類(lèi)創(chuàng)建一個(gè)實(shí)例,但是抽象類(lèi)可以用做一種數(shù)據(jù)類(lèi)型。
例如:GeometricObject[] objects=new GeometricObject[10];是正確的,。
6.子類(lèi)可以重寫(xiě)父類(lèi)的方法并將它定義為抽象的,這雖然不常見(jiàn),但很適用于當(dāng)父類(lèi)的方法實(shí)現(xiàn)在子類(lèi)中變得無(wú)效時(shí)的情況。
二、接口是什么?
接口是一種與類(lèi)很相似的結(jié)構(gòu),用于為對(duì)象定義共同操作,但它的目的是指明相關(guān)或者不相關(guān)類(lèi)的對(duì)象的共同行為。
Java中接口使用interface關(guān)鍵字修飾。接口是抽象方法的集合。如果一個(gè)類(lèi)實(shí)現(xiàn)了某個(gè)接口,那么它就繼承了這個(gè)接口的抽象方法。
類(lèi)和接口之間的關(guān)系稱(chēng)為接口繼承。
下面是簡(jiǎn)單的代碼舉例:
package example;
public class TestEdible {
public static void main(String[] args) {
// TODO Auto-generated method stub
Object[] objects= {new Tiger(),new Chicken(),new Apple()};
//創(chuàng)建由Tiger,Chicken和Apple類(lèi)型的三個(gè)對(duì)象構(gòu)成的數(shù)組
for(int i=0;i<objects.length;i++) {
if(objects[i] instanceof Edible)
System.out.println(((Edible)objects[i]).howToEat());
if(objects[i] instanceof Animal) {
System.out.println(((Animal)objects[i]).sound());
}
}//如果可食用,則調(diào)用howToEat方法,如果是動(dòng)物,則調(diào)用sound方法
}
}
abstract class Animal{
private double weight;
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight=weight;
}
public abstract String sound();//返回動(dòng)物的聲音,是抽象方法,被具體的animal類(lèi)實(shí)現(xiàn)
}
class Chicken extends Animal implements Edible{
@Override
public String howToEat() {
return "Chicken: Fry it";
}//實(shí)現(xiàn)了Edible接口。當(dāng)一個(gè)類(lèi)實(shí)現(xiàn)接口時(shí),該類(lèi)實(shí)現(xiàn)定義在接口中的所有方法。
@Override
public String sound() {
return "Chicken: cock-a-doodle-doo";
}
}
class Tiger extends Animal{
@Override
public String sound() {
return "Tiger:RROOAARR";
}
}
abstract class Fruit implements Edible{
}//Fruit類(lèi)實(shí)現(xiàn)了Edible,但沒(méi)有實(shí)現(xiàn)howToEat方法,所以它必須定義為abstract。
class Apple extends Fruit{
@Override
public String howToEat() {
return "Apple:Make apple cider";
}
}//Fruit的具體子類(lèi)必須實(shí)現(xiàn)howToEat方法,所以Apple類(lèi)實(shí)現(xiàn)了howToEat方法。
需要注意的是,接口中所有的數(shù)據(jù)域都是public static final,而且所有方法都是public abstract,但Java允許忽略這些修飾符。
即下面的兩個(gè)代碼是等價(jià)的:
public interface T {
public static final int K=1;
public abstract void p();
}
public interface T {
int K=1;
void p();
}
盡管public修飾符對(duì)于定義在接口中的方法可以省略,但是在子類(lèi)實(shí)現(xiàn)中方法必須定義為public。
三、抽象類(lèi)和接口的異同點(diǎn)
一個(gè)類(lèi)可以實(shí)現(xiàn)多個(gè)接口,但是只能繼承一個(gè)父類(lèi)。
首先我們先來(lái)列舉個(gè)表格簡(jiǎn)單講述一下抽象類(lèi)和接口之間的異同點(diǎn):
| 比較點(diǎn) | 抽象類(lèi) | 接口 |
| 關(guān)鍵字 | abstract class | interface |
| 字段 | 無(wú)限制 | 變量必須是public static final |
| 方法 | 既可以含普通方法,又可以含抽象方法,無(wú)限制 | 只能含抽象方法,且必須是public的 |
| 繼承/實(shí)現(xiàn) | 只能被類(lèi)或抽象類(lèi)繼承 | 既可以被接口繼承,也能被類(lèi)或抽象類(lèi)實(shí)現(xiàn) |
| 多重繼承 | 不支持 | 可以繼承多個(gè)父接口 |
java只允許為類(lèi)的繼承做單一繼承,但是允許使用接口做多重繼承。例如:
public class NewClass extends BaseClass implements Interface1,...,InterfaceN{
}
利用extends,接口可以繼承其他接口,這樣的接口被稱(chēng)為子接口。例如:下面的代碼中,NewInterface是Interface1,...,InterfaceN的子接口。
public interface NewInterface extends Interface1,...,InterfaceN{
}
一個(gè)實(shí)現(xiàn)NewInterface的類(lèi)必須實(shí)現(xiàn)在NewInterface,Interface1,...,InterfaceN中定義的抽象方法。接口可以繼承其他接口但不能繼承類(lèi)。一個(gè)類(lèi)可以繼承它的父類(lèi)同時(shí)實(shí)現(xiàn)多個(gè)接口。
所有的類(lèi)都有一個(gè)共同的根類(lèi)Object,但是接口并沒(méi)有共同的根。與類(lèi)相似,接口同樣也可以定義一種類(lèi)型。一個(gè)接口類(lèi)型的變量可以引用任何實(shí)現(xiàn)該接口的類(lèi)的實(shí)例。如果一個(gè)類(lèi)實(shí)現(xiàn)了一個(gè)接口,那么這個(gè)接口就類(lèi)似于該類(lèi)的一個(gè)父類(lèi)??梢詫⒔涌诋?dāng)作一種數(shù)據(jù)類(lèi)型使用,將接口類(lèi)型的變量轉(zhuǎn)換為它的子類(lèi),反過(guò)來(lái)同樣可以。
通常情況下,使用接口的頻率更高,因?yàn)榻涌诒阮?lèi)更加靈活,不用使所有東西都屬于同一個(gè)類(lèi)型的類(lèi)。
下面進(jìn)行一個(gè)簡(jiǎn)單的代碼舉例:
abstract class Animal{
public abstract String howToEat();
}//在Animal中定義howToEat方法
//Animal的兩個(gè)子類(lèi)定義如下
class Chicken extends Animal{
@Override
public String howToEat() {
return "Chicken: Fry it";
}
}
class Duck extends Animal{
@Override
public String howToEat() {
return "Roast it";
}
}
public static void main(String[] args) {
Animal animal = new Chicken();
eat(animal);
Animal animal = new Duck();
eat(animal);
}
public static void eat(Animal animal){
System.out.println(animal.howToEat());
}
假設(shè)給定這個(gè)繼承結(jié)構(gòu),java在調(diào)用方法時(shí)可以根據(jù)對(duì)象動(dòng)態(tài)地決定調(diào)用具體的howToEat方法。但有一個(gè)限制條件,即該子類(lèi)必須是另一種動(dòng)物才可以。如果一種動(dòng)物不可食用,那么此刻再繼承Animal類(lèi)就并不合適了。
限制條件這個(gè)問(wèn)題在接口中可以被解決,例如:
public class Demo{
public static void main(String[] args) {
Edible stuff = new Chicken();
eat(stuff);
Edible stuff = new Broccoli();
eat(stuff);
}
public static void eat(Edible stuff){
System.out.println(stuff.howToEat());
}
}
interface Edible{
public String howToEat();
}
class Chicken implements Edible{
@Override
public String howToEat() {
return "Chicken: Fry it";
}
}
class Broccoli implements Edible{
@Override
public String howToEat() {
return "Broccoli: Stir-fty it";
}
}
定義表示可食用對(duì)象的一個(gè)類(lèi),只須讓該類(lèi)實(shí)現(xiàn)Edible接口即可。任何Edible對(duì)象都可以被傳遞以調(diào)用HowToEat方法。
總結(jié)
到此這篇關(guān)于java中抽象類(lèi)和接口的相同和不同點(diǎn)介紹的文章就介紹到這了,更多相關(guān)java中抽象類(lèi)和接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 全角半角字符轉(zhuǎn)換如何實(shí)現(xiàn)
在java中可能會(huì)用到過(guò)全角半角字符轉(zhuǎn)換問(wèn)題,于是網(wǎng)上搜索整理了一下,曬出來(lái)和大家分享,希望可以幫助你們2012-12-12
Spring Boot 整合mybatis 使用多數(shù)據(jù)源的實(shí)現(xiàn)方法
這篇文章主要介紹了Spring Boot 整合mybatis 使用多數(shù)據(jù)源的實(shí)現(xiàn)方法,需要的朋友可以參考下2018-03-03
SpringBoot項(xiàng)目后端開(kāi)發(fā)邏輯全面梳理
這篇文章主要介紹了SpringBoot項(xiàng)目后端開(kāi)發(fā)邏輯全面梳理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
springboot的controller層的常用注解說(shuō)明
這篇文章主要介紹了springboot的controller層的常用注解說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
Spring結(jié)合WebSocket實(shí)現(xiàn)實(shí)時(shí)通信的教程詳解
WebSocket?是基于TCP/IP協(xié)議,獨(dú)立于HTTP協(xié)議的通信協(xié)議,本文將使用Spring結(jié)合WebSocket實(shí)現(xiàn)實(shí)時(shí)通信功能,有需要的小伙伴可以參考一下2024-01-01
Spring Boot異步調(diào)用@Async過(guò)程詳解
這篇文章主要介紹了Spring Boot異步調(diào)用@Async過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
springcloud-feign調(diào)用報(bào)錯(cuò)問(wèn)題
這篇文章主要介紹了springcloud-feign調(diào)用報(bào)錯(cuò)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

