《Java白皮書1996自譯》02:簡單和友好的

詹姆斯·高斯林

第二章 簡單和友好的

在設計上達到完美,不是無法再增加什麼,而是不需要再刪減任何東西。

安東尼·聖艾修伯裏(法國作家)

羅伯特·海萊因在他的科幻小說《滾石》中評論到:

每一項技術都要經歷三個階段:首先是一個極其簡單、令人不滿意的小玩意;第二,是一組極爲複雜的小工具,其設計目的是克服原來的缺點,從而通過極其複雜的折衷辦法取得某種令人滿意的性能;第三,最後一個合理的設計。

海萊因的評論可以很好地描述許多編程語言的演變。Java爲編程語言的發展提供了一種新的觀點——創建一種小型和簡單的語言,這種語言仍然足夠全面,可以處理各種各樣的軟件應用程序開發。雖然Java在表面上類似於C和c++,但Java之所以簡單,是因爲它從前輩那裏系統地刪除了一些特性。本章討論了Java的兩個主要設計特性,即簡單(從刪除特性開始)和熟悉(因爲它看起來像C和c++)。下一章將更詳細地討論Java的面向對象特性。在這一章的末尾,你會發現關於在Java進化過程中從C和c++中刪除的特性的討論。

設計目標

簡單是Java最重要的設計目標之一。簡單性和從它的C和c++祖先那裏移除許多不確定的“特性”使Java相對較小,並減少了程序員在生成可靠應用程序方面的負擔。爲此,Java設計團隊檢查了“現代”C和c++語言的許多方面,從而確定在現代面向對象編程環境中可以消除的特性。

另一個主要的設計目標是,對於個人計算機和工作站領域中的大多數程序員來說,Java看起來很熟悉,其中有很大一部分系統程序員和應用程序程序員熟悉C和c++。因此,Java“看起來像”c++。熟悉C、Objective C、c++、Eiffel、Ada和相關語言的程序員應該會發現他們的Java語言學習曲線相當短——大約幾周。

爲了說明Java簡單和熟悉的方面,我們遵循了一系列優秀編程書籍的傳統,向您展示HelloWorld程序。它是你能寫的最簡單的程序,它能做一些事情。這是用Java實現的HelloWorld。

image

這個例子聲明瞭一個名爲HelloWorld的類。類將在面向對象編程的下一章中討論,但通常我們假設讀者熟悉對象技術並瞭解類、對象、實例變量和方法的基礎知識。

在HelloWorld類中,我們聲明一個名爲main()的方法,該方法反過來包含一個方法調用,以在標準輸出上顯示字符串“Hello world!”,打印“Hello world!”的語句通過調用out對象的println方法來實現這一點。out對象是系統類中的一個類變量,它對文件執行輸出操作。這就是HelloWorld的所有含義。

2.1、Java編程語言的主要特性

Java™編程語言在某種程度上遵循c++,這使得許多程序員對它很熟悉。本節描述Java編程語言的基本特性,並指出該語言與它的祖先C和c++的區別。

2.1.1、基本數據類型

除了這裏討論的基本數據類型之外,Java編程語言中的所有內容都是對象。如果需要,甚至可以將原始數據類型封裝到庫提供的對象中。Java編程語言在其基本數據類型集中緊跟C和c++,但有幾個小的例外。基本數據類型只有三種,即數字類型、字符類型和布爾類型。

數字數據類型

整數數字類型是8位字節、16位短字節、32位int和64位長字節。Java中的8位字節數據類型已經取代了舊的C和c++字符數據類型。Java對char數據類型進行了不同的解釋,如下所述。

在Java中,整數數據類型沒有無符號類型說明符。

真正的數字類型是32位浮點型和64位雙精度浮點型。真正的數字類型及其算術運算由IEEE 754規範定義。浮點文字值,如23.79,默認情況下被認爲是雙精度的;如果您希望將其賦值給float變量,則必須顯式地將其強制轉換爲float。

字符數據類型

Java語言字符數據與傳統的c語言不同。Java的char數據類型定義了16位Unicode字符。Unicode字符是無符號的16位值,它們定義0到65,535範圍內的字符代碼。如果你寫一個聲明,如

image

您將得到一個初始化爲字符q的Unicode值的Unicode(16位無符號值)類型。通過對Java語言應用程序的字符數據類型採用Unicode字符集標準,Java語言應用程序可以適應國際化和本地化,極大地擴展了全球應用程序的市場。

布爾數據類型

Java添加了一個布爾數據類型作爲基本類型,默認了現有的C和c++編程實踐,開發人員在其中爲真和假、YES和NO或類似的結構定義關鍵字。Java布爾變量假定值爲真或假。Java編程語言boolean是一種獨特的數據類型;與常見的C實踐不同,Java編程語言布爾類型不能轉換爲任何數字類型。

2.1.2、算術和關係運算符

所有熟悉的C和c++操作符都適用。Java編程語言沒有無符號數據類型,因此在該語言中添加了>>>操作符,以指示無符號(邏輯)右移。Java還使用+運算符進行字符串連接;下面關於字符串的討論將討論串聯。

2.1.3、數組

與C和c++相比,Java編程語言數組是一流的語言對象。Java編程語言中的數組是具有運行時表示的真實對象。可以聲明和分配任何類型的數組,還可以分配數組的數組以獲得多維數組。

你用這樣聲明一個數組,比如說,點(你在別處聲明的類):

image

這段代碼聲明myPoints是一個未初始化的點數組。此時,爲myPoints分配的唯一存儲是一個引用句柄。在未來的某個時間,您必須分配所需的存儲量,例如:

image

將包含十個引用的數組分配給初始化爲null引用的點。注意,數組的這種分配實際上並不爲您分配Point類的任何對象;你還需要分配點對象,像這樣:

image

對myPoints元素的訪問可以通過普通的c風格的索引來執行,但是所有的數組訪問都被檢查,以確保它們的索引在數組的範圍內。如果索引超出數組的範圍,將生成異常。

數組的長度存儲在特定數組的length實例變量myPoints中。length包含myPoints中元素的數量。例如,代碼片段:

image

將值10賦給howMany變量。

C數組的內存指針的概念元素,和,任意指針運算導致不可靠的C代碼不再你能離開一個數組的末尾,可能破壞記憶和導致了著名的“delayed-crash”綜合症,出現一個內存訪問違反今天表現後幾小時或幾天。程序員可以確信,Java中的數組檢查將導致更健壯和可靠的代碼。

2.1.4、字符串

字符串是Java編程語言對象,而不是c語言中的僞字符數組。實際上有兩種字符串對象:string類用於只讀(不可變)對象。StringBuffer類用於您希望修改的字符串對象(可變字符串對象)。

雖然字符串是Java編程語言對象,但是Java編譯器遵循C語言的傳統,提供了C程序員使用C風格的字符串所享有的語法便利,也就是說,Java編譯器理解用雙引號括起來的字符字符串將被實例化爲字符串對象。因此,聲明:

image

在後臺實例化String類的對象,並使用包含Unicode字符表示“Hello world!”的字符串字符串初始化它。

Java技術擴展了+運算符的含義,表示字符串連接。因此,你可以寫這樣的語句:

image

這個代碼片段將字符串“There are”與數字值num轉換爲字符串的結果連接起來,並將其與字符串“文件中的字符”連接起來。然後在標準輸出上打印這些連接的結果。

String對象提供了一個length()訪問器方法來獲取字符串中的字符數。

2.1.5、多級中斷

Java編程語言沒有goto語句。要中斷或繼續多重嵌套循環或開關結構,您可以將標籤放在循環和開關結構上,然後斷開或繼續按標籤命名的塊。下面是來自Java編程語言內置字符串類的一小段代碼:

image

continue測試語句位於嵌套在另一個for循環中的for循環中。通過引用標籤測試,continue語句將控制傳遞給外部for語句。在傳統C語言中,continue語句只能繼續立即包圍的塊;要繼續或退出外部塊,程序員傳統上要麼使用輔助布爾變量,其唯一目的是確定外部塊是繼續還是退出;或者,程序員(mis)使用goto語句退出嵌套塊。Java編程語言中標記塊的使用大大簡化了編程工作並大大減少了維護。

標記塊的概念可以追溯到20世紀70年代中期,但在現代編程語言中並沒有得到很大的普及。Perl是另一種實現標記塊概念的現代編程語言。Perl的下一個標籤和最後一個標籤等價於Java中的continue label和break label語句。

2.1.6、內存管理和垃圾收集

C和c++程序員現在已經習慣了顯式管理內存的問題:分配內存、釋放內存以及跟蹤什麼內存可以在什麼時候釋放。顯式內存管理已被證明是bug、崩潰、內存泄漏和性能低下的有效來源。

Java技術完全消除了程序員的內存管理負載。不存在c風格的指針、指針算術、malloc和free。自動垃圾收集是Java及其運行時系統不可或缺的一部分。雖然Java技術提供了一個新的操作符來爲對象分配內存,但是沒有顯式的空閒函數。一旦分配了對象,運行時系統就會跟蹤對象的狀態,並在對象不再使用時自動回收內存,從而釋放內存供將來使用。

Java技術的內存管理模型基於對象和對對象的引用。Java技術沒有指針。相反,對已分配存儲的所有引用(實際上意味着對對象的所有引用)都是通過符號“句柄”進行的。Java技術內存管理器跟蹤對對象的引用。當對象沒有更多引用時,該對象是垃圾收集的候選對象。

Java技術的內存分配模型和自動垃圾收集使您的編程任務更容易,消除了整個類的bug,並且通常比通過顯式內存管理獲得的性能更好。下面是一個代碼片段,它說明了什麼時候發生垃圾收集:

image

變量dest在執行reverseIt方法時用作臨時對象引用。當dest超出作用域(reverseIt方法返回)時,對該對象的引用已經消失,然後它就成爲垃圾收集的候選對象。

2.1.7、後臺垃圾收集器

Java技術垃圾收集器通過在與HotJava TM瀏覽器等軟件應用程序交互時利用用戶行爲的本質來實現高性能。典型交互應用程序的典型用戶有許多自然的停頓,在這些停頓中,他們在思考面前的場景或思考下一步要做什麼。Java運行時系統利用這些空閒時間,並在沒有其他線程競爭CPU週期時以低優先級線程運行垃圾收集器。垃圾收集器收集並壓縮未使用的內存,增加了在大量交互使用期間需要足夠的內存資源的可能性。

使用線程運行垃圾收集器只是從Java技術的集成多線程功能中獲得的協同作用的衆多示例之一,否則一個棘手的問題將以簡單而優雅的方式得到解決。

2.1.8、集成的線程同步

Java技術支持多線程,在語言(語法)級別和通過其運行時系統和線程對象的支持。雖然其他系統提供了多線程的工具(通常通過“輕量級進程”庫),但是在語言本身中構建多線程支持爲程序員提供了一種更簡單、更強大的工具,可以輕鬆創建線程安全的多線程類。多線程將在第7章中更詳細地討論。

2.2、從C和c++中刪除的特性

本章的前一部分集中討論了Java的主要特性。本節討論在Java發展過程中從C和c++中刪除的特性。

第一步是從C和c++中消除冗餘。在許多方面,C語言演變成一組重疊的特性,提供了太多的方式來表達相同的內容,而在許多情況下卻沒有提供所需的特性。在試圖添加“C中的類”時,c++只是增加了更多的冗餘,同時保留了C的許多固有問題。

2.2.1、不再需要類型定義、定義或預處理器

用Java編寫的源代碼很簡單。沒有預處理器,沒有#define和相關功能,沒有typedef,沒有這些特性,不再需要頭文件。Java語言源文件提供其他類及其方法的聲明,而不是頭文件。

C和c++的一個主要問題是您需要理解另一個程序員代碼的上下文量:您必須閱讀所有相關的頭文件、所有相關的#定義和所有相關的類型定義,然後才能開始分析程序。從本質上說,使用#define和typedefs編程會導致每個程序員都發明一種新的編程語言,除了它的創建者之外,任何人都無法理解這種語言,從而破壞了良好編程實踐的目標。

在Java中,您可以通過使用常量來獲得#define的效果。通過聲明類可以獲得typedef的效果——畢竟,類有效地聲明瞭一個新類型。您不需要頭文件,因爲Java編譯器將類定義編譯成二進制形式,該形式通過鏈接時間保留所有類型信息。

通過刪除所有這些行李,Java變得非常無上下文。程序員可以閱讀和理解代碼,更重要的是,修改和重用代碼更快更容易。

2.2.2、不再需要結構或聯合體

Java沒有複雜數據類型的結構或聯合體。當你有類的時候,你不需要結構和聯合;您可以通過使用適當的實例變量聲明一個類來實現相同的效果。

下面的代碼片段聲明瞭一個名爲Point的類。

image

下面的代碼片段聲明瞭一個名爲Rectangle的類,它使用Point類的對象作爲實例變量。

image

在C語言中,您將這些類定義爲結構。在Java中,只需聲明類。您可以隨意將實例變量設置爲私有或公共,這取決於您希望向其他對象隱藏實現細節的程度。

2.2.3、不再需要枚舉

Java沒有枚舉類型。通過聲明一個類,您可以獲得與enum類似的東西,該類存在的惟一理由是保存常量。你可以像這樣使用這個功能:

image

你現在可以引用,比如說,South常量使用direction。South這個符號。

以這種方式使用類來包含常量比C的枚舉類型有一個主要優點。在C(和c++)中,枚舉中定義的名稱必須是惟一的:如果您有一個名爲HotColors的枚舉,其中包含紅色和黃色的名稱,則不能在任何其他枚舉中使用這些名稱。例如,您不能定義另一個名爲TrafficLightColors的枚舉也包含紅色和黃色。

使用Java中的類到容器常量技術,您可以在不同的類中使用相同的名稱,因爲這些名稱由包含的類的名稱限定。從上面的例子中,您可能希望創建另一個名爲CompassRose的類:

image

沒有歧義,因爲包含類的名稱充當常量的限定符。在第二個示例中,您將使用CompassRose符號。西北地區訪問相應值。Java有效地爲您提供了限定枚舉的概念,所有這些概念都在現有的類機制中。

2.2.4、不再需要函數

Java沒有函數。面向對象編程取代了函數式和過程式編程風格。混合這兩種風格只會導致混淆並稀釋面嚮對象語言的純粹性。使用函數可以做的任何事情都可以通過定義類併爲該類創建方法來完成。從上面考慮Point類。我們添加了一些公共方法來設置和訪問實例變量:

image

如果x和y實例變量是該類私有的,則訪問它們的唯一方法是通過該類的公共方法。下面是如何從Point類的對象(比如矩形類的對象)中使用Point類的對象:

image

這並不是說函數和過程本身就是錯誤的。但是對於給定的類和方法,我們現在只剩下一種表示給定任務的方法了。通過消除函數,程序員的工作大大簡化了:只處理類及其方法。

2.2.5、不再需要多繼承

多重繼承——以及它產生的所有問題——從Java中被丟棄。多繼承的理想特性由接口提供——在概念上類似於目標C協議。

接口不是類的定義。相反,它是一個或多個類將實現的一組方法的定義。接口的一個重要問題是它們只聲明方法和常量。變量不能在接口中定義。

2.2.6、不再需要goto語句

Java沒有goto語句。研究表明,後藤被(mis)使用的頻率比“因爲它在那裏”要高。消除goto會簡化語言——例如,對於將goto放到for語句中間的效果沒有規則。對大約10萬行C代碼的研究表明,大約90%的goto語句純粹是爲了獲得跳出嵌套循環的效果。如上所述,多級中斷和繼續刪除goto語句的大部分需要。

2.2.7、不再需要操作符重載

程序員無法提供重載標準算術運算符的方法。同樣,通過聲明類、適當的實例變量和操作這些變量的適當方法,也可以輕鬆地實現操作符重載的效果。消除操作符重載會大大簡化代碼。

2.2.8、不再需要自動強制

Java禁止C和c++風格的自動強制。如果希望將一種類型的數據元素強制轉換爲會導致精度損失的數據類型,則必須通過強制轉換顯式地這樣做。考慮以下代碼片段:

image

將myFloat賦值給myInt會導致編譯器錯誤,指示可能丟失精度,並且必須使用顯式轉換。因此,您應該將代碼片段重寫爲:

image

2.2.9、不再需要指針

大多數研究都認爲指針是程序員向代碼中注入bug的主要特性之一。由於結構已經消失,數組和字符串是對象,因此不再需要指向這些結構的指針。因此,Java沒有指針數據類型。任何需要C語言中的數組、結構和指針的任務都可以通過聲明對象和對象數組更容易、更可靠地執行。您可以通過數組的算術索引訪問數組,而不是對數組指針進行復雜的指針操作。Java運行時系統檢查所有數組索引,以確保索引在數組的範圍內。

由於指針不正確,您不再有懸空指針和內存垃圾,因爲Java中沒有指針。

2.3、概要

綜上所述,Java是:

簡單——要完成工作,您需要理解的語言結構的數量是最少的。

友好的——Java看起來像C和c++,但卻忽略了這些語言的巨大複雜性。

既然您已經瞭解了Java是如何通過刪除其前輩的特性來簡化的,那麼請閱讀下一章來討論Java的面向對象特性。


Familiar /fə’mɪlɪə/ adj. 熟悉的;常見的;親近的 n. 常客;密友

注:

我個人認爲,對於c或者c++程序員來說,Java借鑑過來的那些特性確實不陌生。但是,更重要的是,Java拋棄了c和c++的那些令人費解和頭疼的特性,這大大降低了學習Java的難度和開發效率。所以,我覺得Familiar翻譯爲”友好“更爲貼切。


好好學習,天天向上!繼續下一章…


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章