目標(biāo):
? 學(xué)習(xí)靜態(tài)系列
? 了解好的軟件的三大特性
冰雹序列
? “冰雹序列”的定義:從正整數(shù)n開(kāi)始票摇,如果n是偶數(shù),則下一個(gè)數(shù)是n/2精置,否則下一個(gè)數(shù)是3n+1都弹,直到n等于1娇豫。
這里有幾個(gè)例子:
由于存在3n+1這種變化,所以序列元素的大小可能會(huì)忽高忽低——這也是“冰雹序列”名稱(chēng)的來(lái)歷畅厢,冰雹在落地前會(huì)在云層中忽上忽下。那么所有的序列都會(huì)最終“落地”變到1嗎氮昧?(這個(gè)猜想稱(chēng)為考拉茲猜想框杜。)
計(jì)算冰雹序列
下面的代碼用于打印冰雹序列:
在語(yǔ)法中Java和Python的區(qū)別:
? Java中表達(dá)式和語(yǔ)句的基本語(yǔ)義與Python非常相似:例如while,if行為相同袖肥。
? Java需要以分號(hào)結(jié)尾咪辱。額外的標(biāo)點(diǎn)符號(hào)看著很麻煩,實(shí)際上它也提供了更多組織代碼的自由——例如可以將語(yǔ)句分成多行椎组。
? Java在使用 if 和 while的時(shí)需要用括號(hào)括起來(lái)油狂。
? Java需要在塊周?chē)褂没ɡㄌ?hào)。即使Java不會(huì)對(duì)多余的空間給予任何關(guān)注寸癌,也應(yīng)始終縮進(jìn)該塊专筷。編程是一種交流形式,您不僅在與編譯器交流蒸苇,還與人類(lèi)交流磷蛹。
數(shù)據(jù)類(lèi)型
?? Python和Java最大的不同就是Java需要指定變量n的類(lèi)型:int類(lèi)型是一些值的集合,以及這些值對(duì)應(yīng)的操作溪烤。
例如下面這5種常用的原始類(lèi)型 :
int (例如5和-200這樣的整數(shù)味咳,但是其范圍有限制但在2^31之間,大概在±20億)
long (比int更大范圍的整數(shù)檬嘀,大概至2^63)
boolean(true和false)
double (浮點(diǎn)數(shù)槽驶,其表示的是實(shí)數(shù)的子集)
char (單個(gè)字符例如 ‘A’ 和 ‘$’)
Java也有對(duì)象類(lèi)型 ,例如:
String 表示一串連續(xù)的字符鸳兽。
BigInteger 表示任意大小的整數(shù)掂铐。
? ?? 從Java的傳統(tǒng)來(lái)說(shuō),原始類(lèi)型用小寫(xiě)字母贸铜,對(duì)象類(lèi)型的起始字母用大寫(xiě)堡纬。
? ? 操作符是一些能接受輸入并輸出結(jié)果的功能。他們的語(yǔ)法各有區(qū)別蒿秦,Java中常見(jiàn)的有下面這三種:
?前綴烤镐、中綴、后綴操作符. 例如棍鳖, a + b 調(diào)用這樣一種操作(映射) + : int × int → int( + 是這個(gè)操作符的名字, int × int 描述了這兩個(gè)輸入, 最后的 int 描述的了輸出)
? 一個(gè)對(duì)象的方法. 例如炮叶, bigint1.add(bigint2) 調(diào)用這樣一種操作(映射) add: BigInteger × BigInteger → BigInteger.
? 一個(gè)函數(shù). 例如: Math.sin(theta) 調(diào)用這樣一種操作(映射) sin: double → double. 注意碗旅, Math 不是一個(gè)對(duì)象,它是一個(gè)包含sin函數(shù)的類(lèi)镜悉。
? ? 有一些操作符可以對(duì)不同類(lèi)型的對(duì)象進(jìn)行操作祟辟,這時(shí)我們就稱(chēng)之為可重載 (overloaded),例如Java中的算術(shù)運(yùn)算符 +, -, *, / 都是可重載的侣肄。一些函數(shù)也是可重載的旧困。大多數(shù)編程語(yǔ)言都有不容程度的重載性。
靜態(tài)類(lèi)型
? ? Java是一種靜態(tài)類(lèi)型的語(yǔ)言稼锅。所有變量的類(lèi)型在編譯的時(shí)候就已經(jīng)知道了(程序還沒(méi)有運(yùn)行)吼具,所以編譯器也可以推測(cè)出每一個(gè)表達(dá)式的類(lèi)型。
例如矩距,如果a和b是int類(lèi)型的拗盒,那么編譯器就可以知道a+b的結(jié)果也是int類(lèi)型的。事實(shí)上锥债,Eclipse編譯環(huán)境在寫(xiě)代碼時(shí)如果出錯(cuò)就會(huì)發(fā)現(xiàn)陡蝇。
在 動(dòng)態(tài)類(lèi)型 語(yǔ)言中(例如Python)中,這種類(lèi)型檢查是發(fā)生在程序運(yùn)行時(shí)哮肚。
靜態(tài)類(lèi)型 是靜態(tài)檢查的其中一種——這意味著在編譯時(shí)檢查bug登夫。靜態(tài)檢查是為了避免bug的發(fā)生。其中靜態(tài)類(lèi)型阻止了一大部分和類(lèi)型相關(guān)的bug—確切點(diǎn)說(shuō)绽左,就是將操作符用到了不對(duì)應(yīng)的類(lèi)型對(duì)象上悼嫉。例如,如果你進(jìn)行下面這個(gè)操作:
那么靜態(tài)類(lèi)型檢查就會(huì)在你編輯代碼的時(shí)候發(fā)現(xiàn)這個(gè)bug拼窥,而不是等到你編譯后運(yùn)行程序的時(shí)候告知你戏蔑。
靜態(tài)檢查、動(dòng)態(tài)檢查鲁纠、無(wú)檢查
編程語(yǔ)言通常能提供以下三種自動(dòng)檢查的方法:
? 靜態(tài)檢查:bug在程序還沒(méi)有被執(zhí)行的時(shí)候被自動(dòng)地檢查出來(lái)
? 動(dòng)態(tài)檢查:bug在程序正在被執(zhí)行的時(shí)候被發(fā)現(xiàn)
? 無(wú)檢查: 編程語(yǔ)言本身不幫助你發(fā)現(xiàn)錯(cuò)誤总棵,你必須通過(guò)特定的條件(例如輸出的結(jié)果)檢查代碼的正確性。
? 相比較而言改含,靜態(tài)檢查好于動(dòng)態(tài)檢查好于不檢查情龄。
? ? 下面是一些各種類(lèi)型的檢查能檢查出來(lái)的錯(cuò)誤:
靜態(tài)檢查:
?語(yǔ)法錯(cuò)誤:例如多余的標(biāo)點(diǎn)符號(hào)或者錯(cuò)誤的關(guān)鍵詞。即使在動(dòng)態(tài)類(lèi)型的語(yǔ)言例如Python中也會(huì)做這種檢查:如果你有一個(gè)多余的縮進(jìn)捍壤,在運(yùn)行之前就能發(fā)現(xiàn)它骤视。
? 錯(cuò)誤的名字:例如Math.sine(2). (修正:應(yīng)該是 sin.)
? 錯(cuò)誤的參數(shù)個(gè)數(shù):例如 Math.sin(30, 20).
? 錯(cuò)誤的參數(shù)類(lèi)型: Math.sin(“30”).
? 錯(cuò)誤的返回類(lèi)型 :例如一個(gè)聲明返回int類(lèi)型函數(shù)return “30”;
動(dòng)態(tài)檢查:
? 非法的變量值:例如整型變量x、y鹃觉,表達(dá)式x/y 只有在運(yùn)行后y為0才會(huì)報(bào)錯(cuò)专酗,否則就是正確的。因此盗扇,在這個(gè)表達(dá)式中除以0不是一個(gè)靜態(tài)錯(cuò)誤而是一個(gè)動(dòng)態(tài)錯(cuò)誤祷肯。
? 無(wú)法表示的返回值:例如最后得到的返回值無(wú)法用聲明的類(lèi)型來(lái)表示沉填。
? 越界訪問(wèn):例如在一個(gè)字符串中使用一個(gè)負(fù)數(shù)索引。
? 使用一個(gè)null對(duì)象解引用:(null相當(dāng)于Python中的None)
? ? ? 靜態(tài)檢查傾向于類(lèi)型錯(cuò)誤 佑笋,即與特定的值無(wú)關(guān)的錯(cuò)誤翼闹。正如上面提到過(guò)的,一個(gè)類(lèi)型是一系列值的集合蒋纬,而靜態(tài)類(lèi)型就是保證變量的值在這個(gè)集合中猎荠,但是在運(yùn)行前我們可能不會(huì)知道這個(gè)值的結(jié)果到底是多少。所以如果一個(gè)錯(cuò)誤必須要特定的值來(lái)“觸發(fā)”(例如除零錯(cuò)誤和越界訪問(wèn))颠锉,編譯器是不會(huì)在編譯的時(shí)候報(bào)錯(cuò)的法牲。
? ? ? 與此相對(duì)的,動(dòng)態(tài)類(lèi)型檢查傾向于特定值才會(huì)觸發(fā)的錯(cuò)誤琼掠。
原始類(lèi)型并不是真正的數(shù)
? ? ? 在Java和許多其他語(yǔ)言中存在一個(gè)“陷阱”即原始數(shù)據(jù)類(lèi)型的對(duì)象在有些時(shí)候并不像真正的數(shù)字那樣得到應(yīng)有的輸出。結(jié)果就是本來(lái)應(yīng)該被動(dòng)態(tài)檢查發(fā)現(xiàn)的錯(cuò)誤沒(méi)有報(bào)錯(cuò)停撞。例如:
? 整數(shù)的除法:5/2并不會(huì)返回一個(gè)小數(shù)瓷蛙,而是一個(gè)去掉小數(shù)部分的整數(shù)對(duì)象,因?yàn)槌ú僮鞣麑?duì)兩個(gè)整數(shù)對(duì)象運(yùn)算后的結(jié)果還是整數(shù)戈毒,而整數(shù)對(duì)象是無(wú)法表示5/2的精確值的(而我們期望它會(huì)是一個(gè)動(dòng)態(tài)檢查能發(fā)現(xiàn)的錯(cuò)誤)艰猬。
? 整型溢出: int 和 long類(lèi)型的值的集合是一個(gè)有限集合——它們有最大的值和最小的值,當(dāng)運(yùn)算的結(jié)果過(guò)大或者過(guò)小的時(shí)候我們就很可能得到一個(gè)在合法范圍內(nèi)的錯(cuò)誤值埋市。
? 浮點(diǎn)類(lèi)型中的特殊值:在浮點(diǎn)類(lèi)型例如double中有一些不是數(shù)的特殊值:NaN ( “Not a Number”), POSITIVE_INFINITY (正無(wú)窮), and NEGATIVE_INFINITY (負(fù)無(wú)窮).當(dāng)你對(duì)浮點(diǎn)數(shù)進(jìn)行運(yùn)算的時(shí)候可能就會(huì)得到這些特殊值(例如除零或者對(duì)一個(gè)負(fù)數(shù)開(kāi)更號(hào))冠桃,如果你拿著這些特殊值繼續(xù)做運(yùn)算,那你可能就會(huì)得到一個(gè)意想不到結(jié)果(譯者注:例如拿NaN和別的數(shù)進(jìn)行比較操作永遠(yuǎn)是False) 道宅。
閱讀小練習(xí)
讓我們嘗試一些錯(cuò)誤代碼的示例食听,看看它們?cè)贘ava中的行為。這些錯(cuò)誤是靜態(tài)污茵,動(dòng)態(tài)還是根本不捕獲的樱报?
數(shù)組和聚集類(lèi)型
? ? 讓我們把“冰雹序列”的結(jié)果存儲(chǔ)在數(shù)據(jù)結(jié)構(gòu)中而不僅僅是輸出。Java中有兩種常用的線性存儲(chǔ)結(jié)構(gòu):數(shù)組和列表襟士。
? ? 數(shù)組是一連串類(lèi)型相同的元素組成的結(jié)構(gòu)盗飒,而且它的長(zhǎng)度是固定的(元素個(gè)數(shù)固定)。例如陋桂,我們聲明一個(gè)整型數(shù)組變量:
對(duì)于數(shù)組逆趣,常用的操作符有下:
? 索引其中的一個(gè)元素: a[2]
? 賦予一個(gè)元素特定的值: a[2]=0
? 求數(shù)組長(zhǎng)度: a.length (和 String.length() 的區(qū)別—— a.length 不是一個(gè)類(lèi)內(nèi)方法調(diào)用,所以不能在它后面寫(xiě)上括號(hào)和參數(shù))
下面是我們利用數(shù)組寫(xiě)的第一個(gè)求“冰雹序列”的代碼章喉,它存在一些bug:
? ? ? 相信很快你就能發(fā)現(xiàn)錯(cuò)誤:數(shù)組的長(zhǎng)度是100(100稱(chēng)為幻數(shù))汗贫?如果我們嘗試的n的冰雹序列長(zhǎng)到大于100的話就無(wú)法使用這個(gè)數(shù)組身坐。假如我們犯了錯(cuò)誤,Java是否能夠靜態(tài)地落包、動(dòng)態(tài)地檢查出這個(gè)錯(cuò)誤或者根本不檢查部蛇?偶然地,像這樣一個(gè)固定長(zhǎng)度的數(shù)組的溢出在像C或者C++這樣不太安全的語(yǔ)言中是非常常見(jiàn)的咐蝇,而且被稱(chēng)為緩沖區(qū)溢出涯鲁。這種溢出是大量網(wǎng)絡(luò)安全漏洞和網(wǎng)絡(luò)爬蟲(chóng)的罪魁禍?zhǔn)住?/p>
集合類(lèi)型
? ? ? 解決方法是使用List類(lèi)型。List類(lèi)型而不是固定長(zhǎng)度的數(shù)組有序。我們可以這樣聲明列表:
常用的操作符有下:
? 索引一個(gè)元素: list.get(2)
? 賦予一個(gè)元素特定的值: list.set(2, 0)
? 求列表的長(zhǎng)度: list.size()
? ? ? 這里要注意List是一個(gè)接口抹腿,這種類(lèi)型的對(duì)象無(wú)法直接用new來(lái)構(gòu)造,必須用能夠?qū)崿F(xiàn)List要求滿足的操作符的方法來(lái)構(gòu)造旭寿。我們會(huì)在后來(lái)講抽象數(shù)據(jù)類(lèi)型的時(shí)候具體將價(jià)格這個(gè)警绩。ArrayList是一個(gè)實(shí)類(lèi)型的類(lèi),它提供了List操作符的具體實(shí)現(xiàn)盅称。ArrayList不是唯一的實(shí)現(xiàn)方法肩祥,但是是最常用的一個(gè)。在Java API的文檔里找到很多這方面的信息缩膝。另外要注意的是混狠,我們寫(xiě)的是List< Integer >而不是List< int>,不幸的是我們不能直接模仿int[]那樣寫(xiě)List< int>。List只知道如何處理對(duì)象類(lèi)型疾层,而不知道原始類(lèi)型将饺。在Java中,每個(gè)原語(yǔ)類(lèi)型(它們是用小寫(xiě)寫(xiě)的痛黎,通常是縮寫(xiě)的予弧,比如int)都有一個(gè)等效的對(duì)象類(lèi)型(這個(gè)對(duì)象類(lèi)型是大寫(xiě)的,并且拼寫(xiě)完整舅逸,就像Integer一樣)桌肴。Java要求我們?cè)趨?shù)化帶有尖括號(hào)的類(lèi)型時(shí)使用這些對(duì)象類(lèi)型等價(jià)物。但是在其他情況下琉历,Java會(huì)自動(dòng)在int和Integer之間進(jìn)行轉(zhuǎn)換坠七,因此我們可以編寫(xiě)Integer i=5,而不會(huì)出現(xiàn)任何類(lèi)型錯(cuò)誤旗笔。
下面是用列表編寫(xiě)的“冰雹序列”的實(shí)現(xiàn):
這樣實(shí)現(xiàn)除了看起來(lái)簡(jiǎn)潔而且更加安全彪置,因?yàn)榱斜頃?huì)自動(dòng)擴(kuò)充它自己以滿足新添加的元素(當(dāng)然,直到你的內(nèi)存不夠用為止)
迭代
? ? ? 對(duì)于在一個(gè)序列結(jié)構(gòu)(例如列表和數(shù)組)遍歷元素蝇恶,Java和Python的寫(xiě)法差不多:
? ? ? Math.max() 是一個(gè)Java API提供的方便的函數(shù)拳魁。
方法
? ? ? 在Java中,聲明通常必須在一個(gè)方法中撮弧,而每個(gè)方法都要在一個(gè)類(lèi)型中潘懊,所以寫(xiě)“冰雹序列”程序最簡(jiǎn)單可以這么寫(xiě):
public姚糊、private、static
? ? ? ? public意味著任何在你程序中的代碼都可以訪問(wèn)這個(gè)類(lèi)或者方法授舟。其他的類(lèi)型修飾符救恨,例如private ,是用來(lái)確保程序的安全性的——它保證了可變類(lèi)型不會(huì)被別處的代碼所修改释树。我們會(huì)在后面的課程中詳細(xì)提到肠槽。
? ? ?? ? static意味這這個(gè)方法沒(méi)有self這個(gè)參數(shù)——Java會(huì)隱含的實(shí)現(xiàn)它,所以你不會(huì)看到這個(gè)參數(shù)奢啥。靜態(tài)的方法不能通過(guò)對(duì)象來(lái)調(diào)用秸仙,例如List add() 方法 或者 String length()方法,它們要求先有一個(gè)對(duì)象桩盲。靜態(tài)方法的正確調(diào)用應(yīng)該使用類(lèi)來(lái)索引寂纪,例如:
? ? ? 另外,記得在定義的方法前面寫(xiě)上注釋赌结。這些注釋?xiě)?yīng)該描述了這個(gè)方法的功能弊攘,輸入輸出/返回,以及注意事項(xiàng)姑曙。記住注釋不要寫(xiě)的啰嗦,而是應(yīng)該直切要點(diǎn)迈倍,簡(jiǎn)潔明了伤靠。例如在上面的代碼中,n是一個(gè)整型的變量啼染,這個(gè)在聲明的時(shí)候int已經(jīng)體現(xiàn)出來(lái)了宴合,就不需要進(jìn)行注釋。但是如果我們?cè)O(shè)想的本意是n不能為負(fù)數(shù)迹鹅,而這個(gè)編譯器(聲明)是不能檢查和體現(xiàn)出來(lái)的卦洽,我們就應(yīng)該注釋出來(lái),方便閱讀理解和修改斜棚。
? ? ? 這些東西我們會(huì)在后面的課程中詳細(xì)介紹阀蒂,但是你現(xiàn)在就要開(kāi)始試著正確使用他們。
變化的值vs.可被賦值的改變
? ? ? 我們會(huì)介紹“快照?qǐng)D”(snapshot diagrams)弟蚀,以此來(lái)辨別修改一個(gè)變量和修改一個(gè)值的區(qū)別蚤霞。當(dāng)你給一個(gè)變量賦值的時(shí)候,你實(shí)際上是在改變這個(gè)變量指向的對(duì)象(值也不一樣)义钉。
? ? ? 而當(dāng)你對(duì)一個(gè)可變的值進(jìn)行賦值操作的時(shí)候——例如數(shù)組或者列表——你實(shí)際上是在改變對(duì)象本身的內(nèi)容昧绣。
? ? ? 變化是“邪惡”的,好的程序員會(huì)避免可改變的東西捶闸,因?yàn)檫@些改變可能是意料之外的夜畴。
? ? ? 不變性(Immutability)是我們這門(mén)課程的一個(gè)重要設(shè)計(jì)原則拖刃。不變類(lèi)型是指那些這種類(lèi)型的對(duì)象一旦創(chuàng)建其內(nèi)容就不能被更改的類(lèi)型(至少外部看起來(lái)是這樣,我們?cè)诤竺娴牡恼n程中會(huì)說(shuō)一些替代方案)贪绘。思考一下在上面的代碼中哪一些類(lèi)型是可更改類(lèi)型兑牡,哪一些不是?(例如int就是不變的兔簇,List就是可變的发绢,給int類(lèi)型的對(duì)象賦值就會(huì)讓它指向一個(gè)新的對(duì)象)
? ? ?? Java也給我們提供了不變的索引:只要變量被初始化后就不能再次被賦值了——只要在聲明的時(shí)候加上final :
? ? ? 如果編譯器發(fā)現(xiàn)你的final變量不僅僅是在初始化的時(shí)候被“賦值”,那么它就會(huì)報(bào)錯(cuò)垄琐。換句話說(shuō)边酒,final會(huì)提供不變索引的靜態(tài)檢查。
? ? ? 正確的使用final是一個(gè)好習(xí)慣狸窘,就好像類(lèi)型聲明一樣墩朦,這不僅會(huì)讓編譯器幫助你做靜態(tài)檢查,同時(shí)別人讀起來(lái)也會(huì)更順利一些翻擒。
? ? ? 在hailstoneSequence方法中有兩個(gè)變量n和list氓涣,我們可以將它們聲明為final嗎?請(qǐng)說(shuō)明理由陋气。(譯者注:n不行劳吠,list可以。因?yàn)槲覀冃枰淖僴指向的對(duì)象巩趁,而List對(duì)象本身是可以更改的痒玩,我們也不需要改變list對(duì)應(yīng)的對(duì)象)
記錄設(shè)想
? ? ? 在文檔中寫(xiě)下變量的類(lèi)型記錄了一個(gè)關(guān)于它的設(shè)想, 例如這個(gè)變量總是指向一個(gè)整型. 在編譯的時(shí)候 Java 就會(huì)檢查這個(gè)設(shè)想, 并且保證在你的代碼中沒(méi)有任何一處違背這個(gè)設(shè)想。
? ? ? 而使用 final 關(guān)鍵字去定義一個(gè)變量也是一種記錄設(shè)想, 要求這個(gè)變量在其被賦值之后就永遠(yuǎn)不會(huì)再被修改, Java 也會(huì)對(duì)其進(jìn)行靜態(tài)地檢查议慰。
? ? ? 不幸的是 Java 并不會(huì)自動(dòng)檢查所有設(shè)想蠢古,例如:n 必須為正數(shù)。
? ? ? 為什么我們需要寫(xiě)下我們的設(shè)想呢? 因?yàn)榫幊叹褪遣粩嗟脑O(shè)想, 如果我們不寫(xiě)下他們, 就可能會(huì)遺忘掉他們, 而且如果以后別人想要閱讀或者修改我們的軟件, 他們就會(huì)很難理解代碼, 不得不去猜測(cè)(譯者注: 變量的含義/函數(shù)的描述/返回值描述等等)
所以在編程的時(shí)候我們必須朝著如下兩個(gè)目標(biāo)努力:
? 與計(jì)算機(jī)交流. 首先說(shuō)服編譯器你的程序語(yǔ)法正確并且類(lèi)型正確, 然后保證邏輯正確, 這樣就可以讓它在運(yùn)行的時(shí)候給我們正確的結(jié)果别凹。
? 與其他人交流. 盡可能使你的程序易于理解, 所以當(dāng)有人想要在將來(lái)某些時(shí)候修正它, 改進(jìn)它或者對(duì)其進(jìn)行適配的時(shí)候, 他們可以很方便地實(shí)現(xiàn)自己的想法草讶。
黑客派(Hacking)vs.工程派(Engineering)
? ? 黑客派 的編程風(fēng)格可以理解為“放飛自我并且樂(lè)觀的”(貶義):
? 缺點(diǎn): 在已經(jīng)編寫(xiě)大量代碼以后才測(cè)試它們
? 缺點(diǎn): 將所有的細(xì)節(jié)都放在腦子里, 以為自己可以永遠(yuǎn)記住所有的代碼, 而不是將它們編寫(xiě)在代碼中
? 缺點(diǎn): 認(rèn)為 BUG 都不存在或者它們都非常容易發(fā)現(xiàn)和被修復(fù).
而 工程派 對(duì)應(yīng)的做法是(褒義):
? 優(yōu)點(diǎn): 一次只寫(xiě)一點(diǎn)點(diǎn), 一邊寫(xiě)一邊測(cè)試. 在將來(lái)的課程中, 我們將會(huì)探討"測(cè)試優(yōu)先編程" (test-first programming)
? 優(yōu)點(diǎn): 記錄代碼的設(shè)想、意圖 (document the assumptions that your code depends on)
? 優(yōu)點(diǎn): 靜態(tài)代碼檢查將會(huì)保護(hù)你的代碼不淪為“愚蠢的代碼”
本課程的目標(biāo)
本門(mén)課程的主要目標(biāo)為學(xué)習(xí)如何生產(chǎn)具有如下屬性的軟件:
? 遠(yuǎn)離bug 正確性 (現(xiàn)在看起來(lái)是正確的), 防御性 (將來(lái)也是正確的)
? 易讀性 我們不得不和以后有可能需要理解和修改代碼的程序員進(jìn)行交流 (修改 BUG 或者添加新的功能), 那個(gè)將來(lái)的程序員或許會(huì)是幾個(gè)月或者幾年以后的你, 如果你不進(jìn)行交流, 那么到了那個(gè)時(shí)候, 你將會(huì)驚訝于你居然忘記了這么多, 并且這將會(huì)極大地幫助未來(lái)的你有一個(gè)良好的設(shè)計(jì)炉菲。
? 可改動(dòng)性. 軟件總是在更新迭代的, 一些好的設(shè)計(jì)可以讓這個(gè)過(guò)程變得非常容易, 但是也有一些設(shè)計(jì)將會(huì)需要讓開(kāi)發(fā)者扔掉或者重構(gòu)大量的代碼堕战。
? ? ? 軟件還有其他重要屬性(如性能,可用性颁督,安全性)践啄,它們可能會(huì)與這三個(gè)屬性相抵觸。但是沉御,這些是我們?cè)诒敬握n程中關(guān)注的三大巨頭屿讽,并且軟件開(kāi)發(fā)人員通常將其放在構(gòu)建軟件的實(shí)踐中。值得考慮的是我們?cè)诒菊n程中學(xué)習(xí)的每種語(yǔ)言功能,每種編程實(shí)踐伐谈,每種設(shè)計(jì)模式烂完,并了解它們與三巨頭的關(guān)系。
總結(jié)
我們今天主要介紹的思想為靜態(tài)代碼檢查, 下面是該思想和我們課程目標(biāo)的關(guān)系:
? 幫助我們遠(yuǎn)離bug? 靜態(tài)代碼檢查可以通過(guò)捕捉類(lèi)型錯(cuò)誤等其他BUG幫助我們?cè)谶\(yùn)行代碼之前就發(fā)現(xiàn)它們
? 易讀性? 它可以幫助我們理解, 因?yàn)樗械念?lèi)型在代碼中被明確定義 (譯者注: 相比于 Python/PHP 這類(lèi)動(dòng)態(tài)變量類(lèi)型的語(yǔ)言)
? 可改動(dòng)性? 靜態(tài)代碼檢查可以在你在修改你的代碼的時(shí)候定位出也需要被修改的地方, 例如: 當(dāng)你改變一個(gè)變量的類(lèi)型或者名稱(chēng)的時(shí)候, 編譯器立即就會(huì)在所有使用到這個(gè)變量的地方顯示錯(cuò)誤, 提示你也需要更新它們诵棵。