Java編程接口詳細(xì)
一、抽象類和抽象方法
抽象:從具體事物抽出、概括出它們共同的方面、本質(zhì)屬性與關(guān)系等,而將個(gè)別的、非本質(zhì)的方面、屬性與關(guān)系舍棄,這種思維過程,稱為抽象。
這句話概括了抽象的概念,而在Java中,你可以只給出方法的定義不去實(shí)現(xiàn)方法的具體事物,由子類去根據(jù)具體需求來(lái)具體實(shí)現(xiàn)。
抽象類除了包含抽象方法外,還可以包含具體的變量和具體的方法。類即使不包含抽象方法,也可以被聲明為抽象類,防止被實(shí)例化。
抽象類不能被實(shí)例化,也就是不能使用new關(guān)鍵字來(lái)得到一個(gè)抽象類的實(shí)例,抽象方法必須在子類中被實(shí)現(xiàn)。
抽象類總結(jié)規(guī)定:
- 抽象類不能被實(shí)例化,如果被實(shí)例化,就會(huì)報(bào)錯(cuò),編譯無(wú)法通過。只有抽象類的非抽象子類可以創(chuàng)建對(duì)象。
- 抽象類中不一定包含抽象方法,但是有抽象方法的類必定是抽象類。
- 抽象類中的抽象方法只是聲明,不包含方法體,就是不給出方法的具體實(shí)現(xiàn)也就是方法的具體功能。
- 構(gòu)造方法,類方法(用 static 修飾的方法)不能聲明為抽象方法。
- 抽象類的子類必須給出抽象類中的抽象方法的具體實(shí)現(xiàn),除非該子類也是抽象類。
二、接口
interface關(guān)鍵字使得抽象的概念更加向前邁進(jìn)了一步,abstract關(guān)鍵字允許人們?cè)陬愔袆?chuàng)建一個(gè)或多個(gè)沒有任何定義的方法---提供了接口部分。但是沒有提供任何相應(yīng)的具體實(shí)現(xiàn),這些實(shí)現(xiàn)是由此類的繼承者實(shí)現(xiàn)的。
在抽象類中,可以包含一個(gè)或多個(gè)抽象方法;但在接口(interface)中,所有的方法必須都是抽象的,不能有方法體,它比抽象類更加“抽象”。
接口使用 interface 關(guān)鍵字來(lái)聲明,可以看做是一種特殊的抽象類,可以指定一個(gè)類必須做什么,而不是規(guī)定它如何去做。
與抽象類相比,接口有其自身的一些特性:
- 接口中只能定義抽象方法,這些方法默認(rèn)為
public abstract的,因而在聲明方法時(shí)可以省略這些修飾符。試圖在接口中定義實(shí)例變量、非抽象的實(shí)例方法及靜態(tài)方法,都是非法的 - 接口中沒有構(gòu)造方法,不能被實(shí)例化
- 一個(gè)接口不實(shí)現(xiàn)另一個(gè)接口,但可以繼承多個(gè)其他接口。接口的多繼承特點(diǎn)彌補(bǔ)了類的單繼承
接口與抽象類的區(qū)別:
接口作為系統(tǒng)和外界交互的窗口,接口體現(xiàn)的是一種規(guī)范。對(duì)于接口的實(shí)現(xiàn)者而言,接口規(guī)定了實(shí)現(xiàn)者必須向外提供哪些服務(wù)(以方法的形式來(lái)提供);對(duì)于接口的調(diào)用者而言,接口規(guī)定了調(diào)用者可以調(diào)用哪些服務(wù),以及如何調(diào)用這些服務(wù)(就是如何來(lái)調(diào)用方法)。當(dāng)在一個(gè)程序中使用接口時(shí),接口是多個(gè)模塊間的耦合標(biāo)準(zhǔn);當(dāng)在多個(gè)應(yīng)用程序之間使用接口時(shí),接口是多個(gè)程序之間的通信標(biāo)準(zhǔn)。
從某種角度上來(lái)看,接口類似于整個(gè)系統(tǒng)的“總綱”,它制定了系統(tǒng)各模塊之間應(yīng)該遵循的標(biāo)準(zhǔn),因此一個(gè)系統(tǒng)中的接口不應(yīng)該經(jīng)常改變。一旦接口改變,對(duì)整個(gè)系統(tǒng)而言甚至其他系統(tǒng)的影響將是輻射式的,導(dǎo)致系統(tǒng)中的大部分類都需要改寫。所以,在一般的應(yīng)用里,最頂級(jí)的是接口,然后是抽象類實(shí)現(xiàn)接口,最后才到具體類實(shí)現(xiàn)。
抽象類則不一樣,抽象類作為系統(tǒng)中多個(gè)子類的共同父類,它所體現(xiàn)的是模板式設(shè)計(jì)。抽象類作為多個(gè)子類的的抽象父類,可以被當(dāng)成系統(tǒng)實(shí)現(xiàn)過程中的中間產(chǎn)品,這個(gè)產(chǎn)品已經(jīng)實(shí)現(xiàn)了系統(tǒng)的部分功能(那些在抽象類中已經(jīng)提供實(shí)現(xiàn)的方法),但這個(gè)產(chǎn)品依然不能當(dāng)成最終產(chǎn)品,必須有更進(jìn)一步的完善。
除此之外,接口和抽象類在用法上也存在如下區(qū)別:
- 接口里只能包含抽象方法,抽象類則可以包含普通方法。
- 接口里不能定義靜態(tài)方法,抽象類里可以定義靜態(tài)方法。
- 接口里不包含構(gòu)造器,抽象類可以包含構(gòu)造器。抽象類里的構(gòu)造器并不是用于創(chuàng)建對(duì)象,而是讓其子類調(diào)用這些構(gòu)造器來(lái)完成屬于抽象類的初始化操作。
- 接口里不能包含初始化塊,但抽象類可以包含初始化塊。
- 接口里只能定義靜態(tài)常量,抽象類既可以定義普通變量,也可以定義靜態(tài)常量。
- 接口可以可以繼承多個(gè)接口,類只能繼承一個(gè)類。
- 抽象類主要是用來(lái)抽象類別,接口主要是用來(lái)抽象方法功能。當(dāng)關(guān)注事物的本質(zhì)時(shí),使用抽象類,當(dāng)關(guān)注一種操作時(shí),使用接口。
三、Java中的多重繼承
接口不僅僅是一種更加純粹的抽象類,它的目標(biāo)比這更高。因?yàn)榻涌谥懈緵]有任何具體實(shí)現(xiàn),所以沒有任何與接口相關(guān)的存儲(chǔ),因此也就無(wú)法阻止多個(gè)接口的組合。在C++中,組合多個(gè)類的接口的行為叫做多重繼承,但這可能會(huì)帶來(lái)很多副作用,因?yàn)槊總€(gè)類都有一個(gè)具體實(shí)現(xiàn)。在Java中,可以執(zhí)行一樣的行為,但是只有一個(gè)類可以有具體實(shí)現(xiàn),所以通過組合多個(gè)接口,C++的問題不會(huì)在Java中發(fā)生。
表達(dá)這樣一個(gè)意思:“ x 從屬于 a,也從屬于 b,也從屬于 c ”
使用接口的核心原因:
1).為了能夠向上轉(zhuǎn)型為多個(gè)基類型(以及由此帶來(lái)的靈活性);
2).防止客戶端程序員創(chuàng)建該類的對(duì)象,并確保這僅僅是建立一個(gè)接口(這與使用抽象基類原因相同)
這帶來(lái)的一個(gè)問題是,應(yīng)該使用接口還是抽象類?
如果要?jiǎng)?chuàng)建不帶任何方法定義和成員變量的基類,那么就應(yīng)該選擇接口而不是抽象類。事實(shí)上,若知道某事物應(yīng)該成為一個(gè)基類,那么第一選擇應(yīng)該是接口。
四、通過繼承來(lái)擴(kuò)展接口
1、組合接口時(shí)的名字沖突
在實(shí)現(xiàn)多重繼承時(shí),會(huì)碰到一個(gè)小陷阱,在前面的例子中,CanFight和ActionCharacter都有一個(gè)相同的void fight()方法。問題不是它們方法相同,問題是,如果它們的簽名(參數(shù))或返回類型不同,會(huì)怎么樣呢?
//: interfaces/InterfaceCollision.java
package object;
interface I1 { void f(); }
interface I2 { int f(int i); }
interface I3 { int f(); }
class C { public int f() { return 1; } }
class C2 implements I1, I2 {
public void f() {}
public int f(int i) { return 1; } // overloaded
}
class C3 extends C implements I2 {
public int f(int i) { return 1; } // overloaded
}
class C4 extends C implements I3 {
// Identical, no problem:
public int f() { return 1; }
}
// Methods differ only by return type:
//!class C5 extends C implements I1 {} --23
//! interface I4 extends I1, I3 {} ///:~ --24 I1, I3中f()返回值類型不一致
//class C5 extends C implements I1{ //實(shí)現(xiàn)的方法和積累方法命名相同,但方法的返回值不一樣。
// int f(){
// return 0;
// }
//}
//
//interface I4 extends I1 , I3{ //重寫的方法名相同,但是返回值不同。
//
// @Override
// void f();
//}
因?yàn)樗麄兊姆椒枷嗤?,但是返回值不同,并不能?shí)現(xiàn)方法重載。所以不能實(shí)現(xiàn)多重繼承和組合接口。
五、適配接口
接口最吸引人的原因之一就是允許同一個(gè)接口具有多種不同的實(shí)現(xiàn)。
接口最常見的用法就是使用策略設(shè)計(jì)模式。此時(shí)你編寫一個(gè)執(zhí)行某些操作的方法,而該方法將接受一個(gè)你指定的接口。你主要就是聲明:“ 你可以用任何你想要的對(duì)象來(lái)調(diào)用我的方法,只要你的對(duì)象遵循我的接口?!?/p>
比如Java SE5的Scanner類,它的構(gòu)造器接收的是一個(gè)Readable接口。
public Scanner(Readable source) {
this(Objects.requireNonNull(source, "source"), WHITESPACE_PATTERN);
}
// Readable 是一個(gè)字符源。read方法的調(diào)用方能夠通過 CharBuffer 使用 Readable 中的字符。
public interface Readable {
// 將輸入內(nèi)容添加到CharBuffer參數(shù)中。
public int read(java.nio.CharBuffer cb) throws IOException;
}
example1 : 實(shí)現(xiàn)Readable接口。
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Random;
import java.util.Scanner;
public class RandomWords implements Readable {
private int count;
public RandomWords(int count) {
this.count = count;
}
private static Random random = new Random(47);
private static final char[] capitals = "ABCDEFTHIGKLMNOPQRSTUVWXYZ".toCharArray();
private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz".toCharArray();
private static final char[] vowerls = "aeiou".toCharArray();
@Override
public int read(CharBuffer cb) throws IOException {
if (count-- == 0) {
return -1;
}
cb.append(capitals[random.nextInt(capitals.length)]);
for (int i = 0; i < 4; i++) {
cb.append(vowerls[random.nextInt(vowerls.length)]);
cb.append(lowers[random.nextInt(lowers.length)]);
}
cb.append(" ");
return 10;
}
public static void main(String[] args) {
@SuppressWarnings("resource")
Scanner scanner = new Scanner(new RandomWords(10));
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
}
}
/*output:
Yazeruyac
Fowenucor
Toeazimom
Raeuuacio
Nuoadesiw
Hageaikux
Ruqicibui
Numasetih
Kuuuuozog
Waqizeyoy
*/
example2 : 未實(shí)現(xiàn)Readable的類,就可以使用適配器+代理的方式
class RandomDoubles{
private static Random rand =new Random(47);
public double next(){
return rand.nextDouble();
}
}
// ---------------------------------------------------
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Random;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner s=new Scanner(new AdaptedRandomDoubles(7));
while(s.hasNext()){
System.out.println(s.next());
}
}
}
class AdaptedRandomDoubles extends RandomDoubles implements Readable {
private int count;
public AdaptedRandomDoubles(int count){
this.count=count;
}
public int read(CharBuffer cb) throws IOException {
if(count--==0){
return -1;
}
String result=Double.toString(this.next());
cb.append(result);
return result.length();
}
}
六、接口中的域
實(shí)例變量都是static final
七、嵌套接口
在類中嵌套接口的語(yǔ)法是相當(dāng)顯而易見的,就像非嵌套接口一樣,可以擁有public和“包訪問”兩種可視性。
1.類中的接口
{
void f();
}
class A {
interface B {
void f();
}
public class BImp implements B {
public void f() {
}
}
private class BImp2 implements B {
public void f() {
}
}
public interface C {
void f();
}
class CImp implements C {
public void f() {
}
}
private class CImp2 implements C {
public void f() {
}
}
private interface D
private class DImp implements D {
public void f() {
}
}
public class DImpl2 implements D {
public void f() {
}
}
public D getD() {
return new DImpl2();
}
private D dRef;
public void receive(D d) {
dRef = d;
dRef.f();
}
}
interface E {
interface G {
void f();
}
//Redundant "public"
public interface H {
void f();
}
void g();
//cannot be private within an interface
}
public class NestingInterface {
public class BImpl implements A.B {
public void f() {
}
}
class CImpl implements A.C {
public void f() {
}
}
// cannot implement a private interface
// class DImpl implements A.D {
// public void f() {
// }
// }
class EImpl implements E {
public void g() {
}
}
class EImpl2 implements E.G {
public void f() {
}
class EG implements E.G {
public void f() {
}
}
}
public static void main(String[] args) {
A a = new A();
A a2 = new A();
//Can't access A.D: 不能訪問私有接口A.D
//! A.D ad = a.getD();
//Doesn't return anything but A.D: 除了私有接口A.D,不能返回任何東西
//! A.DImp2 di2 = a.getD(); //返回回來(lái)的私有接口A.D, 不能向下轉(zhuǎn)型為A.DImp2
//Cannot access a member of the interface: 不能訪問私有接口A.D中的成員
//! a.getD().f();
//Only another A can do anything with getD(): 只有另一個(gè)A才能使用getD()做任何事
a2.receive(a.getD());
}
}
A.DImp2只能被其自身所使用。你無(wú)法說它實(shí)現(xiàn)了一個(gè)private接口D,因此,實(shí)現(xiàn)一個(gè)private接口只是一種方式,它可以強(qiáng)制該接口中的方法定義不要添加任何類型信息(也就是說,不允許向上轉(zhuǎn)型),即A.DImp2不能轉(zhuǎn)型為private接口D;- 接口也可以被實(shí)現(xiàn)為
private的,就像在A.D中看到的那樣;private接口不能在定義它的類之外被實(shí)現(xiàn) - 將返回值交給有權(quán)使用它的對(duì)象。在本例中,是另一個(gè)A通過
receiveD()方法來(lái)實(shí)現(xiàn)的; - 嵌套在另一個(gè)接口中的接口自動(dòng)是
public的,而不能聲明為private的;
2.接口中的接口
interface E{
// 只能是默認(rèn)或者public
interface G {
//默認(rèn)為public
void f();
}
// Cannot be private within an interface:
//! private interface I {}
}
class t2 implements E.G{
public void f() {
}
}
八、接口與工廠
接口時(shí)實(shí)現(xiàn)多重繼承的途徑,而生成遵循某個(gè)接口的對(duì)象的典型方式就是工廠方法設(shè)計(jì)模式
通過工廠方法,接口和實(shí)現(xiàn)完全分離,可以非常方便的更改實(shí)現(xiàn)。
interface Service // Service接口,可以有多種實(shí)現(xiàn)
{
void method1();
void method2();
}
interface ServiceFactory // 工廠接口,可以由多種實(shí)現(xiàn)
{
Service getService();
}
class Implementation1 implements Service { //Service接口的實(shí)現(xiàn)1
public Implementation1() { }
public void method1() {
System.out.println("Implementation1 method1");
}
public void method2() {
System.out.println("Implementation1 method2");
}
}
class Implementation1Factory implements ServiceFactory{ //生成對(duì)象1的工廠1
public Service getService() {
return new Implementation1();
}
}
class Implementation2 implements Service { // Service接口的實(shí)現(xiàn)2
public Implementation2() { }
public void method1() {
System.out.println("Implementation2 method1");
}
public void method2() {
System.out.println("Implementation2 method2");
}
}
class Implementation2Factory implements ServiceFactory{//生成對(duì)象2的工廠2
public Service getService() {
return new Implementation1();
}
}
public class Factories { //使用service的模塊
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService(); //向上造型,工廠將生成某類實(shí)現(xiàn)接口的對(duì)象
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(new Implementation1Factory());
//serviceConsumer(new Implementation2Factory());很方便就可以更改實(shí)現(xiàn)
}
}
/*output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*/
匿名內(nèi)部類改進(jìn)
interface Service {
void method1();
void method2();
}
interface ServiceFactory {
Service getService();
}
class Implementation1 implements Service {
private Implementation1() {
}
public void method1() {
System.out.println("Implementation1 method1");
}
public void method2() {
System.out.println("Implementation1 method2");
}
public static ServiceFactory factory = new ServiceFactory() {
public Service getService() {
return new Implementation1();
}
};
}
class Implementation2 implements Service {
private Implementation2() {
}
public void method1() {
System.out.println("Implementation1 method1");
}
public void method2() {
System.out.println("Implementation1 method2");
}
public static ServiceFactory factory = new ServiceFactory() {
public Service getService() {
return new Implementation2();
}
};
}
public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(Implementation1.factory);
serviceConsumer(Implementation2.factory);
}
}
總結(jié):
優(yōu)先選擇類而不是接口。從類開始,如果接口的必需性變得非常明確,那么就進(jìn)行重構(gòu)。
到此這篇關(guān)于Java編程接口詳細(xì)的文章就介紹到這了,更多相關(guān)Java編程 接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Netty實(shí)現(xiàn)自定義協(xié)議編解碼器
這篇文章主要為大家介紹了Netty實(shí)現(xiàn)自定義協(xié)議編解碼器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
使用SpringSecurity 進(jìn)行自定義Token校驗(yàn)
這篇文章主要介紹了使用SpringSecurity 進(jìn)行自定義Token校驗(yàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Java的Jackson庫(kù)中復(fù)雜對(duì)象集合的幾種簡(jiǎn)單轉(zhuǎn)換
本文主要介紹了Java的Jackson庫(kù)中復(fù)雜對(duì)象集合的幾種簡(jiǎn)單轉(zhuǎn)換。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02
JAVA微信掃碼支付模式二線上支付功能實(shí)現(xiàn)以及回調(diào)
本篇文章主要介紹了JAVA微信掃碼支付模式二線上支付功能實(shí)現(xiàn)以及回調(diào),這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2016-11-11
Java各種排序算法匯總(冒泡,選擇,歸并,希爾及堆排序等)
這篇文章主要介紹了Java各種排序算法,以大量實(shí)例形式匯總分析了Java常用的各種排序算法,包括冒泡排序、快速排序、堆排序、插入排序、希爾排序、選擇排序、歸并排序等,需要的朋友可以參考下2015-11-11
Spring Boot整合郵件發(fā)送與注意事項(xiàng)
這篇文章主要給大家介紹了關(guān)于Spring Boot整合郵件發(fā)送與注意事項(xiàng)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
Java中DataInputStream和DataOutputStream的使用方法
這篇文章主要介紹了Java中DataInputStream和DataOutputStream的使用方法,通過創(chuàng)建對(duì)象展開具體的內(nèi)容介紹,需要的小伙伴可以參考一下2022-05-05
java 實(shí)現(xiàn)計(jì)數(shù)排序和桶排序?qū)嵗a
這篇文章主要介紹了java 實(shí)現(xiàn)計(jì)數(shù)排序和桶排序?qū)嵗a的相關(guān)資料,需要的朋友可以參考下2017-02-02

