Java程序語言和環(huán)境的設(shè)計目的是為了解決現(xiàn)代程序設(shè)計上的問題。它最初是一項較大規(guī)模『消費者電子產(chǎn)品先進軟件發(fā)展專案』的一部份,該專案目的是為了發(fā)展小型、可靠、可移植、分布式、實時的嵌入式系統(tǒng)。我們在專案一開始原決定采用C++,不過碰到一些問題。最初,那只是編譯器技術(shù)方面的問題,可是后來我們遭遇更多困難,且發(fā)現(xiàn)最好的解決方式就是更換編程語言。
(1) Java
Java是一種簡單、面向?qū)ο蟆⒎植际健⒔忉尅⒔?壯、安全、結(jié)構(gòu)中立、可移植、高效能、多線程、動態(tài)的語言。描述系統(tǒng)特性的方法之一就是把一堆高效能的術(shù) 語擺在一起。一如上述,我們也使用一組標準的術(shù)語來描述Java。以下我們將解釋這些術(shù)語在 Java上代表的意義,以及我們試圖解決的問題是哪些。『首先,我們要介紹阿基米得公司(Archimedes Inc.);它是我們?yōu)榱朔奖阏f明Java功能而虛構(gòu)的一家軟件公司。阿基米得從事有關(guān)基本物理教學 用途的軟件開發(fā),其軟體設(shè)計目的是要與使用者產(chǎn)生交互關(guān)系,不僅提供一如傳統(tǒng)教科書的文字 與插圖,而且也包括一組軟件實驗工作室,供使用者進行實驗和模擬真實世界的物理行為。例 如,它最基本的一項實驗允許學生組成杠桿和滑輪,然后觀察它們的物理作用。在以下的論述 中,我們將借由這些實驗和阿基米得設(shè)計師的旁白,以說明Java語言概念。』
(2) 簡單
我們想建立一種真正簡單的系統(tǒng),它允許使用者 輕易地設(shè)計程序而不需要像現(xiàn)今的標準程序一樣接受深奧的訓練。目前大多數(shù)程序設(shè)計師都采用 C語言,而面向?qū)ο蟪绦蛟O(shè)計師則大多使用C++。因此,即使我們發(fā)現(xiàn)C++并非適合我們,我 們在Java的設(shè)計上還是盡可能讓它與C++相近,以確保系統(tǒng)更容易被理解。Java刪除許多極少被使用、不容易理解和令人混淆 的C++功能,這些功能在我們的使用經(jīng)驗中只能帶來麻煩而非效益。刪除的功能主要包括運算符 重載(operator overloading)、多重繼承(inheritance)以及廣泛的自動強迫同型(automatic coercions); 重載是指以一個辨識元參照多重項目,Java語言也提供重載函數(shù),不過它重載的對象是方法(method)而非變量或運算符。我們增加自動內(nèi)存垃圾收集(auto garbage collection) 功能,因此簡化Java程序工作,不過同時也讓系統(tǒng)變得稍復(fù)雜一些。儲存管理(storage management)是使C與C++ 應(yīng)用程序變得復(fù)雜的常見的一項原因,即關(guān)于內(nèi)存的分配與釋放。Java語言的自動垃圾收集功能(周期性地 釋放未被使用的內(nèi)存)不僅簡化了程序設(shè)計工作,而且能大幅度減少小錯誤(bugs)數(shù)量。『阿基米得設(shè)計師們原想花時間思考杠桿與滑輪原 理,但是后來卻發(fā)現(xiàn)大部份時間都浪費在世俗的程序工作上。事實上,他們的核心專業(yè)知識在于 教學而非程序設(shè)計。這些程序工作中最復(fù)雜的部份就是從他們那二萬行程序碼中,找出那些地方 浪費了內(nèi)存。』 確保Java『簡單化』的另一特性在于『小而單 純』。Java的目標之一是要協(xié)助開發(fā)能獨立地在小型機器上順利執(zhí)行的軟件。Java的基本解釋器 (interpreter)和對象類(class)支援約占4OKB,而若增加基本的標準程序庫和線程(thread)支援 (它 實際上是一整自含式的微核心) 需增加175K。體積小對于在嵌入式的系統(tǒng)中的使用是很重要的,并因 此Java可以通過網(wǎng)絡(luò)輕易下載。
(3) 面向?qū)ο?/P>
這是在業(yè)界被過度渲染的術(shù)語之一。不過,面向 對象是一項很強的設(shè)計功能,因為它促成明確的接口定義,并允許發(fā)展者建立可重復(fù)使用的『軟 件IC』。簡單的說,面向?qū)ο笤O(shè)計是一種以數(shù)據(jù)(對象) 及其接口為重心的程序設(shè)計技術(shù)。我們以木匠比例;一位『面向?qū)ο蟆荒窘匙钪匾暤氖撬胫谱鞯囊巫樱浯尾攀撬脕碇谱鲆巫拥墓ぞ摺O喾吹模晃弧悍敲嫦驅(qū)ο蟆荒窘呈前阎饕男乃挤旁诠ぞ呱稀C嫦驅(qū)ο笤O(shè)計也是定義程序模塊如何『即插即用』的機制。Java的面向?qū)ο笤O(shè)施實質(zhì)上就是C++,并包含 Objective C的一些延伸,以提供更有動態(tài)的解決方案。『阿基米得設(shè)計師在他們的模擬式中設(shè)計許多 東西,包括繩子與橡皮筋等。他們初期的C版本產(chǎn)品是一個非常龐大的系統(tǒng),因為他們必須個別地設(shè)計軟件來描述繩子和橡皮筋。當以面向?qū)ο蠓绞街匦略O(shè)計應(yīng)用程序時,他們發(fā)現(xiàn)可以定義一個基本對象來代表繩子與橡皮筋兩者之間的共同部份,然后繩子與橡皮筋即定義成基本對象類型的子對象類。其后,當他們有需要增加鏈結(jié)時就能非常輕易地建立起來,因為他們可以將這些鏈結(jié)建立在先前已設(shè)計妥當?shù)膶ο笾希恍枰麄€重新設(shè)計新對象模擬。』
(4) 分布式
Java擁有廣泛的例程庫(routine library)能輕易地處 理TCP/IP協(xié)議,例如HTTP與FTP等。這使得在Java中比在C或C++中更容易創(chuàng)建網(wǎng)絡(luò)連接。Java應(yīng)用程 序可以借由URL在通過網(wǎng)絡(luò)開啟和存取對象,就如同存取一個本地(local)文件系統(tǒng)一樣簡單。 『阿基米得設(shè)計師最初把他們的程序建立在CD-ROM上。不過他們想把一些交互式教學游戲程序 概念放在下一個產(chǎn)品內(nèi)。例如,他們想讓不同電腦上的學生一起建立一臺模擬機器供大家學習。 不過,他們評估過的所有網(wǎng)絡(luò)系統(tǒng)都過于復(fù)雜且要求深奧的軟件工程專業(yè)能力。于是,他們只好 放棄這個概念。』
(5) 健壯
Java的目標是要協(xié)助發(fā)展者建立各方面都必須 可靠的程序。Java強調(diào)在設(shè)計初期即檢查可能存在的問題,其后則執(zhí)行動態(tài)(runtime)檢查,并排除 容易出現(xiàn)錯誤的條件。強類型(strongly typed)語言例如C++的優(yōu)點是允許 在編譯時進行深入的檢查,以便提前發(fā)現(xiàn)錯誤。不幸的是,C++承襲了C在編譯檢查(compile-time checking)時的一些漏洞;C的檢查較為松散,特別是在方法/過程聲明方面。我們在Java中要求 聲明,但并不支持C風格的隱性聲明。鏈結(jié)器(linker)了解類型系統(tǒng)并重復(fù)執(zhí)行許多 已由編譯器完成的類型檢查,以避免出現(xiàn)版本不匹配問題。Java與C/C++之間最大的不同點之一在于Java擁有 一種指針(pointer)模型,能排除發(fā)生內(nèi)存被覆蓋和毀損數(shù)據(jù)之可能性。Java不采用指針算 術(shù)法,而是提供真正的陣列。這允許程序執(zhí)行下標檢查;再者,它也不可能發(fā)生借由對象 類型轉(zhuǎn)換將一個任意整數(shù)轉(zhuǎn)成指針的情形。『阿基米得設(shè)計師的應(yīng)用程序在C執(zhí)行時基本上相 當快速。不過他們的軟件開發(fā)日程一再落后,因為總有清理不完的小錯誤逃過他們的檢查。他們 面對許多麻煩,包括內(nèi)存分配失敗、版本不一致、接口不匹配等。C讓他們能在程序碼中增加一些 巧妙的設(shè)計,然而卻必須因此付出時間代價以求確保程序品質(zhì)。由于錯誤清不干凈,因此他們的軟件推出第一版之后就得忙著寫修補程序。』Java雖不能排除品質(zhì)確認問題,不過它讓這道程 序變得簡單許多。許多動態(tài)語言例如Lisp、TCL與Smalltalk等,通 常被用來制作原型程序(prototyping)。它們在這方面成功的理由是因為它們都非常健壯(robust),讓程序設(shè)計師不再怕處理內(nèi)存問題,因為他們不須擔心內(nèi)存失敗。Java即擁有這項特性。Java 程序員相對地可以不害怕處理內(nèi)存問題,因為在Java中不存在指針,Java程序不可能意外覆蓋一 片內(nèi)存緩沖區(qū)的末尾。Java程序也不可能非法訪問內(nèi)存,但這些在C或C++中都有可能發(fā)生。動態(tài)語言適合開發(fā)原型程序發(fā)展的理由之一,就 是它們不會在發(fā)展初期就要求你擬定明確的決策。Java剛好相反,它強迫你明確地做選擇。這些選擇伴隨許多輔助:你可以編寫方法調(diào)用(method invocations),而如果你有某些錯誤的地 方,你將會在編譯時獲得通知,而不須擔心方法調(diào)用上的錯誤。
(6) 安全
Java設(shè)計目的是要供使用于網(wǎng)絡(luò)/分布式運算環(huán) 境。為此,Java非常強調(diào)安全性,以確保建立無病毒且不會被侵入的系統(tǒng)。Java的驗證技術(shù)是以公鑰 (public-key)加密法為基礎(chǔ)。『健壯性』與『安全』之間存在一種很強的相互 作用關(guān)系。例如,指針語意的改變,讓應(yīng)用程式不可能偽造對象結(jié)構(gòu)存取權(quán),或存取它們在對 象中沒有存取權(quán)的私有數(shù)據(jù)。這等于關(guān)起大門,阻絕大多數(shù)病毒活動。『有人為阿基米得的PC版軟件寫了一個有趣的修 補程序,然后將它貼到一個主要的電子布告欄上面流通。由于這支修補程序很容易取得而且又能 為系統(tǒng)增加一些有趣的功能,于是很多人都下載使用。這個程序未經(jīng)阿基米得設(shè)計師檢驗過,不 過它用起來似乎還不錯。直到四月一日那天,好幾千名使用者發(fā)現(xiàn)他們小孩使用的教學軟件突然 跳出幾張不堪入目的圖片。不用多加說明,阿基米得設(shè)計師們雖不用為這個意外負起責任,但是 他們還是必須設(shè)法控制那個修補程序造成的破壞。』
(7) 結(jié)構(gòu)中立性
Java的設(shè)計目標是要支援網(wǎng)絡(luò)應(yīng)用程序。一般而 言,網(wǎng)絡(luò)是由許多不同的系統(tǒng)構(gòu)成,包括各種CPU與操作系統(tǒng)結(jié)構(gòu)。為了讓Java應(yīng)用程序能夠 在網(wǎng)絡(luò)上任何地方執(zhí)行,其編譯器會產(chǎn)出一種具備結(jié)構(gòu)中立性(architecture neutral)的目標文件格 式。編譯后的程序碼可以在提供Java runtime系統(tǒng)的多種不同處理器上面執(zhí)行。這不僅對網(wǎng)絡(luò)應(yīng)用很有幫助,而且也很適合單一 系統(tǒng)軟件流通。在目前的個人電腦市場上,應(yīng)用程序發(fā)展者必須為他們的程式分別編寫IBM PC 和Apple Macintosh相容版本。現(xiàn)在,PC市場正(透過Windows/NT)分散成許多CPU結(jié)構(gòu),而 Apple則從68000轉(zhuǎn)向PowerPC,這些事實使得我們幾乎不可能設(shè)計出能在所有平臺上執(zhí)行的軟體。Java允許同一版本的應(yīng)用程序在所有平臺執(zhí)行。Java編譯器是借由產(chǎn)出與某一電腦結(jié)構(gòu)無關(guān)的字節(jié)代碼指令,以達到上述功能。它們能輕易地在任何機器上解釋,并且動態(tài)地轉(zhuǎn)換成原生模式 的機器碼。『阿基米得是一家小公司。他們從設(shè)計PC軟件起 家,因為那是最大的市場。經(jīng)過一段時日之后,他們成長至規(guī)模夠大的公司,因此有能力為 Macintosh平臺開發(fā)軟件,不過那確實是一項很大的工程,而且投資報酬并非真的很好。他們無力 負擔將軟件移植到PowerPC Macintosh或MIPS。NT機器所涉及的人力與成本。當?shù)谝徊ǔ绷鱽砼R 時,他們未能捉住機會,而競爭者即趁虛而入。』
(8) 可移植性
結(jié)構(gòu)中立性是確保程序可移植的最重要部份,不 過除此之外還有很多條件必須配合。和C與C++不同的是,Java規(guī)范中并無任何『結(jié)構(gòu)相依性』的陳述存在。它指定基本數(shù)據(jù)類型的大小,以及其算術(shù)運算元之執(zhí)行行為。例如,"int" 代表一 個有符號的二進制補碼32比特整數(shù),而"float"代表一個32比特IEEE 754浮點數(shù)。這些選擇在 今天的環(huán)境很適用,因為幾乎所有CPU都具備這些特性。程序庫屬于系統(tǒng)的一部份,它定義了一些可移植 的程序接口。例如,它包括一個抽象的Windows類,并且提供了類在Unix、Windows和 Macintosh平臺上的實現(xiàn)。Java系統(tǒng)本身具備相當好的移植性。編譯器以Java 寫成,而runtime程序使用ANSI C,并有一個實質(zhì)上與POSIX相容的移植疆界(portability boundary)。"
(9) 解釋
Java解釋器(interpreter)可以直接在任何已移植解 釋器的機器上解釋、執(zhí)行Java字節(jié)代碼,不需存儲。再者,由于其鏈結(jié)比較傾向于逐步增量與輕量過程, 因此發(fā)展程序更快、更精密。由于編譯期間的信息屬于字節(jié)代碼資料流的一部 份,因此可以在運行期間攜帶更多的信息。這正是鏈結(jié)器類型檢查的基礎(chǔ),它也讓程序更容易執(zhí)行除錯。『阿基米得設(shè)計師花很多時間等候程序編譯和鏈 結(jié)。他們也花很多時間追蹤許多無法感測到的錯誤,因為有些更改過的原始檔案未能完成編譯程 序 (雖然他們使用了make公用程序) 而導致版本不一致。同時還要追蹤一些在程序中許多聲明不 一致的程序。于是,他們的軟件發(fā)展日程又延誘
幾個月。』
(10) 高效能
雖然解釋過的字節(jié)代碼性能已相當不錯,不過有 些情形下還是要求程序達到更高執(zhí)行效能。字節(jié)代碼可以動態(tài)地(runtime)為執(zhí)行應(yīng)用程序的 特定CPU解釋成機器碼。這對于習慣使用一般編譯器與動態(tài)載入器(loader)的設(shè)計者而言,有點類似將最終的機器碼產(chǎn)生器放到動態(tài)載入器之內(nèi)。字節(jié)代碼格式在設(shè)計上即顧及機器碼的產(chǎn)生,因 此實際的機器碼產(chǎn)生程序相當簡單。產(chǎn)出的機器碼是有效的,編譯器自動分配寄存器,而 在產(chǎn)出字節(jié)代碼時也會進行一些優(yōu)化。我們以解釋碼在一臺Sun Microsystem SPARC Station 10上執(zhí)行時,達到每秒三十萬個方法調(diào)用(method calls)速率。字節(jié)代碼轉(zhuǎn)換 至機器碼的速度性能,幾乎和原生模式的C或C++沒有兩樣。『當阿基米得公司剛成立時,他們 用Smalltalk設(shè)計原型程序。這協(xié)助該公司獲得投資者贊助,不過那并沒有真正的幫助他們生產(chǎn)產(chǎn) 品:為了讓他們的模擬程序跑得夠快并符合精簡系統(tǒng)要求,他們只好用C重新設(shè)計。』
(11) 多線程
在真實世界中,許多事情同時發(fā)生在我們身邊。 多線程(multithreading)是一種應(yīng)用程序設(shè)計法。不幸的是,要設(shè)計一個一次同時處理許多事 件的程序,比設(shè)計傳統(tǒng)單一線程C與C++程序來得復(fù)雜許多。Java擁有一組復(fù)雜的同步化基本單元,它們 是以廣泛使用的C.A.R. Hoare監(jiān)視器與條件變量圖為基礎(chǔ)。將這些概念溶合到語言之后,它們即變得 很容易使用且更為健壯。這項溶合方式大部份來自Xerox的Cedar/Mesa系統(tǒng)。多線程的其他效益包括更好的交互式回應(yīng)能力 與實時執(zhí)行行為。然而這會受到底層平臺的限制:獨立執(zhí)行的Java運行環(huán)境有著很好的實時執(zhí)行行 為,而若在其他系統(tǒng)例如Unix、Windows、Macintosh或Windows NT等之上執(zhí)行則由于底層 平臺的原因?qū)崟r反應(yīng)性將會受到影響。『阿基米得的模擬程序一次同時執(zhí)行許多項實 驗:拉動繩子、轉(zhuǎn)動輪子和 「 桿,同時還追蹤使用者輸入。由于他們必須將這些模擬全部設(shè)計在一個單一線程的程序格式內(nèi),因此所有同時發(fā)生的動作 (不論彼此之問是否有關(guān)系) 必須用人工方式予以混合。使用一個事件回路可以讓情況更清楚一些,不過那仍舊是一團混亂,系統(tǒng)變得很脆弱而難以理解。他們從整個網(wǎng)絡(luò)擷取數(shù)據(jù),不過最初他們只能一次抓一個區(qū)塊,這種串行的網(wǎng)絡(luò)通訊非常緩慢。當他們轉(zhuǎn)移到多線程模式之后,網(wǎng)絡(luò)通訊問題即迎刃而解。』
(13) 動態(tài)
從許多方面而言,Java是一種比C或C++更具動態(tài) 特性的語言。它在設(shè)計上強調(diào)為進化中的運算環(huán)境提供支援。例如,C++在生產(chǎn)環(huán)境中的主要問題之一在于該 程序碼在一般設(shè)計上引起的負作用。如果甲公司設(shè)計一個對象類程序庫,而乙公司采購后它并 放到自己的產(chǎn)品中使用,然后如甲公司更改其程式庫并提供新的版本,那么乙公司將幾乎可以確定必須重新編譯和重新流通他們的新軟件。當最終用戶分別向甲乙兩家公司購買軟件 (例如甲 是作業(yè)系統(tǒng)廠商,而乙是應(yīng)用程序廠商) ,那么問題就產(chǎn)生了。例如,如果甲公司為其程序庫供應(yīng)一種升級版 本,那么乙公司的所有軟件都將必須修改。雖然C++可以避免此一問題,不過那極為困難,而且它也意味著不能直接使用該語言的任何面向?qū)ο蠊δ堋!喊⒒椎貌捎?DPC公司提供的面向?qū)ο髨D形程 式庫建立他們的產(chǎn)品。3DPC后來推出新
版本的 圖形程序庫,并且有數(shù)家電腦制造商將它搭配在 新機器上出貨。采購這些新機器的阿基米得客戶很失望地發(fā)現(xiàn)他們的舊軟件不能繼續(xù)使用。 (在 真實世界中,這種情形只發(fā)生在Unix系統(tǒng)上。在PC領(lǐng)域里,3DPC將永遠不會推出這樣的程序庫,因為他們更改和使用C++面向?qū)ο蠊δ艿哪芰κ艿綐O大的限制。 )』
Java是在稍后的階段為模塊與模塊之間建立這些 連接,因此完全避免了這些問題,并能更直接地運用面向?qū)ο笤O(shè)計體系。程序庫可以自由地增加 新方法和實例(instance)變量,而不會對它們的用戶產(chǎn)生任何效應(yīng)。Java能了解由Objective C引用過來的接口概念。 簡單的說,接口就是規(guī)范一組與對象相對應(yīng)的方法,但對象如何實現(xiàn)這些方法則留待解決。一個 類實現(xiàn)一個接口是要提供這個接口所包含的所有方法的實現(xiàn)。以此相反,派生子類則從父類繼承 了一組方法以及它們的實現(xiàn)。一個Java類可以實行多個接口,但只能從一個父類繼承。接口告訴連接對象它可以做什么而非怎么做,使得它在代碼上更具有靈活性和可復(fù)用性。對象類有一種運行(runtime)表示法:它有一種類稱 為Class,其內(nèi)容包括runtime class定義。在C或C++程序中,如果你有一個指針指向一個對 向,但你不知道該對象的類型為何,那么你將沒有辦法找出它。然而,在Java中要根據(jù)runtime類 型信息尋找是很直截了當?shù)摹R驗椋赾ompile-time和run-time時都會檢查數(shù)據(jù)類型轉(zhuǎn)換,所以 你在Java中可以信任這種轉(zhuǎn)換。另一方面,C與C++的編譯器則只是相信你已做了正確的處理。此外,它也可能從一個包含名稱的字串查找一個 對象類別定義。這意味著你可以演算一個數(shù)據(jù)類型名稱,然后輕易地以動態(tài)方式鏈結(jié)到執(zhí)行系統(tǒng)。『阿基米得為擴充營收源,希望讓他們的產(chǎn)品能 利用新的 入式模塊來擴充系統(tǒng)。這種擴充以前在PC上是可能做到的,只是很少實現(xiàn)。他們必 須增加一些新程序設(shè)計員,因為那些工作很復(fù)雜,而且也增加許多除錯問題。』
(14)總結(jié)
Java語言提供一種強有力的工具支援程序設(shè)計員。 Java讓程序設(shè)計變得更容易,因為它屬于面向?qū)ο笳Z言,而且提供了自動的內(nèi)存垃圾收集功 能。再者,由于Java碼具備結(jié)構(gòu)中立性,因此其應(yīng)用程序成為非同質(zhì)性運算環(huán)境 (例如Internet) 的理想方案。
posted on 2005-02-04 11:40
jacky 閱讀(326)
評論(0) 編輯 收藏