国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Effective Java 筆記

enali / 3073人閱讀

摘要:如果采用抽象類,則屬性組合可能導(dǎo)致子類的組合爆炸。內(nèi)部類的設(shè)計靜態(tài)成員類用修飾的內(nèi)部類,可以不依賴于外部實例進行創(chuàng)建。如下所示其構(gòu)造函數(shù)默認是,并且無法修改。

對所有對象都通用的方法 equals和hashCode方法的關(guān)系

重寫equals方法必須也要重寫hashCode方法。

equals用的屬性沒變,則多次調(diào)用hashCode返回值也必須保持不變。

equals比較相等的對象,hashCode也必須相同。反之不然。

所處相同hash bucket的對象,hashCode可能不同,因為在求解bucket位置時會對hashCode進行截斷,根據(jù)bucket大小采用后段值。

clone方法的設(shè)計原則

Cloneable接口并不提供接口方法clone,clone是Object類實現(xiàn)的基本方法。

實現(xiàn)Cloneable接口的類應(yīng)該提供一個public的clone函數(shù)覆蓋原來protect的方法。

clone方法首先調(diào)用super的clone方法,然后再處理需要深層拷貝的內(nèi)部屬性。

道行不深不要使用該接口。

Comparable接口的設(shè)計原則

傳遞(a>b,b>c則a>c),對稱(a>b則b

最好compareTo方法和equals方法保持一致。

類和接口 訪問控制

頂層類(非嵌套)和接口只有兩種訪問級別,包私有和公有的。聲明為public則類為公有的,無聲明則默認包私有。

成員域、方法、嵌套類和嵌套接口有private、包私有(默認)、protected和public四種級別。可訪問性依次增強。

子類在重寫父類方法時,可訪問性只能保持不變或者增強。因為通過超類的引用也可以正常的使用子類實例。

實例域一定不要設(shè)置為public。否則,會失去對該域的控制權(quán)。

公有類的暴露公有域

公用類如果暴露了自己的域,則會導(dǎo)致客戶端濫用該域,并且該公用類在以后的升級中無法靈活的改變屬性的表達方式。

不可變對象的設(shè)計

String、基本類型的包裝類是不可變對象。

設(shè)計不可變類應(yīng)該遵循以下準則:

不要提供任何修改對象狀態(tài)的方法。

保證類不會被擴展。一般類可以加final。

所有的域都是final。

所有的域都是私有的。

保證所有可變域無法被客戶端獲得。

不可變對象線程安全,可以被自由的共享。不可變類不應(yīng)該提供clone和拷貝構(gòu)造器,直接共享最好,但是String還是提供了拷貝構(gòu)造器。

不可變類也有一定的劣勢,因為一個操作可能涉及多個臨時的不可變類,而導(dǎo)致大量對象的創(chuàng)建和銷毀,所以此時應(yīng)該采用不可變類配套的可變類。如String類對應(yīng)的StringBuilder。

應(yīng)該提供盡可能小的可變狀態(tài)。

復(fù)合優(yōu)先于繼承

繼承會破壞封裝性,實現(xiàn)繼承需要對父類的實現(xiàn)進行充分的了解。所以父類和子類應(yīng)該實現(xiàn)在同一個包內(nèi),由同一個開發(fā)團隊維護。

一個類在設(shè)計時應(yīng)該明確指明是不是為了可被繼承而設(shè)計的。

一個類如果沒有考慮自己可能被繼承,有些方法可能會被重寫,則其內(nèi)部調(diào)用這些可被重寫方法的方法就可能會出現(xiàn)意想不到的異常行為。繼承該方法的子類會調(diào)用父類的內(nèi)部方法,而父類內(nèi)部方法的更新可能會導(dǎo)致子類的異常。

接口優(yōu)于抽象類

現(xiàn)有的類可以很容易的加入新的接口,因為接口可以實現(xiàn)多個。

類只允許有一個父類,如果用抽象類描述共性,則需要該共性的類必須為該抽象類的后代,即使這些子類并沒有很顯然的關(guān)系。

接口可以讓我們實現(xiàn)非層次結(jié)構(gòu)的類框架。如果采用抽象類,則屬性組合可能導(dǎo)致子類的組合爆炸。

接口的缺點是擴展新方法時,所有實現(xiàn)該接口的類都要重新添加該方法,而抽象類可以提供默認實現(xiàn)。不過,現(xiàn)在Java8提供了default描述默認實現(xiàn)方法,似乎這種弊端可以避免。

接口中定義屬性

接口中定義的屬性默認為final static。

最好不要用接口來定義屬性,因為實現(xiàn)該接口的類會引入這些屬性,造成類的命名空間污染。接口應(yīng)該用來描述類可以執(zhí)行的動作。

內(nèi)部類的設(shè)計

靜態(tài)成員類:用static修飾的內(nèi)部類,可以不依賴于外部實例進行創(chuàng)建。

非靜態(tài)成員類:創(chuàng)建時依賴于外部類的實例,并且創(chuàng)建后與外部實例綁定。無靜態(tài)屬性。

匿名內(nèi)部類:作為參數(shù),只會被實例化一次。和非靜態(tài)成員類似。在非靜態(tài)環(huán)境中,會與外部類的實例綁定。

局部類:聲明局部變量的地方可以聲明局部類,它有名字,可以在局部被多次實例化。在非靜態(tài)環(huán)境中,會與外部類的實例綁定。

泛型 不要在代碼中使用原生類型

如下代碼使用原生類型:

    ArrayList a=new ArrayList();
    a.add(new Object());

以上代碼編譯和運行都可以通過,但是埋下了很多隱患。

List是List的子類,而不是List的子類。List這種原生類型逃避了類型檢查。

List中add任何對象都對。List的變量可以引用任何參數(shù)化(非參數(shù)也可以)的List,但是無法通過該變量添加非null元素。

假設(shè)Men extends Person, Boy extends Men:

表示上界,<? super T>表示下界。

ArrayList ml=new ArrayList();,等號右邊部分可以是Men與其子類的參數(shù)化ArrayList,或者是ArrayList<>()和ArrayList()。初始化時可以在new ArrayList()中填入Boy對象,此后不能再往ml里存元素,從ml的視角,ml可能是ArrayList、ArrayList等等,存入任何Men或者其子類對象都不合適,為了安全起見都不允許存。只能取元素,并且取出的元素只能賦值給Men或者其基類的引用,因為其中元素可能存了任何Men的子類,為了保險起見取出的值用Men或其基類表示。

ArrayList ml=new ArrayList();,初始化時可以存Person對象,之后可以再存入Men(與其子類,這是默認的),但是再存入Person對象是錯誤的。從ml視角,等號右邊可以是ArrayList()、ArrayList()等等,所以最高只能存入Men對象。取出的元素都是Object,因為等號右邊可以是ArrayList()。

總結(jié)2和3條,可知 <? super T>是對等號右邊實參數(shù)化ArrayList的限制,而不是對ArrayList中可存入元素的描述。因為從引用ml中無法得知其實際指向的是那種參數(shù)化的ArrayList實例,所以再往其中添加元素時會采用最謹慎的選擇。

列表和數(shù)組的區(qū)別

數(shù)組是協(xié)變的,也就是Fruit[] fs= new Apple[5];是合法的,因為Apple是Fruit的子類,則數(shù)組也成父子關(guān)系,而列表則不適用于該規(guī)則。數(shù)組的這種關(guān)系容易引發(fā)錯誤,如fs[0]= new Banana(),編譯時沒錯,這在運行時報錯。

創(chuàng)建泛型數(shù)組是非法的,如new E[]; new List[]; new List[] 。泛型參數(shù)在運行時會被擦除,List[]數(shù)組可能存入List對象,因為運行時兩者都是List對象,這顯然是錯誤的,所以泛型不允許用在數(shù)組中。

如下代碼在編譯時不會出錯,在運行時出錯java.lang.ClassCastException

ArrayList list=new ArrayList();  
        for (int i = 0; i < 10; i++) {  
            list.add(""+i);  
        }
        //該行報錯       
        String[] array= (String[]) list.toArray();  
}    

原因很迷,toArray返回的是Object[]數(shù)組,但是不能強制轉(zhuǎn)化為String[],明明元素實際類型是String。有的解釋說,在運行時只有List的概念,而沒有List概念。我感覺事情沒這么簡單。

泛型和泛型方法

泛型是在整個類上采用泛型,這樣可以在類內(nèi)部方便的使用泛型參數(shù)。泛型方法是更精細的利用參數(shù)類型,將泛型參數(shù)設(shè)定在每個方法上。

比較下面兩個接口,體會其中不同:

public interface Comparable {
    public int compareTo(T o);
}

public interface Comparable2 {
    public  int compareTo2(T o);
}

public class Apple implements Comparable, Comparable2{
    @Override
    public int compareTo(Apple o) {
        return 0;
    }

    @Override
    public  int compareTo2(T o) {
        //T 可以為任何類,所以Apple可以和任何類比較
        return 0;
    }
}
遞歸類型限制

有類:

class Apple implements Comparable{

}

class RedApple extends Apple{

}

有方法:

public static  > T get(T t){
    return t;
}

該方法就采用了遞歸的類型限制,因為泛型T被限制為 Comparable的子類,而Comparable中又包含了T,這形成一種遞歸的類型限制。Apple類可以調(diào)用該函數(shù),RedApple則會出現(xiàn)錯誤,如下所示。

RedApple ra=new RedApple();
Apple a= get(ra); //正確   
RedApple b=get(ra); //錯誤

原因是在調(diào)用泛型函數(shù)時,會自動進行類型推斷,第一個get函數(shù)根據(jù)左邊參數(shù),推斷T為Apple,符合條件。在第二個get公式中,推斷T為RedApple,不符合get函數(shù)的泛型限制條件。

一個比較復(fù)雜的泛型函數(shù)
public static > T max(List list)

其中>描述了T實現(xiàn)了Comparable接口或者其基類實現(xiàn)了該接口,通過繼承獲得Comparable的情況比較常見,這增加了該函數(shù)的通用性。參數(shù)List表示List只要存的是T的子類就可以,這是顯然合理的,同樣增強了該函數(shù)的通用性。

異構(gòu)容器

一個容器如Set只有1個類型參數(shù),Map只有2個類型參數(shù),但是有時候需要多個類型參數(shù)。下面是一個設(shè)計巧妙、可以容納多個類型參數(shù)的類。

public static void main(String[] args){
    Favorites f =new Favorites();
    f.putFavorite(String.class, "Java");
    f.putFavorite(Integer.class, 1111);
    f.putFavorite(Class.class, Favorites.class);
    int fi=f.getFavorite(Integer.class);
}

public class Favorites{
    private Map, Object> favorites=new HashMap, Object>();

    public  void putFavorite(Class type, T instance){
        if(type==null)
            throw new NullPointerException("Type is null");
        favorites.put(type, instance);
    }

    public  T getFavorite(Class type){
        return type.cast(favorites.get(type));
    }
}

String.class為Class的實例,Integer.class為Class的實例,這兩者顯然不是同一個類,但是卻可以放在同一個Map中。Map中采用了通配符?,按理Map無法再加入任何元素,但是該通配符并不是直接表示Map的類型參數(shù),而是Class。因此Map的鍵值可以是Class、Class等不同的類型,因此成為一個異構(gòu)容器。

其中Map實例favorites并沒有限制value一定是key描述的類的實例,而方法putFavorite通過類型參數(shù)T,巧妙的限制了兩者的關(guān)系。

枚舉和注解 枚舉類型舉例

枚舉類型更像是一個不能new的類,只能在定義時就實例化好需要的固定數(shù)目的實例。如下所示:

public enum Planet {
    VENUS(2),
    EARTH(3),
    MARS(5);

    int data;

    Planet(int i){
        this.data=i;
    }

    public int getData(){
        return data;
    }
}

其構(gòu)造函數(shù)默認是private,并且無法修改。在枚舉中還可以定義抽象方法,如下:

public enum Operation{
    PLUS { double apply(double x, double y){return x+y;}},
    MINUS { double apply(double x, double y){return x-y}};

    abstract double apply(double x, double y);
}
自定義注解的例子

定義一個用來測試方法是否能拋出目標異常的注解。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest{
    Class[] value();
}

元注解指明了該注解在運行時保留,并且只適用于注解方法。

使用如下:

@ExpectionTest({IndexOutOfBoundsException.class, NullPointerException.class})
public static void doublyBad(){
    List list=new ArrayList();
    //該方法會拋出IndexOutOfBoundsException
    list.addAll(5,null);
}

測試過程實現(xiàn)如下:

public static void main(String[] args) throws Exception{
    int tests=0;
    int passed=0;
    Class testClass=Class.forName(args[0]);
    for(Method m : testClass.getDeclaredMethods()){
        if(m.isAnnotationPresent(ExceptionTest.class)){
            tests++;
            try{
                m.invoke(null);
                System.out.printf("Test failed: no exceptions");
            }catch( Throwable wrappedExc){
                Throwable exc = wrappedExc.getCause();
                Class[] excTypes=m.getAnnotation(ExceptionText.class).value();
                int oldPaassed=passed;
                for(Class excType:excTypes){
                    if(excType.isInstance(exc)){
                        passed++;
                        break;
                    }
                }

                if(passed==oldPassed)
                    System.out.printf("Test failed");
            }
        }
    }
}
方法 必要時進行保護性拷貝

構(gòu)造函數(shù)在接受外來參數(shù)時,必要時需要拷貝參數(shù)對象,而不是直接將參數(shù)對象賦值給自己的屬性。因為直接采用外部傳入的對象,外部可以任意的修改這些對象,從而導(dǎo)致你自己設(shè)計的類內(nèi)部屬性變化。如果不想讓使用你設(shè)計的類的客戶有修改其內(nèi)部屬性的權(quán)利,除了設(shè)置為private外,還應(yīng)該注意采用拷貝的方式使用外部傳入的數(shù)據(jù)。選擇copy而不是clone,是因為傳入對象可能是客戶定制過的參數(shù)子類,該子類仍然可能將其暴露在外面。

需要保護內(nèi)部屬性不被修改,除了關(guān)注構(gòu)造函數(shù)的參數(shù),還需要關(guān)注get類似的方法。這些返回內(nèi)部屬性的方法,應(yīng)該返回拷貝過的屬性對象。

慎用重載

重載方法是靜態(tài)的,在編譯時就已經(jīng)選擇好,根據(jù)參數(shù)的表面類型,如Collection c=new ArrayList(),有兩個重載函數(shù),getMax(Collection a)getMax(ArrayList a),在調(diào)用getMax(c)時,會選擇getMax(Collection a)方法,該選擇在編譯時就決定好了。當重載方法有多個參數(shù)時,情況會變得更復(fù)雜,選擇結(jié)果可能出人意料。

而方法的重寫選擇時動態(tài)的,在運行時根據(jù)調(diào)用者的實際類型決定哪個方法被調(diào)用。

慎用可變參數(shù)

可變參數(shù)可以讓用戶靈活的填入不同數(shù)量的參數(shù),但是該方法本質(zhì)上是將參數(shù)組織成數(shù)組,所以每次調(diào)用這些方法時都會涉及數(shù)組的創(chuàng)建和銷毀,開銷較大。

并發(fā) 同步的意義

保證數(shù)據(jù)從一個一致狀態(tài)轉(zhuǎn)一到另一個一致狀態(tài),任何時候讀取該數(shù)據(jù)都是一致的。

保證對數(shù)據(jù)的修改,其它線程立即可見。

讀寫變量是原子性的

除了double和long以外,讀寫變量是原子性的。但是Java無法保證一個線程的修改對另一個線程是可見的。

在同步模塊中小心調(diào)用其它方法

如果一個同步方法在其中調(diào)用了一個不由自己控制的方法,比如客戶傳入的方法,客戶可能在實現(xiàn)方法時申請同步鎖,或者啟動新線程申請鎖,這可能會導(dǎo)致死鎖。

并發(fā)工具優(yōu)先于wait和notify

java.util.concurrent包提供了執(zhí)行框架、并發(fā)集合和同步器三種工具,應(yīng)該盡量使用這些工具來實現(xiàn)并發(fā)功能,而不是使用wait、notify。

如果使用wait,notify則應(yīng)該采用如下模式:

    public void waitA(){
        synchronized(a){    //獲得鎖
            while(a>10)     //放在while循環(huán)中保證滿足條件
                try {
                    a.wait(); //釋放鎖、如果被喚醒則需要重新獲得鎖
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }

    //其它線程調(diào)用該方法喚醒等待線程
    public void notifyA(){
        a.notifyAll();
    }

notifyAll方法相較于notify方法更安全,它保證喚醒了所有等待a對象的線程,被喚醒不代表會被立即執(zhí)行,因為還需要獲得鎖。

不要對線程調(diào)度器有任何期望

Tread yield(讓步)即當前線程將資源歸還給調(diào)度器,但是并不能保證當前線程下面一定不會被選中。線程的優(yōu)先級設(shè)置也是不能保證按你預(yù)期的進行調(diào)度。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/69409.html

相關(guān)文章

  • Effective Java筆記

    摘要:構(gòu)造器的參數(shù)沒有確切地描述其返回的對象,適當名稱的靜態(tài)工廠方法更容易使用,也易于閱讀。在文檔中,沒有像構(gòu)造器那樣明確標識出來,因此,對于提供了靜態(tài)工廠方法而不是構(gòu)造器的類來說,要查明如何實例化一個類,有點困難。 第二章 創(chuàng)建和銷毀對象 第1條 考慮用靜態(tài)工廠方法代替構(gòu)造器 兩者創(chuàng)建對象的形式,例如:構(gòu)造器是new Boolean();靜態(tài)工廠方法是 public static Bool...

    Drummor 評論0 收藏0
  • effective java 觀后感

    摘要:這本書是我第一次買的,從買來至今整本書還沒有看完,只看了一半,原因是個人比較懶,而且玩的心比較大,經(jīng)過這么多年的沉淀,終于可以偷點時間寫下對于這本書的觀后感了整本書給我的感覺不像是一個技術(shù)書,更多的是講解一些實用技巧,而對于我這個職場菜鳥來 effective Java 這本書是我第一次買的, 從買來至今整本書還沒有看完, 只看了一半, 原因是個人比較懶,而且玩的心比較大,經(jīng)過這么多年...

    calx 評論0 收藏0
  • Effective Java》學習筆記 第二章 創(chuàng)建和銷毀對象

    摘要:第二章創(chuàng)建和銷毀對象何時以及如何創(chuàng)建對象,何時以及如何避免創(chuàng)建對象,如何確保他們能夠適時地銷毀,以及如何管理對象銷毀之前必須進行的各種清理動作。表示工廠方法所返回的對象類型。 第二章 創(chuàng)建和銷毀對象 何時以及如何創(chuàng)建對象,何時以及如何避免創(chuàng)建對象,如何確保他們能夠適時地銷毀,以及如何管理對象銷毀之前必須進行的各種清理動作。 1 考慮用靜態(tài)工廠方法代替構(gòu)造器 一般在某處獲取一個類的實例最...

    tinylcy 評論0 收藏0

發(fā)表評論

0條評論

enali

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<