
public class Stack
{
private Object[] elements;
private int size = 0;
public Stack(int initialCapacity)
{
this.elements = new Object[initialCapacity];
}
public void push(Object e)
{
ensureCapacity();
elements[size++] = e;
}
public Object pop()
{
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity()
{
if (elements.length == size)
{
Object[] oldElements = elements;
elements = new Object[2*element.length+1];
System.arraycopy(oldElements,0,elements,0,size);
}
}
}
public Object pop()
{
if(size = = 0)
throw new EmptyStackException();
Object result = elements[--size];
elements.[size]=null;
return result;
}六,避免使用終結(finalizer)函數
原因:終結函數通常是不可預測的,一般情況下不要使用,使用的結果會(huì )帶來(lái)很多問(wèn)題,不穩定,性能差,移植問(wèn)題等等。
分析原因:
1,從一個(gè)對象不可到達到它的終結函數被執行,這段時(shí)間是任意的,不確定的,所以時(shí)間關(guān)鍵的系統不應該使用終結函數。
2,及時(shí)的執行終結函數是垃圾回收算法決定的,這種算法在不同的JVM實(shí)現中會(huì )大相徑庭,使用終結函數的結果就是導致移植性問(wèn)題
3,如果執行終結函數的線(xiàn)程一直低于當前的線(xiàn)程的優(yōu)先級,很可能造成占用大量?jì)却?,極端情況是出現OutOfMemoryError
4,JSL不保證終結函數一定被執行,所以不要依賴(lài)終結函數來(lái)更新關(guān)鍵性的永久狀態(tài),例如數據庫的永久鎖
5,不要相信System.gc() System.runFinalization這兩個(gè)函數,它們只能提高終結函數的執行機會(huì ),并不保證一定執行。唯一保證一定執行的是System.runFinalizersOnExit喝Runtime.runFinalizersONExit()但這兩個(gè)方法已經(jīng)被聲明不建議使用.
6,一個(gè)在終結函數中的一場(chǎng)不會(huì )打出任何信息
七:在改寫(xiě)equals方法的時(shí)候遵守通用約定
分析:
1,有些情況下不要隨意改寫(xiě)equals
(1),一個(gè)類(lèi)的每個(gè)實(shí)例本質(zhì)上是唯一的,例如Thread
(2),不管新一個(gè)類(lèi)是否提供了“邏輯相等”的測試功能,例如java.util.Random
(3),超類(lèi)已經(jīng)改寫(xiě)了equals,從超類(lèi)繼承過(guò)來(lái)的行為對于子類(lèi)也是適合的 例如Set從AbstractSet繼承了equals
(4),一個(gè)類(lèi)是私有的,或者是包級私有的,并且確定它的equals方法永遠不會(huì )被調用
2, 通用的約定
自反性: 對于任意的引用值x ,x.equals(x)一定為true
對稱(chēng)性: 對于任意的引用值x,y x.equals(y)返回true是 y.equals(x)也一定返回true
傳遞性:對于任意的引用值x,y,z 如果x.equals(y)返回true 并且y.equals(z)返回true 那么 x.equals(z)也一定是true
一致性:對于任意的x,y如果x,y沒(méi)有被更改,調用任意多次x.equals(y)返回的結果應該一樣。
非空性:對任意的引用x ,x.equals(null)一定返回false
3不要將equals聲明中的Object對象換成別的類(lèi)型
4,不要讓equals方法依賴(lài)不可靠資源
八:改寫(xiě)equals方法時(shí)總要改寫(xiě)hashCode
原因:來(lái)自java.lang.Object關(guān)于hashCode的規范
1,在一個(gè)應用執行期間,如果一個(gè)對象的equals方法比較所用到的信息沒(méi)有修改的話(huà),那么對該對象調用hashCode多次,比如如一的返回同一個(gè)數
2,如果兩個(gè)對象的equals方法返回true,那么分別調用hashCode方法返回的值應該相等
3,在兩個(gè)兌現的equals方法返回false時(shí),盡可能的讓hashCode方法返回的值不相等,提高散列表的性能
分析:如果改寫(xiě)了equals沒(méi)有改寫(xiě)hashCode在使用map等集合類(lèi)的時(shí)候會(huì )出現問(wèn)題。
九:盡可能的改寫(xiě)toString方法,并在顯示內容中盡可能的包括令人感興趣的信息。并且在注釋中表示出你的意圖。
十:謹慎的改寫(xiě)clone方法,改寫(xiě)前考慮淺拷貝和全拷貝
十一:考慮實(shí)現Comparable接口,如果你的對象要排序,那么記得實(shí)現這個(gè)方法
十二:使類(lèi)和成員的可訪(fǎng)問(wèn)能力最小化,
十三:支持非可變性
非可變性遵循以下的原則:
1,不提供任何改變對象的方法
2,保證沒(méi)有可被子類(lèi)改寫(xiě)的方法
3,保證所有的域都使final
4,使所有的域都成為私有的
5,保證任何可變組件互斥訪(fǎng)問(wèn)
6,非可變對象本質(zhì)是線(xiàn)程安全的,不需要同步
十四:復合優(yōu)于繼承(Think in java中有不少說(shuō)明)
十五:要們專(zhuān)門(mén)為繼承而設計,并給出文檔說(shuō)明,要么禁止繼承
十六:接口優(yōu)于抽象類(lèi)(參考一下GOF的設計模式)
十七:接口只是被定義類(lèi)型,不要試圖使用常量接口
十八:優(yōu)先考慮靜態(tài)成員類(lèi)
說(shuō)明:嵌套類(lèi)有四種
靜態(tài)成員類(lèi) , 非靜態(tài)成員類(lèi), 匿名類(lèi), 局部類(lèi) 除了第一種之外,其它三種都被稱(chēng)為內部類(lèi)
1,靜態(tài)成員類(lèi)是一種最簡(jiǎn)單的嵌套類(lèi),最好把它看成一個(gè)普通類(lèi),只是碰巧被聲明在另一個(gè)類(lèi)內部而已,
它可以訪(fǎng)問(wèn)外圍類(lèi)的所有成員,包括那些生民為私有的成員。靜態(tài)成員類(lèi)是外圍類(lèi)的一個(gè)靜態(tài)成員,也遵守同樣的可訪(fǎng)問(wèn)性規則,如果它被聲明為私有的,那么它只能在外圍類(lèi)內部可以訪(fǎng)問(wèn)。靜態(tài)成員類(lèi)的一個(gè)用法是公有的輔助類(lèi)。例如HashMap的 static class Entry
非靜態(tài)成員類(lèi)和靜態(tài)成員類(lèi)的區別主要是非靜態(tài)成員類(lèi)需要一個(gè)外圍類(lèi)實(shí)例的引用,如果你不需要訪(fǎng)問(wèn)外圍類(lèi)的實(shí)例的話(huà),記得使用靜態(tài)成員類(lèi)。
匿名類(lèi)被使用的相對多一些,但是大量的使用匿名類(lèi)會(huì )讓你的代碼比較亂,作過(guò)GUI開(kāi)發(fā)的人多會(huì )有所感觸。并且記住,盡可能的讓你的匿名類(lèi)短小。
局部類(lèi),局部類(lèi)的使用是最少的,很少會(huì )使用到這個(gè),如果用到記得使局部類(lèi)盡可能的短小
對于C語(yǔ)言用戶(hù)的部分
十九:用類(lèi)代替結構
二十:用類(lèi)層次代替聯(lián)合
二十一:用類(lèi)來(lái)代替enum,但是在jdk1.5的時(shí)候提供了enum的支持,有些東西不一樣了
二十二:用類(lèi)和接口代替函數指針
二十三、在函數的開(kāi)始檢查參數的有效性
如果函數對參數有要求,例如不接受Null ,不接受負數等等,應該盡可能在函數的最開(kāi)始給出校驗,如果發(fā)現錯誤拋出異常
二十四、在需要的時(shí)候使用保護性拷貝
1,假設類(lèi)的客戶(hù)會(huì )盡一切手段來(lái)破壞這個(gè)類(lèi)的約束條件,在這樣的前提下,你必須保護性的設計程序。
2,實(shí)例
import java.util.Date;

public final class Period
{
private final Date start;
private final Date end;
public Period(Date start,Date end)
{
if (start.compareTo(end)>0)
{
throw new IllegalArgumentException(start+"after"+end);
}
this.start = start;
this.end = end;
}
//
.other code
}
//這個(gè)函數看似沒(méi)有問(wèn)題,實(shí)際上存在著(zhù)漏洞,如下使用方法
Date start = new Date();
Date end = new Date();
Period p = new Period(start,end);
//如果加上這句,檢驗就失效了。
end.setYear(78);
//為了對應這個(gè)問(wèn)題,更改構造函數:

public Period(Date start,Date end)
{
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (start.compareTo(end)>0)
{
throw new IllegalArgumentException(start+"after"+end);
}
}注意,拷貝要在檢驗之前進(jìn)行
3,參數類(lèi)型可以被不可信任方子類(lèi)化的情形,清不要使用clone方法進(jìn)行參數的保護化拷貝
二十五、謹慎的設計方法的原型
1,謹慎的選擇方法的名字,一個(gè)好的方法名字可以讓人很快記住
2,不要過(guò)于追求提供便利的方法,如果方法太多會(huì )增加使用者的學(xué)習負擔,只有當一個(gè)操作被頻繁使用的時(shí)候再添加一個(gè)對應的方法。
3,避免太長(cháng)的參數列表,盡量讓你的參數不大于三個(gè)
4,對于參數類(lèi)型,優(yōu)先使用接口,而不是類(lèi)。
原因:如果使用接口,你可以隨意的替換實(shí)現,或者同時(shí)存在多個(gè)實(shí)現。
使用類(lèi)沒(méi)有這個(gè)優(yōu)勢。
5,謹慎的使用函數對象(一個(gè)類(lèi)中一堆靜態(tài)函數)
二十六、謹慎的使用重載
1,實(shí)例
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


public class CollectionClassifier
{
public static String classify(Set s)
{
return "Set";
}
public static String classify(List s)
{
return "List";
}
public static String classify(Collection s)
{
return "Unknow Collection";
}

public static void main(String[] args)
{
Collection[] tests = new Collection[]
{
new HashSet(),
new ArrayList(),
new HashMap().values()
};
for(int i=0;i<tests.length;i++)
{
System.out.println(classify(tests[i]));
}
}
}結果是打印出三個(gè)unknown
這個(gè)程序的行為是違反直覺(jué)的,對弈重載方法的選擇是靜態(tài)的,而對于被改寫(xiě)的方法的選擇是動(dòng)態(tài)的
(這個(gè)可以參考我的另一篇文章)
2,盡量不要使用兩個(gè)參數數目相同的重載方法
二十七、使用零長(cháng)度數組代替Null作為返回值
原因:返回Null會(huì )造成使用者每次使用的時(shí)候都要作一次判斷,但有人會(huì )說(shuō)返回一個(gè)零長(cháng)度數組會(huì )產(chǎn)生new的開(kāi)銷(xiāo),不如Null性能好。這個(gè)不是一定的,因為我們可以這樣來(lái)作
private final static Cheese[] NULL_CHESE_ARRAY = new Cheese[0];
每次需要的時(shí)候返回這個(gè)數組就好了。
二十八、為所有的導出Api元素編寫(xiě)文檔注釋
二十九、使一個(gè)局部變量的作用域最小化,最好的辦法使在第一次使用的時(shí)候聲明
1,幾乎每一個(gè)局部變量的聲明都應該包含一個(gè)初始化表達式,如果你還沒(méi)有足夠的信息來(lái)初始化那就推遲聲明。
2,for循環(huán)優(yōu)先于while循環(huán),見(jiàn)下邊的例子
// for循環(huán)
for(Iterator ie = list.iterator();ie.hasNext())
{
doSomething(ie.next());
}
// while循環(huán)
Iterator ie1 = list1.iterator();
while(ie1.hasNext())
{
doSomething(ie1.next());
}
Iterator ie2 = list2.iterator();
while(ie1.hasNext())
{ //bug
doSomething(ie2.next());
}這個(gè)問(wèn)題源于復制粘貼,在編碼的過(guò)程中復制粘貼幾乎是不可避免的,使用for循環(huán)當你出錯的時(shí)候可以在編譯器發(fā)生錯誤,而使用while則不會(huì )發(fā)現。盡早發(fā)現錯誤總是好的。
三十、了解和使用庫(產(chǎn)生隨機數)
詳細:如果你希望產(chǎn)生一個(gè)位于0-某個(gè)上界的隨機數,大多數的人的寫(xiě)法如下
static Random rnd = new Random();
static int random(int n)
{
return Math.abs(rnd.nextInt())%n;
}這個(gè)方法存在三個(gè)缺點(diǎn):
缺點(diǎn)一:
如果n是一個(gè)比較小的2的乘方 那么經(jīng)過(guò)一段相當短的周期后它產(chǎn)生的隨即數序列將會(huì )重復
缺點(diǎn)二:
如果n不是2的乘方,那么平均起來(lái)某些數比另外一些數出現的更為頻繁,如果n比較大則這個(gè)問(wèn)題更加顯著(zhù)如果產(chǎn)生100萬(wàn)范圍內的隨機數,你會(huì )發(fā)現數字幾乎全部在0-666 666 ,前2/3的數字
缺點(diǎn)三:
在有些情況下會(huì )災難性失敗,返回一個(gè)落在范圍之外的數字。原因是使用了Math.abs來(lái)得到一個(gè)非負數。
如果nextInt()返回 Integer.MIN_VALUE,那么abs后也會(huì )返回Integer.MIN_VALUE ,假設n不是2的乘方,那么取模操作符%將返回一個(gè)負數,這幾乎肯定造成你的程序失敗,而且這個(gè)失敗很難重現。
為了編寫(xiě)一個(gè)避免上邊三個(gè)缺點(diǎn)的random,你必須了解線(xiàn)性同于偽隨機發(fā)生器、數論、和2的求補運算知識。不過(guò)Jdk已經(jīng)實(shí)現了一個(gè)現成的可以使用,那就是Random.nextInt(int)
這一段很多比較簡(jiǎn)單,簡(jiǎn)單羅列一下,部分重要的做了解釋
三十一、如果要求精確的答案,盡量避免使用float 和double,這個(gè)可以參照我的一片文章
貨幣尤其不合適??梢允褂肂igDecimal代替
三十二、如果其它類(lèi)型更適合,盡量避免使用字符串
1,字符串不能替代其它的值類(lèi)型
2,字符串不適合代替枚舉類(lèi)型
3,字符串不適合代替聚集類(lèi)型
4,字符串也不是和代替能力表
因為有些時(shí)候,使用字符串會(huì )大大降低性能
三十三、了解字符串連接的性能
說(shuō)明:使用StringBuffer代替 +來(lái)連接字符串
三十四、通過(guò)接口來(lái)引用對象,這能讓你的程序更加靈活
三十五、接口優(yōu)先于反射。
使用反射會(huì )帶來(lái)很多問(wèn)題,例如:
1,不能編譯期發(fā)現錯誤
2,代碼混亂
3,調試困難
4,性能損失。
除非必須,否則不使用反射
三十六、謹慎的使用本地方法JNI
三十七、謹慎的進(jìn)行優(yōu)化,有三條優(yōu)化格言:
1,很多計算上的過(guò)失都被歸咎于效率原因(沒(méi)有獲得必要的效率),而不是其它的原因--甚至包括盲目的作傻事. ---William A.Wulf [Wulf72]
2,不要去計較一些小的效率上的得失,在97%的情況下,不成熟的優(yōu)化是一切問(wèn)題的根源。
------Donald E.Knuth[Knuth74]
3,在優(yōu)化方面要遵守兩個(gè)原則:
規則一:不要做優(yōu)化
規則二:還是不要做優(yōu)化--也就是說(shuō),在你還沒(méi)有絕對清晰的未優(yōu)化方案前,請不要優(yōu)化。
-----M.A.Jackson[Jackson75]
每次試圖做優(yōu)化之前和之后請對性能進(jìn)行測試
三十八:遵守普遍接受的命名規則
三十九:值針對不正常的條件才使用異常,也就是說(shuō)不要在正常的情況下使用異常來(lái)控制流程,活著(zhù)解決某些已知的問(wèn)題。因為會(huì )大量的損失性能
四十、對于可恢復的條件使用被檢查的異常,對于程序錯誤使用運行時(shí)異常
詳細:Java提供了三種可拋出結構,checked Exception, run-time exception , error
什么時(shí)候使用什么很容易讓人混淆,下邊是一個(gè)簡(jiǎn)單的區分原則
1,如果期望調用者能夠恢復,那么對于這樣的條件應該使用被檢查異常
2,你所實(shí)現的所有未檢查的拋出結構都是run time exception ,而不是Error
四十一:避免不必要的使用被檢查異常
四十二:盡可能的使用標準異常,例如IllegalArgumentException ,NullPointerException ,IndexOutOfBoundsException等等
四十三:拋出異常要適合于相應的抽象。
高層實(shí)現應該捕獲異常,同時(shí)拋出一個(gè)可以按照高層抽象解釋的異常(業(yè)務(wù)邏輯上符合高層邏輯),這種做法叫做異常轉譯
四十四:每個(gè)方法拋出的異常都應改有文檔
四十五:在細節消息中包含失?。东@信息
詳細:在異常字符串中應包含有用的信息,例如IndexOutOfBoundsException異常的細節消息應該包括下界、上界以及沒(méi)有落在其中的實(shí)際下標
聯(lián)系客服