《Effective Java》讀書筆記(持續(xù)更新)

引言

創(chuàng)建和銷毀對象

何時以及如何創(chuàng)建對象,何時以及如何避免創(chuàng)建對象,如何確保創(chuàng)建的對象能夠被適時地銷毀,以及如何管理銷毀之前必須進行的所有清楚工作

第1條 考慮用靜態(tài)工廠方法代替構(gòu)造器

  • 靜態(tài)工廠方法與構(gòu)造器不同的第一大優(yōu)勢在于:它們有名稱
  • 靜態(tài)工廠方法與構(gòu)造器不同的第二大優(yōu)勢在于:不必再每次調(diào)用它們的時候都創(chuàng)建一個新對象
  • 靜態(tài)工廠方法與構(gòu)造器不同的第三大優(yōu)勢在于,它們可以返回原返回類型的任何子類型的對象

服務(wù)提供者框架包含三個組件:

服務(wù)接口 提供者實現(xiàn)
提供者注冊API 系統(tǒng)用來注冊實現(xiàn),讓客戶端訪問
服務(wù)訪問API 客戶端用來獲取服務(wù)的實例
服務(wù)提供者接口(可選) 負責(zé)創(chuàng)建其服務(wù)實現(xiàn)的實例

示例代碼

public interface Service
{
    public void service();
}
public interface Provider
{
    Service newService();
}
public class Services
{
    private Services(){}
    private static final Map< String, Provider > = new ConcurrentHashMap< String, Provider >();
    public static final String DEFAULT_PROVIFER_NAME = "<def>";
    
    public static void registerDefaultProvider( Provider p )
    {
        registerProvider( DEFAULT_PROVIDER_NAME, p );
    }
    public static void registerProvider( String name, Provider p )
    {
        providers.put( name, p );
    }

    public static Service newInstance()
    {
        return newInstance( DEFAULT_PROVIDER_NAME );
    }
    public static Service newInstance( String name )
    {
        Provider p = providers.get( name );
        if ( p == null )
        {
            throw new IllegalArgumentException( "No provider registered with name : " + name );
        }
        return p.newServices();
    }
}
  • 靜態(tài)工廠方法的第四大優(yōu)勢在于,在創(chuàng)建參數(shù)化類型實例的時候,它們使代碼變得更加簡潔
    通過語法糖來簡化泛型代碼
public static< K, V > HashMap< K, V > newInstance()
{
    return new HashMap< K, V >();
}

Map< String, List< String > > map = HashMap.newInstance();

  • 靜態(tài)工廠方法的主要缺點在于,類如果不含公有的或者受保護的構(gòu)造器,就不能被子類化
  • 靜態(tài)工廠方法的第二個缺點在于,它們與其他的靜態(tài)方法實際上沒有任何區(qū)別
    靜態(tài)工廠方法慣用名稱:

valueOf 返回的實例和其參數(shù)具有相同的值,實際上是類型轉(zhuǎn)換方法
of valueOf的替代
getInstance 根據(jù)方法的參數(shù)返回相應(yīng)的實例,對于Singleton,無參數(shù),返回唯一的實例
newInstance 和getInstance功能類似,但確保返回的每個實例是新創(chuàng)建的
getType 和getInstance功能類似,在工廠方法處于不同的類中的時候使用,Type表示工廠方法所返回的對象類型
newType 和newInstance功能類似,在工廠方法處于不同的類中的時候使用

第2條 遇到多個構(gòu)造器參數(shù)時要考慮用構(gòu)建器

靜態(tài)工廠和構(gòu)造器的局限:不能很好地擴展到大量的可選參數(shù)

重載構(gòu)造器模式可行,但是當(dāng)有許多參數(shù)的時候,客戶端代碼會很難編寫,且難以閱讀

一種解決方法 JavaBean模式
調(diào)用一個無參構(gòu)造器來創(chuàng)建對象,然后調(diào)用setter方法來設(shè)置每個必要的參數(shù),以及每個相關(guān)的可選參數(shù)
潛在問題:狀態(tài)不一致、阻止了把類做成不可變的可能、

Builder模式 安全性和可讀性的折衷

不直接生成想要的對象,讓客戶端利用所有必要的參數(shù)調(diào)用構(gòu)造器,得到一個builder對象,然后客戶端在builder對象上調(diào)用類似于setter方法,來設(shè)置每個相關(guān)的可選參數(shù),最后客戶端調(diào)用無參的build方法來生成不可變的對象

示例代碼

public class NutritionFacts
{
    private final int seringSize;
    private final int servings;
    private final int calories;

    public static class Builder
    {
        private final int servingSize;
        private final int servings;
        
        private final int calories = 0;
        
        public Builder( int servingSize, int servings )
        {
            this.servingSize = servingSize;
            this.servings = servings;
        }
        
        public Builder calories( int val )
        {
            calories = val;
            return this;
        }
        public NutritionFacts build()
        {
            return new NutritionFacts( this );
        }
    }
    
    private NutritionFacts( Builder builder )
    {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
    }
}

//call code
NutritionFacts cocaCola = new NutritionFacts.Builder( 240, 8 ).calories( 100 ).build();

builder模式模擬了具名的可選參數(shù)
Java中傳統(tǒng)的抽象工廠實現(xiàn)是Class對象,用newInstance方法充當(dāng)builer方法一部分。但newInstance方法總是企圖調(diào)用類的無參構(gòu)造器,而構(gòu)造器可能不存在,同時,該方法還會傳播由無參構(gòu)造器拋出的異常,Class.newInstance破壞了編譯時的異常檢查
builer不足:為了創(chuàng)建對象,必須先創(chuàng)建其構(gòu)建器,可能造成性能問題;比重疊構(gòu)造器模式更加冗長,只有在很多參數(shù)的時候才使用。

如果類的構(gòu)造器或者靜態(tài)工廠中具有多個參數(shù),設(shè)計時,Builder模式是個不錯的選擇

第3條 用私有構(gòu)造器或者枚舉類型強化Singleton屬性

使類成為Singleton使客戶端測試十分困難,因為無法替換其模擬實現(xiàn),除非實現(xiàn)一個充當(dāng)類型的接口

兩種Singleton方法

public class Elvis
{
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }
    public void leaveTheBuilding() { ... }
}

//another
public class Elvis
{
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }
    public static Elvis getInstance() { return INSTANCE; }
    public void leaveTheBuilding(){ ... }
}

享有特權(quán)的客戶端可以借助AccessibleObject.setAccessible方法,通過反射機制調(diào)用私有構(gòu)造器

第三種Singleton實現(xiàn)方法,編寫一個包含單個元素的枚舉類型

public enum Elvis
{
    INSTANCE;
    public void leaveTheBuilding() { ... }
}

更加簡潔,無償?shù)靥峁┝诵蛄谢瘷C制,絕對防止多次實例化,單元素的枚舉類型已經(jīng)成為實現(xiàn)Singleton的最佳方法

第4條 通過私有構(gòu)造器強化不可實例化的能力

企圖通過將類做成抽象類來強制該類不可被實例化,是行不通的
一種私有構(gòu)造器實現(xiàn)

public class UtilityClass
{
    private UtilityClass()
    {
        throw new AssertionError();
    }
}

第5條 避免創(chuàng)建不必要的對象

一般來說,最好能重用對象而不是在每次需要的時候就創(chuàng)建一個相同功能的新對象

String s = new String( "stringtest" );//Donot do this
String s = "stringtest";

對于同時提供了靜態(tài)工廠方法和構(gòu)造器的不可變類,通常優(yōu)先使用靜態(tài)工廠方法,避免創(chuàng)建不必要的對象

public class Person
{
    private final Date birthDate;
    
    //Donot do this
    public boolean isBabyBoomer()
    {
        Calendar gmtCal = Calendar.getInstance( TimeZone.getTimeZone("GMT") );
        gmtCal.set( 1946, Calendar.JANUARY, 1, 0, 0, 0 );
        Date boomStart = gmtCal.getTime();
        gmtCal.set( 1965, Calendar.JANUARY, 1, 0, 0, 0 );
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo( boomStart )>=0
                && birthData.compareTo( boomEnd )<0;
    }
}

//better implements
public class Person
{
    private final Date birthDate;
    
    private static final Date BOOM_START;
    private static final Date BOOM_END;
    static
    {
        Calendar gmtCal = Calendar.getInstance( TimeZone.getTimeZone( "GMT" ) );
        gmtCal.set( 1946, Calendar.JANUARY, 1, 0, 0, 0 );
        BOOM_START = gmtCal.getTime();
        gmtCal.set( 1965, Calendar.JANUARY, 1, 0, 0, 0 );
        BOOM_END = gmtCal.getTime();
    }
    
    //Do this
    public boolean isBabyBoomer()
    {
        return birthDate.compareTo( BOOM_START)>=0
                && birthData.compareTo( BOOM_END)<0;
    }
}

要優(yōu)先使用基本類型而不是裝箱基本類型,要當(dāng)心無意識的自動裝箱
通過維護自己的對象池來避免創(chuàng)建對象并不是一種好的做法,除非池中的對象是非常重量級的,真正正確使用對象池的典型對象示例就是數(shù)據(jù)庫連接池
一般而言,維護自己的對象池會增加代碼的復(fù)雜性,增加內(nèi)存占用,還會損害性能

當(dāng)應(yīng)該重用現(xiàn)有對象的時候,不要創(chuàng)建新的對象
當(dāng)該創(chuàng)建新對象的時候,不要重用現(xiàn)有的對象
在提倡使用保護性拷貝的時候,因重用對象而付出的代價要遠遠大于因創(chuàng)建重復(fù)對象而付出的代價
必要時如果沒能實施保護性拷貝,將會導(dǎo)致潛在的錯誤和安全漏洞;不必要地創(chuàng)建對象則只會影響程序的風(fēng)格和性能

第6條 消除過期的對象引用

過期引用,指永遠也不會再被解除的引用。
如果一個對象引用被無意識地保留起來了,那么垃圾回收機制不僅不會處理這個對象,而且也不會處理被這個對象所引用的所有其他對象

清空對象引用是一種例外,而不是一種規(guī)范行為
只要類是自己管理內(nèi)存,應(yīng)該警惕內(nèi)存泄漏問題

內(nèi)存泄漏的另外一個常見來源是緩存:只要在緩存之后存在對某個項的鍵的引用,該項就有意義,可以用WeakHashMap代表緩存;當(dāng)緩存中的項過期之后,會自動被刪除

只有當(dāng)所要的緩存項的生命周期是由該鍵的外部引用而不是由值決定時,WeakHashMap才有用處

內(nèi)存泄漏的第三個常見來源是監(jiān)聽器和其他回調(diào):確保回調(diào)立即被當(dāng)做垃圾回收的最佳方法是只保存它們的弱引用

第7條 避免使用終結(jié)方法

終結(jié)方法(finalize)通常是不可預(yù)測的,也是很危險的,一般情況下是不必要的
終結(jié)方法的缺點在于不能保證會被及時地執(zhí)行
Java語言規(guī)范不僅不保證終結(jié)方法會被及時地執(zhí)行,而且根本就不保證其被執(zhí)行,不應(yīng)該依賴終結(jié)方法來更新重要的持久狀態(tài)
使用終結(jié)方法有一個非常嚴重的性能損失

對于確實需要終止的方法,應(yīng)提供一個顯示的終止方法,并要求改類的客戶端在每個實例不再有用的時候調(diào)用這個方法
顯式的終止方法通常與try-finally結(jié)構(gòu)結(jié)合起來使用,以確保及時終止
終結(jié)方法有兩種合法用途

  • 當(dāng)對象所有者忘記調(diào)用顯式終止方法時,終結(jié)方法充當(dāng)“安全網(wǎng)”
  • 與對象的本地對等體有關(guān)。本地對等體是一個本地對象,普通對象通過本地方法委托給一個本地對象,垃圾回收器無法感知本地對象的存在,當(dāng)Java對等體被回收時,它不會被回收

“終結(jié)方法鏈”不會被自動執(zhí)行,需要進行顯式調(diào)用
另外一種可選方法是終結(jié)方法守衛(wèi)者

對于所有對象都通用的方法

對equals、hashCode、toString、clone和finalize深入分析

類和接口

關(guān)于類和接口的一些指導(dǎo)原則

泛型

通過泛型,告訴編譯器每個集合中接受哪些對象類型,編譯器自動為插入進行轉(zhuǎn)化,并在編譯時告知是否插入了類型錯誤的對象,使得程序更加安全和清楚

第23條 請不要在新代碼中使用原生態(tài)類型

泛型類和接口統(tǒng)稱為泛型,每種泛型定義一組參數(shù)化的類型,每個泛型都定義一個原生態(tài)類型,即不帶任何實際類型參數(shù)的泛型名稱
如果不提供類型參數(shù),使用集合類型和其他泛型是合法的,但是不應(yīng)該這么做。如果使用原生類型,就失掉了泛型在安全性和表述性方面的所有優(yōu)勢
泛型有子類型化的規(guī)則,List< String>是原生態(tài)類型List的一個子類型,而不是參數(shù)化類型List< Object >的子類型,如果使用像List這樣的原生態(tài)類型,就會失掉類型安全性,但是如果使用像List< Object >這樣的參數(shù)化類型,則不會
Java提供了一種對于不確定的類型的安全的替代方法,稱作無限制的通配符類型:如果要使用泛型,但不確定或者不關(guān)心實際的類型參數(shù),就可以使用一個問號代替,可以持有任何集合

通配符類型是安全的,原生態(tài)類型是不安全的:
可以將任何元素放進使用原生態(tài)類型的集合中,但不能將任何元素( null除外 )放到通配符類型集合中

例外情況

  • 在類文字中必須使用原生態(tài)類型
    規(guī)范不允許使用參數(shù)化類型(雖然允許數(shù)組類型和基本類型),如List.class、String[].class和int.class都合法,但是List< String.class >和List< ? >.class不合法
  • 與instanceof操作符有關(guān)
    泛型信息在運行時被擦除,在參數(shù)化類型而非無限制通配符類型上使用instanceof操作符是非法的,用無限制通配符類型代替原生態(tài)類型,對instanceof操作符的行為不會產(chǎn)生影響
    在這種情況下,直接用原生態(tài)類型即可,如
if ( 0 instanceof Set )
{
    Set< ? > = ( Set< ? > )o;
    ...
}

注意:一旦確定o為Set,就需要將其轉(zhuǎn)換成通配符類型Set< ? >,而不是轉(zhuǎn)換成遠程類型Set

使用原生態(tài)類型會在運行時導(dǎo)致異常,因此不要在新代碼中使用

第24條 消除非受撿警告

使用泛型編程時,會遇到許多編譯器警告:非受檢強制轉(zhuǎn)化警告、非受檢方法調(diào)用警告、非受撿普通數(shù)組創(chuàng)建警告以及非受檢轉(zhuǎn)換警告

要盡可能地消除每一個非受檢警告

如果無法消除警告,同時可以證明引起警告的代碼是類型安全的,(只有在這種情況下才)可以用一個@SuppressWarnings( "unchecked" )注解來禁止這條警告

SuppressWarnings注解可以用在任何粒度的級別中,從單獨的局部變量聲明到整個類都可以,應(yīng)該始終在盡可能小的范圍內(nèi)使用SuppressWarnings注解

每當(dāng)使用SuppressWarnings( "unchecked" )注解時,都要添加一條注釋,說明為什么這么做是安全的

第25條 列表優(yōu)先于數(shù)組

數(shù)組與泛型的不同點

  • 數(shù)組是協(xié)變的,泛型是不可變的

協(xié)變表示如果Sub為Super的子類型,那么數(shù)組類型Sub[]就是Super[]的子類型

  • 數(shù)組是具體化的,泛型是通過擦除來實現(xiàn)的
    數(shù)組會在運行時才知道并檢查它們的元素類型約束,泛型只在編譯時強化它們的類型信息,并在運行時丟棄(擦除) 它們的元素類型信息

數(shù)組和泛型不能很好地混合使用,如創(chuàng)建泛型、參數(shù)化類型或者類型參數(shù)的數(shù)組是非法的,因為泛型數(shù)組是非法的,不是類型安全的

從技術(shù)角度來說,像E、List< E >和List< String >這樣的類型應(yīng)稱作是不可具體化類型,不可具體化類型是指其運行時表示法包含的信息比它編譯時表示法包含的信息更少的類型
唯一可具體化參數(shù)化類型是無限制的通配符類型,如List< ? >和Map< ? >,雖不常用,但創(chuàng)建無限制通配類型的數(shù)組是合法的

當(dāng)?shù)玫椒盒蛿?shù)組創(chuàng)建錯誤時,最好的解決方法通常是優(yōu)先使用集合類型List< E >,而不是數(shù)組類型E[],損失一些性能或者簡潔性,換回更高的類型安全性和互用性

static Object reduce( List list, Function f, Object initVal )
{
    synchronized( list )
    {
        Object result = initVal;
        for ( Object o : list )
        {
            result = f.apply( result, o );
        }
        return result;
    }
}

interface Function
{
    Object apply( Object arg1, Object arg2 );
}

不要從同步區(qū)域中調(diào)用外來方法

static Object reduce( List list, Function f, Object initVal )
{
    Object[] snapshot = list.toArray();
    Object result = initVal;
    for ( E e : snapshot )
    {
        result = f.apply( result, e );
    }
    return result;
}

interface Function< T >
{
    T apply( T arg1, T arg2 );
}

一種naive嘗試

static <E> E reduce( List<E> list, Function<E> f, E initVal )
{
    E[] snapshot = list.toArray();
    E result = initVal;
    for ( E e : snapshot )
    {
        result = f.apply( result, e );
    }
    return result;
}

利用泛型修改

static< E > E reduce( List< E > list, Function< E > f, E initVal )
{
    List< E > snapshot;
    synchronized( list )
    {
        snapshot = new ArrayList< E >( list );
    }
    E result = initVal;
    for ( E e : snapshot )
    {
        result = f.apply( result, e );
    }
    return result;
}

第26條 優(yōu)先考慮泛型

泛型stack

public class Stack< E >
{
    private E[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    public Stack()
    {
        //elements = new E[ DEFAULT_INITIAL_CAPACITY ];
        //不能創(chuàng)建不可具體化的類型的數(shù)據(jù),兩種解決方法
        elements = ( E[] )new Object[ DEFAULT_INITIAL_CAPACITY ];
        //第二種方法,將elements域的類型從E[]改為Object[]
    }
    public void push( E e )
    {
        ensureCapacity();
        elements[ size++ ] = e;
    }
    public E pop()
    {
        if ( size == 0 )
        {
            throw new EmptyStackException();
        }
        E result = elements[ --size ];
        elements[ size ] = null;
        return result;
    }
    //...
}

使用泛型比使用需要在客戶端代碼中進行轉(zhuǎn)換的類型更加安全,也更加容易
在設(shè)計新類型的時候,要確保不要需要這種轉(zhuǎn)換就可以使用,也就是把類做成泛型的

第27條 優(yōu)先考慮泛型方法

靜態(tài)工具方法尤其適合于泛型化,Collections中所有的“算法”方法都泛型化了
利用有限制的通配符類型,可以是方法更加靈活
泛型方法的一個顯著特征是,無需明確指定類型參數(shù)的值,不像調(diào)用泛型構(gòu)造器的時候是必須指定的

編譯器通過檢查方法參數(shù)的類型來計算類型參數(shù)的值,這個過程稱作類型推導(dǎo)

在調(diào)用泛型構(gòu)造器的時候,要明確傳遞類型參數(shù)的值可能有點麻煩,類型參數(shù)出現(xiàn)在變量聲明的兩邊,代碼冗余
Map< String, List< String > > anagrams = new HashMap< String, List< String > >();
可以編寫一個泛型靜態(tài)工廠方法

public static< K, V > HashMap< K, V > newHashMap()
{
    return new HashMap< K, V >();
}
Map< String, List< String > > anagrams = newHashMap();

泛型單例工廠:創(chuàng)建不可變但又適合于許多不同類型的對象

public interface UnaryFunction< T >
{
    T apply( T arg );
}

private static UnaryFunction< Object > IDENTITY_FUNCTION =
 new UnaryFunction< Object >() { 
    public Object apply( Object arg ) { return arg; }
 };
@SuppressWarnings( "unchecked" )
public static< T > UnaryFunction< T > identityFunction()
{
    return ( UnaryFunction< T > ) IDENTITY_FUNCTION;
}

public static void main( String[] args )
{
    String[] strings = { "jute", "hemp", "nylon" };
    UnaryFunction< String > sameString = identityFunction();
    for ( String s : strings )
    {
        System.out.println( sameString.apply( s ) );
    }

    Number[] numbers = { 1, 2.0, 3L };
    UnaryFunction< Number > sameNumber = identityFunction();
    for ( Number n : numbers )
    {
        System.out.println( sameNumber.apply(n) );
    }
}

通過某個包含該類型參數(shù)本身的表達式來限制類型參數(shù)是允許的,這就是遞歸類型限制
遞歸類型限制最普遍的用途與Comparable接口有關(guān),定義類型的自然順序

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

//Using a recursive type bound to express mutual comparability
public static< T extends Comparable< T > > T max( List< T > list )
{
    Iterator< T > i = list.iterator();
    T result = i.next();
    while ( i.hasNext() )
    {
        T t = i.next();
        if ( t.compareTo( result ) > 0 )
        {
            result = t;
        }
    }
    return result;
}

第28條 利用有限制通配符來提升API的靈活性

參數(shù)化類型是不可變的,導(dǎo)致泛型間沒有子類關(guān)系,Java提供了一種特殊的參數(shù)化類型,稱作有限制的通配符類型來處理邏輯上可以存儲的泛型結(jié)構(gòu)

public class Stack< E >
{
    public Stack();
    public void push( E e );
    public E pop();
    public boolean isEmpty();

    public void pushAll( Iterable< ? extends E > src )
    {
        for ( E e : src )
        {
            push( e );
        }
    }
    
    public void popAll( Collection< ? super E > dst )
    {
        while ( !isEmpty() )
        {
            dst.add( pop() );
        }
    }
}

為了獲得最大限度的靈活性,要在表示生產(chǎn)者或消費者的輸入?yún)?shù)上使用通配符類型

PECS表示producer-extends, consumer-super

public static< E > Set< E > union( Set< ? extends E > s1, Set< ? extends E > s2 )

Set< Integer > integers = ...;
Set< Double > doubles = ...;
//Set< Number > numbers = union( integers, doubles );   //error
Set< Number > numbers = Union.<Number>union( integers, doubles );

擴展max方法

public static< T extends Comparable< T > > T max( List< T > list )

//modified
public static< T extends Comparable< ? super T > > T max( List< ? extends T > list )
{
    //Iterator< T > i = list.iterator(); //error
    Iterator< ? extends T > i = list.iterator();
    T result = i.next();
    while ( i.hasNext() )
    {
        T t = i.next();
        if ( t.compareTo(result) > 0 )
        {
            result = t;
        }
    }
    return result;
}
List< ScheduleFuture< ? > > scheduleFutures = ...;
public static void swap( List< ? > list, int i, int j )
{
    swapHelper( list, i, j );
}

private static< E > void swapHelper( List< E > list, int i, int j )
{
    list.set( i, list.set( j, list.get(i) ) );
}

在API中使用通配符類型需要技巧,但使API變得靈活得多,如果編寫的是將被廣泛使用的類庫,一定要是當(dāng)?shù)乩猛ㄅ浞愋?br> 基本原則:producer-extends, consumer-super
所有的comparable和comparator都是消費者

第29條 優(yōu)先考慮類型安全的異構(gòu)容器

異構(gòu):不像普通的容器,所有鍵都是不同類型的

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

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

集合API說明泛型的用法:限制容器只能由固定數(shù)目的類型參數(shù)。
可以通過將類型參數(shù)放在鍵上而不是容器上來避開這一限制
對于類型安全的異構(gòu)容器,可以用Class對象作為鍵,以這種方式使用的Class對象稱作類型令牌

枚舉和注解

方法

如何處理參數(shù)和返回值,如何設(shè)計方法簽名,如何為方法編寫文檔,在可用性、健壯性和靈活性上有進一步的提升

通用程序設(shè)計

討論局部變量的處理、控制結(jié)構(gòu)、類庫的使用、各種數(shù)據(jù)類型的用法,以及反射機制和本地方法的用法,并討論優(yōu)化和命名慣例

第45條 將局部變量的作用域最小化

將局部變量的作用域最小化,可以增加代碼的可讀性和可維護性,并降低出錯的可能性
要使局部變量的作用域最小化,最有效的方式就是在第一次使用它的地方聲明
局部變量的聲明應(yīng)該包含一個初始化表達式,例外情況是進行try-catch

如果變量被一個方法初始化,而該方法可能會拋出一個受檢的異常,該變量就必須在try塊的內(nèi)部被初始化,如果變量的值在try塊的外部被使用,則必須在try塊之前聲明

for循環(huán)優(yōu)先于while循環(huán),for循環(huán)更加簡短,且不易出錯
使方法小而集中

第46條 for-each循環(huán)優(yōu)先于傳統(tǒng)的for循環(huán)

for-each循環(huán)在簡潔性和預(yù)防Bug方面有著傳統(tǒng)的for循環(huán)無法比擬的優(yōu)勢,并且沒有性能損失,應(yīng)盡可能地使用
for-each循環(huán)不僅可以遍歷集合和數(shù)組,還可以遍歷任何實現(xiàn)Iterable接口的對象

public interface Iterable< E >
{
    //return an iterator over the elements in this iterable
    Iterator< E > iterator();
}

有三種常見的情況無法使用for-each循環(huán)

  • 過濾
    如果需要遍歷集合,刪除特定元素,需要使用顯式的迭代器,然后調(diào)用remove方法
  • 轉(zhuǎn)換
    如果需要遍歷列表或者數(shù)組,并取代其部分或者全部的元素值,需要列表迭代器或者數(shù)組索引,以設(shè)定元素的值
  • 平行迭代
    如果需要并行地遍歷多個集合,則需要顯示地控制迭代器或者索引變量,以便所有迭代器或者索引變量都可以得到同步前移

第47條 了解和使用類庫

第48條 如果需要精確的答案,請避免使用float和double

第49條 基本類型優(yōu)先于裝箱基本類型

第50條 如果其他類型更適合,則盡量避免使用字符串

第51條 當(dāng)心字符串連接的性能

第52條 通過接口引用對象

第53條 接口優(yōu)先于反射機制

第54條 謹慎地使用本地方法

第55條 謹慎地進行優(yōu)化

第56條 遵守普遍接受的命名慣例

異常

如何發(fā)揮異常的優(yōu)點,提高程序的可讀性、可靠性和可維護性,以及減少使用不當(dāng)所帶來的負面影響,并提供了異常使用的指導(dǎo)原則

第57條 只針對異常的情況才使用異常

并發(fā)

序列化

闡述序列化方面的技術(shù),討論序列化代理模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內(nèi)容

  • 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法,而不是構(gòu)造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法,并非Fa...
    孫小磊閱讀 2,015評論 0 3
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,726評論 18 399
  • 《Effective Java》筆記(上) 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法,而不是構(gòu)造...
    OCNYang閱讀 2,608評論 2 17
  • 1 在富山村姥姥家 她兩歲時,來到了富山村的姥姥家。 百米多高的山,腳底下流淌著蜿蜒入海的河流。百十來戶的小...
    普雅也是花閱讀 434評論 0 1