摘要:恰當(dāng)?shù)脑瓌t是優(yōu)先選擇類而不是接口。接口是一種重要的工具,但是它們?nèi)菀妆粸E用。
接口和內(nèi)部類為我們提供了一種將接口與實(shí)現(xiàn)分離的更加結(jié)構(gòu)化的方法
抽象類和抽象方法抽象類可以理解為是一種不夠純粹的接口,它是普通類與接口之間的一種中庸之道。
再論初始化首先看看下面的實(shí)例代碼:
class Glyph { void draw() { print("Glyph.draw()"); } Glyph() { print("Glyph() before draw()"); draw(); print("Glyph() after draw()"); } } class RoundGlyph extends Glyph { private int radius = 1; RoundGlyph(int r) { radius = r; print("RoundGlyph.RoundGlyph(), radius = " + radius); } void draw() { print("RoundGlyph.draw(), radius = " + radius); } } public class PolyConstructors { public static void main(String[] args) { new RoundGlyph(5); } } /* Output: Glyph() before draw() RoundGlyph.draw(), radius = 0 Glyph() after draw() RoundGlyph.RoundGlyph(), radius = 5 *///:~
根據(jù)Thinking in Java的描述:
1)在其它任何事物發(fā)生之前,將分配給對(duì)象的存儲(chǔ)空間初始化為成二進(jìn)制的零(基本類型按照其類型初始化,引用類型為null)
2)調(diào)用基類的構(gòu)造器(如果基類中實(shí)例屬性或者靜態(tài)屬性有初始化過(guò)程則先執(zhí)行初始化過(guò)程),此時(shí),調(diào)用被覆蓋后的draw(),由于步驟1的緣故,
我們會(huì)發(fā)現(xiàn)radius的值為0.
3)按照聲明的順序調(diào)用成員的初始化方法
4)調(diào)用導(dǎo)出類的構(gòu)造器主體
之所以出現(xiàn)上面的這種問(wèn)題,是因?yàn)槌跏蓟捻樞?,以及在?gòu)造器中調(diào)用了可以被子類重寫(xiě)的方法造成的。這種錯(cuò)誤往往難以發(fā)現(xiàn)。如何避免這種
錯(cuò)誤呢?在編寫(xiě)構(gòu)造器時(shí)有一個(gè)重要的準(zhǔn)則:“用盡可能簡(jiǎn)單的方法使對(duì)象進(jìn)入正常狀態(tài):如果可以的話,避免調(diào)用其他方法”
在構(gòu)造器中唯一能夠安全調(diào)用的那些方法是基類中的final方法()(也適用于private方法,因?yàn)閜rivate方法自動(dòng)屬于final方法),
使用接口的核心原因:
為了能夠向上轉(zhuǎn)型為多個(gè)基類型(以及由此帶來(lái)的靈活性).看看下面的示例代碼:
package c07.po; interface CanFight { void fight(); } interface CanClimb { void canClimb(); } interface CanSwim { void swim(); } interface CanFly { void fly(); } class ActionCharacter { public void fight() { } } class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly,CanClimb { @Override public void fly() { } @Override public void swim() { } @Override public void canClimb() { } } public class Adventure { public static void t(CanFight x) { x.fight(); } public static void u(CanSwim x) { x.swim(); } public static void v(CanFly x) { x.fly(); } public static void w(ActionCharacter x) { x.fight(); } public static void c(CanClimb c) { c.canClimb(); } public static void main(String[] args) { Hero h = new Hero(); t(h); // Treat it as a CanFight u(h); // Treat it as a CanSwim v(h); // Treat it as a CanFly w(h); // Treat it as an ActionCharacter c(h); // Treat it as a CanClimb } }
接口帶給Java多重繼承的特性,可以使一個(gè)對(duì)象C既可以被認(rèn)作是A對(duì)象,也可以被認(rèn)作是B對(duì)象,并且在被認(rèn)作是A或者B對(duì)象的時(shí)候可以利用多態(tài)實(shí)
現(xiàn)不同對(duì)象的同一方法的不同的行為。這樣就提高了設(shè)計(jì)的靈活性。并且在對(duì)象C作為方法參數(shù)時(shí),最好使用它的基類型作為參數(shù)類型,這樣對(duì)象c即
適用于以類型A作為參數(shù)類型的方法,也可以適用于類型B作為參數(shù)類型的方法,提高了靈活性。
可以這么說(shuō):接口是保證系統(tǒng)可擴(kuò)展可插拔的基礎(chǔ)(關(guān)鍵因素),一個(gè)設(shè)計(jì)良好的系統(tǒng)必須留有足夠的接口。
通過(guò)繼承,可以很容易的在接口中添加新的方法聲明,還可以通過(guò)繼承在新接口中組合數(shù)個(gè)接口。這兩種情況都可以獲得新的接口,可以參看下面的
示例代碼:
package c07.po; interface Monster { void menace(); } interface DangerousMonster extends Monster { void destroy(); } interface Lethal { void kill(); } class DragonZilla implements DangerousMonster { public void menace() { } public void destroy() { } } interface Vampire extends DangerousMonster, Lethal { void drinkBlood(); } class VeryBadVampire implements Vampire { public void menace() { } public void destroy() { } public void kill() { } public void drinkBlood() { } } public class HorrorShow { static void u(Monster b) { b.menace(); } static void v(DangerousMonster d) { d.menace(); d.destroy(); } static void w(Lethal l) { l.kill(); } public static void main(String[] args) { DangerousMonster barney = new DragonZilla(); u(barney); v(barney); Vampire vlad = new VeryBadVampire(); u(vlad); v(vlad); w(vlad); } } // /:~組合接口時(shí)的命名沖突
實(shí)現(xiàn)多重繼承時(shí),可能會(huì)遇到一個(gè)小麻煩。兩個(gè)接口的包含方法簽名和返回類型都完全一致的方法當(dāng)然沒(méi)有什么問(wèn)題,但是下面的例子就有些麻煩了
先考慮一件事情:編譯器是無(wú)法分別一個(gè)方法名和方法參數(shù)都相同僅有返回值不同的方法的。兩個(gè)方法簽名完全一致的方法會(huì)導(dǎo)致編譯器報(bào)錯(cuò)。
關(guān)于方法簽名可以這么簡(jiǎn)單的理解:方法的名字,方法的參數(shù)列表(包括參數(shù)類型和參數(shù)的順序)組成方法的方法簽名。
看看下面的示例代碼:
方法簽名
public interface F1 { void f(); String g(); } public interface F2 { void f(int i ); int g(int g); } public interface F3 { int f(); } public class F1F2 implements F1, F2 { /** * 接口F1的方法void f() 與接口F2的方法void f(int i ) 名稱相同但是參數(shù)列表不同,所以可以形成重載overload * 這是可行的,也就是不同接口兩個(gè)方法同名但是參數(shù)列表不同 */ @Override public void f(int i) { } @Override public void f() { } /** * 接口F1的String g()和接口F2的int g(int g)只有方法名稱相同,參數(shù)和返回值都不同,不形成重載 */ @Override public int g(int g) { return 0; } @Override public String g() { return null; } } public class F1F3 implements F1,F3 {//The type F1F3 must implement the inherited abstract method F3.f() /**ERROR * The return type is incompatible with F3.f() 接口F1的void f()方法與接口F3的方法int f()參數(shù)列表和名稱完全相同,只有返回值不同,不能形成重載,但是也無(wú)法通過(guò) 編譯,編譯器無(wú)法分別兩個(gè)同名同參的方法 所以類F1F3是無(wú)法同時(shí)兼容接口F1和F3的 當(dāng)然這種蛋疼的事情還是很少遇見(jiàn)的。 @Override public void f() { // TODO Auto-generated method stub } */ @Override public String g() { return null; } }
摘抄自一段提問(wèn)
What is method signature in java?
The term "method signature" has an exact meaning in Java and is explicitly defined in the Java Language Specification.
The signature of a method consists of the method name and the parameter list (type and number). It DOES NOT include the return type or modifiers such as access modifiers (public, protected,
For example, you cannot have two methods with the same name and parameter list that differs only in that one is static and the other is not, or that differ in that one returns void and the other returns a non-void value.
The use of generics resulted in the addition of the term subsignature, which is documented in the latest Java Language Specification
原文鏈接:https://answers.yahoo.com/question/index?qid=20070306205943AAAsAnx
Java給出的官方文檔的解釋是這樣的:
8.4.2. Method Signature
Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the the type parameters of M, the same formal parameter types.
The signature of a method m1 is a subsignature of the signature of a method m2 if either:
m2 has the same signature as m1, or
the signature of m1 is the same as the erasure (§4.6) of the signature of m2.
Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1.
It is a compile-time error to declare two methods with override-equivalent signatures in a class.
class Point { int x, y; abstract void move(int dx, int dy); void move(int dx, int dy) { x += dx; y += dy; } } /** This program causes a compile-time error because it declares two move methods with the same (and hence, override-equivalent) signature. This is an error even though one of the declarations is abstract. **/
The notion of subsignature is designed to express a relationship between two methods whose signatures are not identical, but in which one may override the other. Specifically, it allows a method whose signature does not use generic types to override any generified version of that method. This is important so that library designers may freely generify methods independently of clients that define subclasses or subinterfaces of the library.
class CollectionConverter { List toList(Collection c) {...} } class Overrider extends CollectionConverter { List toList(Collection c) {...} } /** Now, assume this code was written before the introduction of generics, and now the author of class CollectionConverter decides to generify the code, thus: **/ class CollectionConverter {接口中的域List toList(Collection c) {...} } /** Without special dispensation, Overrider.toList would no longer override CollectionConverter.toList. Instead, the code would be illegal. This would significantly inhibit the use of generics, since library writers would hesitate to migrate existing code. **/
放入接口中的任何域都都自動(dòng)是static 和final的,所以接口是一種便捷的用來(lái)創(chuàng)建常量的工具。在JavaSE5之前,這是產(chǎn)生enum類型的唯一途徑
接口中的域不可以是“空f(shuō)inal的”,但是可以被非常量表達(dá)式初始化。
接口的靜態(tài)常量的初始化只有在接口加載的時(shí)候才會(huì)進(jìn)行初始化,比如直接調(diào)用接口常量可以引起接口常量的初始化,再比如子類調(diào)用實(shí)現(xiàn)接口的方法
會(huì)引起接口常量的初始化.實(shí)例代碼如下:
不要總是使用接口進(jìn)行設(shè)計(jì)
public class Value { private String str; public Value(String str){ this.str = str; System.out.println(this.str); } } public interface InterFaceA { public Value v1 = new Value("v1"); Value v2 = new Value("v2"); void printValue(); } public class TC { public static Value v1TC = new Value("v1TC"); public static Value v2TC= new Value("v2TC"); } public class ImplA extends TC implements InterFaceA{ public ImplA(){ System.out.println("ImplA構(gòu)造函數(shù)來(lái)啦"); } @Override public void printValue() { System.out.println(v1); System.out.println(v2); } } package c07.ta; import static org.junit.Assert.*; import org.junit.Test; public class TestClient { /** * 直接訪問(wèn)接口常量,會(huì)引起接口常量的初始化(全部常量) */ @Test public void test1() throws Exception { Value v1 = ImplA.v1; //輸出 : v1 v2 } /** * 只初始化接口子類,只會(huì)先初始化父類的靜態(tài)常量,再初始化子類 * 并不會(huì)進(jìn)行接口的初始化,也不會(huì)有接口靜態(tài)常量的初始化 */ @Test public void test2() throws Exception { ImplA implA = new ImplA(); /*輸出: * v1TC v2TC ImplA構(gòu)造函數(shù)來(lái)啦 */ } /** *初始化接口子類,并且調(diào)用子類實(shí)現(xiàn)接口的方法,會(huì)引起接口的初始化,那么接口的靜態(tài)常量也就初始化了 */ @Test public void test3() throws Exception { ImplA implA = new ImplA(); implA.printValue(); /*輸出: v1TC v2TC ImplA構(gòu)造函數(shù)來(lái)啦 v1 v2 c07.ta.Value@18fe7c3 c07.ta.Value@b8df17 */ } }
“確定接口是理想的選擇,因而應(yīng)該總是選擇接口而不是具體的類”這其實(shí)是一種誘惑。這種邏輯看起來(lái)是這樣的:因?yàn)樾枰褂貌煌木唧w實(shí)現(xiàn),因此總應(yīng)該添加這種抽象性
。這句話本身并沒(méi)有錯(cuò),但是卻成為草率設(shè)計(jì)濫用接口的指導(dǎo)。
首先需要明確的一點(diǎn)是:任何抽象性都應(yīng)該是由真正的需求而產(chǎn)生的。當(dāng)必須時(shí),你應(yīng)該重構(gòu)接口而不是到處添加額外級(jí)別的間接性,并由此帶來(lái)額外的復(fù)雜性。
如果過(guò)度的對(duì)接口抽象,那么帶來(lái)的后果則是類膨脹和復(fù)雜性的增加,因此既要保證系統(tǒng)的靈活性和可擴(kuò)展性(利用多態(tài)特性,實(shí)現(xiàn)接口進(jìn)行抽象),也要保證系統(tǒng)的復(fù)雜性不過(guò)分提高(慎用接口)。
恰當(dāng)?shù)脑瓌t是優(yōu)先選擇類而不是接口。從類開(kāi)始,如果接口的必須性變得非常明確,那么就進(jìn)行重構(gòu)。接口是一種重要的工具,但是它們?nèi)菀妆粸E用。
所以我認(rèn)為合適的設(shè)計(jì)過(guò)程應(yīng)該是先從劃分類開(kāi)始,先劃分類的功能,再對(duì)類進(jìn)行重新審視和設(shè)計(jì),對(duì)類進(jìn)行重構(gòu)并且提煉出抽象類,在這基礎(chǔ)上再進(jìn)行接口的抽象,接口抽象后再進(jìn)行審視,對(duì)接口進(jìn)行重構(gòu),之后再審視接口與類,這樣完成從底到上,在從頂?shù)较碌脑O(shè)計(jì)一個(gè)系統(tǒng)(子系統(tǒng),模塊,三五個(gè)類組成的組件)。
author zhaob
time : 2014-09-14 16:57
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64137.html
摘要:為了方便廣大的開(kāi)發(fā)者,特此統(tǒng)計(jì)了網(wǎng)上諸多的免費(fèi),為您收集免費(fèi)的接口服務(wù),做一個(gè)的搬運(yùn)工,以后會(huì)每月定時(shí)更新新的接口。將長(zhǎng)段中文切詞分開(kāi)。 為了方便廣大的開(kāi)發(fā)者,特此統(tǒng)計(jì)了網(wǎng)上諸多的免費(fèi)API,為您收集免費(fèi)的接口服務(wù),做一個(gè)api的搬運(yùn)工,以后會(huì)每月定時(shí)更新新的接口。有些接口來(lái)自第三方,在第三方注冊(cè)就可以成為他們的會(huì)員,免費(fèi)使用他們的部分接口。 百度AccessToken:針對(duì)HTTP ...
摘要:接口測(cè)試形式單個(gè)接口測(cè)試包含性能測(cè)試和通過(guò)接口調(diào)用進(jìn)行場(chǎng)景測(cè)試。充分來(lái)說(shuō)就是接口測(cè)試相對(duì)容易實(shí)現(xiàn)自動(dòng)化持續(xù)集成。 本文你將了解到 1、接口測(cè)試基本概念,包含什么是接口,什么是接口測(cè)試,為什么要做接口測(cè)試2、接口測(cè)試用例設(shè)計(jì)3、怎樣不用寫(xiě)代碼,也能快速的根據(jù)開(kāi)發(fā)的API文檔完成接口自動(dòng)化測(cè)試腳本 注:如果你對(duì)接口基本概念和接口測(cè)試用例已熟悉,可以直接跳過(guò),其實(shí)看一遍也無(wú)防,就當(dāng)作 溫故知...
摘要:接口的對(duì)象可以利用子類對(duì)象的向上轉(zhuǎn)型進(jìn)行實(shí)例化賦值。接口文件保存在結(jié)尾的文件中,文件名使用接口名。接口相應(yīng)的字節(jié)碼文件必須在與包名稱相匹配的目錄結(jié)構(gòu)中。接口不能包含成員變量,除了全局常量定義。 概念 接口,在JAVA編程語(yǔ)言中是一個(gè)引用類型,是抽象方法的集合,接口通常以interface來(lái)聲明。一個(gè)類通過(guò)繼承接口的方式,從而來(lái)繼承接口的抽象方法。 接口中只能包含抽象方法和全局常量。 接...
摘要:接口和內(nèi)部類為我們提供了一種將接口與實(shí)現(xiàn)分離的更加結(jié)構(gòu)化的方法。 接口和內(nèi)部類為我們提供了一種將接口與實(shí)現(xiàn)分離的更加結(jié)構(gòu)化的方法。 1.抽象類和抽象方法 抽象類,是普通的類與接口之間的一種中庸之道. 抽象方法:僅有聲明而沒(méi)有方法體. 抽象類:包含抽象方法的類.如果一個(gè)類包含一個(gè)或多個(gè)抽象方法,該類必須被限定為抽象的. 如果從一個(gè)抽象類繼承,并想創(chuàng)建該新類的對(duì)象,那么久必須為基類中的所...
摘要:子類繼承抽象類,并具體實(shí)現(xiàn)方法。抽象類的使用區(qū)別于具體類,抽象類無(wú)法直接創(chuàng)建抽象類對(duì)象,但是可以聲明抽象類的變量,引用抽象類對(duì)應(yīng)具體子類對(duì)象。接口優(yōu)于抽象類中討論到一條規(guī)則接口優(yōu)于抽象類。接口聲明能力,抽象類提供默認(rèn)實(shí)現(xiàn)全部或部分方法。 接口 類,強(qiáng)調(diào)數(shù)據(jù)類型(自定義)的概念,在一些情況下,并不能反映對(duì)象以及對(duì)象操作的本質(zhì)。有時(shí)我們關(guān)注的并非對(duì)象的類型,而是對(duì)象的能力。 接口聲明一組功...
摘要:面向?qū)ο蠡驹瓌t單一職責(zé)原則與接口隔離原則面向?qū)ο蠡驹瓌t單一職責(zé)原則與接口隔離原則面向?qū)ο蠡驹瓌t里式代換原則與依賴倒置原則面向?qū)ο蠡驹瓌t最少知道原則與開(kāi)閉原則一單一職責(zé)原則單一職責(zé)原則簡(jiǎn)介單一職責(zé)原則的英文名稱是,簡(jiǎn)稱。 面向?qū)ο蠡驹瓌t(1)- 單一職責(zé)原則與接口隔離原則 面向?qū)ο蠡驹瓌t(1)- 單一職責(zé)原則與接口隔離原則面向?qū)ο蠡驹瓌t(2)- 里式代換原則與依賴倒置原則面...
閱讀 2311·2023-04-25 14:22
閱讀 3748·2021-11-15 18:12
閱讀 1303·2019-08-30 15:44
閱讀 3224·2019-08-29 15:37
閱讀 654·2019-08-29 13:49
閱讀 3466·2019-08-26 12:11
閱讀 887·2019-08-23 18:28
閱讀 1592·2019-08-23 14:55