事實上, Java 原本就是從 C++衍生出來的。 java
C++和 Java 之間仍存在一些顯著的差別。能夠這樣說,這些差別表明着技術的極大進步。一旦咱們弄清楚了這些差別,就會理解爲何說 Java 是一種優秀的程序設計語言。這裏將引導你們認識用於區分Java 和 C++的一些重要特徵。程序員
(1) 最大的障礙在於速度:解釋過的 Java 要比 C 的執行速度慢上約 20 倍。不管什麼都不能阻止 Java 語言進行編譯。出現了一些準實時編譯器,它們能顯著加快速度。固然,咱們徹底有理由認爲會出現適用於更多流行平臺的純固有編譯器,但倘若沒有那些編譯器,因爲速度的限制,必須有些問題是Java 不能解決的。數據庫
(2) 和 C++同樣, Java 也提供了兩種類型的註釋。編程
(3) 全部東西都必須置入一個類。不存在全局函數或者全局數據。若是想得到與全局函數等價的功能,可考慮將 static 方法和 static 數據置入一個類裏。注意沒有象結構、枚舉或者聯合這一類的東西,一切只有「類」( Class)!數組
(4) 全部方法都是在類的主體定義的。因此用 C++的眼光看,彷佛全部函數都已嵌入,但實情並不是如此。安全
(5) 在 Java 中,類定義採起幾乎和 C++同樣的形式。但沒有標誌結束的分號。沒有 class foo 這種形式的類聲明,只有類定義。
網絡
1 class aType() 2 void aMethod() {/* 方法主體 */} 3 }
(6) Java 中沒有做用域範圍運算符「 ::」。 Java 利用點號作全部的事情,但能夠不用考慮它,由於只能在一個類裏定義元素。即便那些方法定義,也必須在一個類的內部,因此根本沒有必要指定做用域的範圍。咱們注意到的一項差別是對 static 方法的調用:使用 ClassName.methodName()。 除此之外, package(包)的名字是用點號創建的,並能用 import 關鍵字實現 C++的「 #include」的一部分功能。例以下面這個語句:
多線程
1 import java.awt.*;
( #include 並不直接映射成 import,但在使用時有相似的感受。)框架
(7) 與 C++相似, Java 含有一系列「主類型」( Primitive type),以實現更有效率的訪問。在 Java 中,這些類型包括 boolean, char, byte , short, int, long, float 以及 double。全部主類型的大小都是固有的,且與具體的機器無關(考慮到移植的問題)。這確定會對性能形成必定的影響,具體取決於不一樣的機器。對類型的檢查和要求在 Java 裏變得更苛刻。例如:
■條件表達式只能是 boolean(布爾)類型,不可以使用整數。
■必須使用象 X+Y 這樣的一個表達式的結果;不能僅僅用「 X+Y」來實現「反作用」。分佈式
(8) char(字符)類型使用國際通用的 16 位 Unicode 字符集,因此能自動錶達大多數國家的字符。
(9) 靜態引用的字串會自動轉換成 String 對象。和 C 及 C++不一樣,沒有獨立的靜態字符數組字串可供使用。
(10) Java 增添了三個右移位運算符「 >>>」,具備與「邏輯」右移位運算符相似的功用,可在最末尾插入零值。「 >>」則會在移位的同時插入符號位(即「算術」移位)。
(11) 儘管表面上相似,但與 C++相比, Java 數組採用的是一個頗爲不一樣的結構,並具備獨特的行爲。有一個只讀的 length 成員,經過它可知道數組有多大。並且一旦超過數組邊界,運行期檢查會自動丟棄一個異常。全部數組都是在內存「堆」裏建立的,咱們可將一個數組分配給另外一個(只是簡單地複製數組句柄)。數組標識符屬於第一級對象,它的全部方法一般都適用於其餘全部對象。
(12) 對於全部不屬於主類型的對象,都只能經過 new 命令建立。和 C++不一樣, Java 沒有相應的命令能夠「在堆棧上」建立不屬於主類型的對象。全部主類型都只能在堆棧上建立,同時不使用new 命令。全部主要的類都有本身的「封裝(器)」類,因此可以經過 new 建立等價的、之內存「堆」爲基礎的對象(主類型數組是一個例外:它們可象 C++那樣經過集合初始化進行分配,或者使用 new)。
(13) Java 中沒必要進行提早聲明。若想在定義前使用一個類或方法,只需直接使用它便可—— 編譯器會保證使用恰當的定義。因此和在 C++中不一樣,咱們不會碰到任何涉及提早引用的問題。
(14) Java 沒有預處理機。若想使用另外一個庫裏的類,只需使用import 命令,並指定庫名便可。不存在相似於預處理機的宏。
(15) Java 用包代替了命名空間。因爲將全部東西都置入一個類,並且因爲採用了一種名爲「封裝」的機制,它能針對類名進行相似於命名空間分解的操做,因此命名的問題再也不進入咱們的考慮之列。數據包也會在單獨一個庫名下收集庫的組件。咱們只需簡單地「 import」(導入)一個包,剩下的工做會由編譯器自動完成。
(16) 被定義成類成員的對象句柄會自動初始化成 null。對基本類數據成員的初始化在 Java 裏獲得了可靠的保障。若不明確地進行初始化,它們就會獲得一個默認值(零或等價的值)。可對它們進行明確的初始化(顯式初始化):要麼在類內定義它們,要麼在構建器中定義。採用的語法比C++的語法更容易理解,並且對於 static 和非 static 成員來講都是固定不變的。咱們沒必要從外部定義 static 成員的存儲方式,這和 C++是不一樣的。
(17) 在 Java 裏,沒有象 C 和 C++那樣的指針。用 new 建立一個對象的時候,會得到一個引用。例如:
1 String s = new String("howdy");
然而, C++引用在建立時必須進行初始化,並且不可重定義到一個不一樣的位置。但Java 引用並不必定侷限於建立時的位置。它們可根據狀況任意定義,這便消除了對指針的部分需求。在C 和 C++裏大量採用指針的另外一個緣由是爲了能指向任意一個內存位置(這同時會使它們變得不安全,也是Java 不提供這一支持的緣由)。指針一般被看做在基本變量數組中四處移動的一種有效手段。 Java 容許咱們以更安全的形式達到相同的目標。解決指針問題的終極方法是「固有方法」。將指針傳遞給方法時,一般不會帶來太大的問題,由於此時沒有全局函數,只有類。並且咱們可傳遞對對象的引用。 Java 語言最開始聲稱本身「徹底不採用指針!」但隨着許多程序員都質問沒有指針如何工做?因而後來又聲明「採用受到限制的指針」。你們可自行判斷它是否「真」的是一個指針。但無論在何種狀況下,都不存在指針「算術」。
(18) Java 提供了與 C++相似的「構建器」( Constructor)。若是不本身定義一個,就會得到一個默認構建器。而若是定義了一個非默認的構建器,就不會爲咱們自動定義默認構建器。這和C++是同樣的。注意沒有複製構建器,由於全部自變量都是按引用傳遞的。
(19) Java 中沒有「破壞器」( Destructor)。變量不存在「做用域」的問題。一個對象的「存在時間」是由對象的存在時間決定的,並不是由垃圾收集器決定。有個 finalize()方法是每個類的成員,它在某種程度上相似於 C++的「破壞器」。但 finalize()是由垃圾收集器調用的,並且只負責釋放「資源」(如打開的文件、套接字、端口、 URL 等等)。如需在一個特定的地點作某樣事情,必須建立一個特殊的方法,並調用它,不能依賴 finalize()。而在另外一方面, C++中的全部對象都會(或者說「應該」)破壞,但並不是 Java 中的全部對象都會被看成「垃圾」收集掉。因爲 Java 不支持破壞器的概念,因此在必要的時候,必須謹慎地建立一個清除方法。並且針對類內的基礎類以及成員對象,須要明確調用全部清除方法。
(20) Java 具備方法「過載」機制,它的工做原理與 C++函數的過載幾乎是徹底相同的。
(21) Java 不支持默認自變量。
(22) Java 中沒有 goto 。它採起的無條件跳起色制是「 break 標籤」或者「 continue 標準」,用於跳出當前的多重嵌套循環。
(23) Java 採用了一種單根式的分級結構,所以全部對象都是從根類 Object 統一繼承的。而在 C++中,咱們可在任何地方啓動一個新的繼承樹,因此最後每每看到包含了大量樹的「一片森林」。在Java 中,咱們不管如何都只有一個分級結構。儘管這表面上看彷佛形成了限制,但因爲咱們知道每一個對象確定至少有一個Object 接口,因此每每能得到更強大的能力。 C++目前彷佛是惟一沒有強制單根結構的惟一一種 OO 語言。
(24) Java 沒有模板或者參數化類型的其餘形式。它提供了一系列集合: Vector(向量), Stack(堆棧)以及 Hashtable(散列表),用於容納 Object 引用。利用這些集合,咱們的一系列要求可獲得知足。但這些集合並不是是爲實現象 C++「標準模板庫」( STL)那樣的快速調用而設計的。 Java 1.2 中的新集合顯得更加完整,但仍不具有正宗模板那樣的高效率使用手段。
(25) 「垃圾收集」意味着在 Java 中出現內存漏洞的狀況會少得多,但也並不是徹底不可能(若調用一個用於分配存儲空間的固有方法,垃圾收集器就不能對其進行跟蹤監視)。然而,內存漏洞和資源漏洞可能是因爲編寫不當的 finalize()形成的,或是因爲在已分配的一個塊尾釋放一種資源形成的(「破壞器」在此時顯得特別方便)。垃圾收集器是在 C++基礎上的一種極大進步,使許多編程問題消彌於無形之中。但對少數幾個垃圾收集器力有不逮的問題,它倒是不大適合的。但垃圾收集器的大量優勢也使這一處缺點顯得微不足道。
(26) Java 內建了對多線程的支持。利用一個特殊的 Thread 類,咱們可經過繼承建立一個新線程(放棄了run()方法)。若將 synchronized(同步)關鍵字做爲方法的一個類型限制符使用,相互排斥現象會在對象這一級發生。在任何給定的時間,只有一個線程能使用一個對象的synchronized 方法。在另外一方面,一個synchronized 方法進入之後,它首先會「鎖定」對象,防止其餘任何 synchronized 方法再使用那個對象。只有退出了這個方法,纔會將對象「解鎖」。在線程之間,咱們仍然要負責實現更復雜的同步機制,方法是建立本身的「監視器」類。遞歸的 synchronized 方法能夠正常運做。若線程的優先等級相同,則時間的「分片」不能獲得保證。
(27) 咱們不是象 C++那樣控制聲明代碼塊,而是將訪問限定符( public, private 和 protected)置入每一個類成員的定義裏。若未規定一個「顯式」(明確的)限定符,就會默認爲「友好的」( friendly )。這意味着同一個包裏的其餘元素也能夠訪問它(至關於它們都成爲C++的「 friends」 —— 朋友),但不可由包外的任何元素訪問。類—— 以及類內的每一個方法—— 都有一個訪問限定符,決定它是否能在文件的外部「可見」。 private 關鍵字一般不多在 Java 中使用,由於與排斥同一個包內其餘類的訪問相比,「友好的」訪問一般更加有用。然而,在多線程的環境中,對 private 的恰當運用是很是重要的。 Java 的 protected 關鍵字意味着「 可由繼承者訪問,亦可由包內其餘元素訪問」。注意 Java 沒有與 C++的 protected 關鍵字等價的元素,後者意味着「只能由繼承者訪問」(之前可用「 private protected」實現這個目的,但這一對關鍵字的組合已被取消了)。
(28) 嵌套的類。在 C++中,對類進行嵌套有助於隱藏名稱,並便於代碼的組織(但 C++的「命名空間」已使名稱的隱藏顯得多餘)。 Java 的「封裝」或「打包」概念等價於 C++的命名空間,因此再也不是一個問題。Java 1.1 引入了「內部類」的概念,它祕密保持指向外部類的一個句柄—— 建立內部類對象的時候須要用到。這意味着內部類對象也許能訪問外部類對象的成員,毋需任何條件—— 就好象那些成員直接隸屬於內部類對象同樣。這樣便爲回調問題提供了一個更優秀的方案—— C++是用指向成員的指針解決的。
(29) 因爲存在前面介紹的那種內部類,因此 Java 裏沒有指向成員的指針。
(30) Java 不存在「嵌入」( inline)方法。 Java 編譯器也許會自行決定嵌入一個方法,但咱們對此沒有更多的控制權力。在 Java 中,可爲一個方法使用 final 關鍵字,從而「建議」進行嵌入操做。然而,嵌入函數對於 C++的編譯器來講也只是一種建議。
(31) Java 中的繼承具備與 C++相同的效果,但採用的語法不一樣。 Java 用 extends 關鍵字標誌從一個基礎類的繼承,並用 super 關鍵字指出準備在基礎類中調用的方法,它與咱們當前所在的方法具備相同的名字(然而, Java 中的 super 關鍵字只容許咱們訪問父類的方法—— 亦即分級結構的上一級)。經過在 C++中設定基礎類的做用域,咱們可訪問位於分級結構較深處的方法。亦可用super 關鍵字調用基礎類構建器。正如早先指出的那樣,全部類最終都會從 Object 裏自動繼承。和 C++不一樣,不存在明確的構建器初始化列表。但編譯器會強迫咱們在構建器主體的開頭進行所有的基礎類初始化,並且不容許咱們在主體的後面部分進行這一工做。經過組合運用自動初始化以及來自未初始化對象句柄的異常,成員的初始化可獲得有效的保證。
1 public class Foo extends Bar { 2 public Foo(String msg) { 3 super(msg); // Calls base constructor 4 } 5 public baz(int i) { // Override 6 super.baz(i); // Calls base method 7 } 8 }
(32) Java 中的繼承不會改變基礎類成員的保護級別。咱們不能在 Java 中指定 public, private 或者protected 繼承,這一點與 C++是相同的。此外,在衍生類中的優先方法不能減小對基礎類方法的訪問。例如,假設一個成員在基礎類中屬於 public,而咱們用另外一個方法代替了它,那麼用於替換的方法也必須屬於public(編譯器會自動檢查)。
(33) Java 提供了一個 interface 關鍵字,它的做用是建立抽象基礎類的一個等價物。 在其中填充抽象方法,且沒有數據成員。這樣一來,對於僅僅設計成一個接口的東西,以及對於用 extends 關鍵字在現有功能基礎上的擴展,二者之間便產生了一個明顯的差別。不值得用 abstract 關鍵字產生一種相似的效果,由於咱們不能建立屬於那個類的一個對象。一個 abstract (抽象)類可包含抽象方法(儘管並不要求在它裏面包含什麼東西),但它也能包含用於具體實現的代碼。所以,它被限制成一個單一的繼承。經過與接口聯合使用,這一方案避免了對相似於 C++虛擬基礎類那樣的一些機制的須要。爲建立可進行「例示」(即建立一個實例)的一個 interface(接口)的版本,需使用 implements 關鍵字。它的語法相似於繼承的語法,以下所示:
1 public interface Face { 2 public void smile(); 3 } 4 public class Baz extends Bar implements Face { 5 public void smile( ) { 6 System.out.println("a warm smile"); 7 } 8 }
(34) Java 中沒有 virtual 關鍵字,由於全部非 static 方法都確定會用到動態綁定。在 Java 中,程序員沒必要自行決定是否使用動態綁定。 C++之因此採用了 virtual,是因爲咱們對性能進行調整的時候,可經過將其省略,從而得到執行效率的少許提高(或者換句話說:「若是不用,就不必爲它付出代價」)。 virtual常常會形成必定程度的混淆,並且得到使人不快的結果。 final 關鍵字爲性能的調整規定了一些範圍—— 它向編譯器指出這種方法不能被取代,因此它的範圍可能被靜態約束(並且成爲嵌入狀態,因此使用C++非virtual 調用的等價方式)。這些優化工做是由編譯器完成的。
(35) Java 不提供多重繼承機制( MI),至少不象 C++那樣作。與 protected 相似, MI 表面上是一個很不錯的主意,但只有真正面對一個特定的設計問題時,才知道本身須要它。因爲Java 使用的是「單根」分級結構,因此只有在極少的場合才須要用到 MI。 interface 關鍵字會幫助咱們自動完成多個接口的合併工做。
(36) 運行期的類型標識功能與 C++極爲類似。例如,爲得到與句柄 X 有關的信息,可以使用下述代碼:
1 X.getClass().getName();
爲進行一個「類型安全」的緊縮造型,可以使用:
1 derived d = (derived)base;
這與舊式風格的 C 造型是同樣的。編譯器會自動調用動態造型機制,不要求使用額外的語法。儘管它並不象C++的「 new casts」那樣具備易於定位造型的優勢,但 Java 會檢查使用狀況,並丟棄那些「異常」,因此它不會象 C++那樣容許壞造型的存在。
(37) Java 採起了不一樣的異常控制機制,由於此時已經不存在構建器。可添加一個finally 從句,強制執行特定的語句,以便進行必要的清除工做。 Java 中的全部異常都是從基礎類 Throwable 裏繼承而來的,因此可確保咱們獲得的是一個通用接口。
1 public void f(Obj b) throws IOException { 2 myresource mr = b.createResource(); 3 try { 4 mr.UseResource(); 5 } catch (MyException e) { 6 // handle my exception 7 } catch (Throwable e) { 8 // handle all other exceptions 9 } finally { 10 mr.dispose(); // special cleanup 11 } 12 }
(38) Java 的異常規範比 C++的出色得多。丟棄一個錯誤的異常後,不是象 C++那樣在運行期間調用一個函數, Java 異常規範是在編譯期間檢查並執行的。除此之外,被取代的方法必須遵照那一方法的基礎類版本的異常規範:它們可丟棄指定的異常或者從那些異常衍生出來的其餘異常。這樣一來,咱們最終獲得的是更爲「健壯」的異常控制代碼。
(39) Java 具備方法過載的能力,但不容許運算符過載。 String 類不能用+和+=運算符鏈接不一樣的字串,並且String 表達式使用自動的類型轉換,但那是一種特殊的內建狀況。
(40) 經過事先的約定, C++中常常出現的 const 問題在 Java 裏已獲得了控制。咱們只能傳遞指向對象的句柄,本地副本永遠不會爲咱們自動生成。若但願使用相似 C++按值傳遞那樣的技術,可調用 clone(),生成自變量的一個本地副本(儘管 clone()的設計依然尚顯粗糙)。根本不存在被自動調用的副本構建器。爲建立一個編譯期的常數值,可象下面這樣編碼:
1 static final int SIZE = 255 2 static final int BSIZE = 8 * SIZE
(41) 因爲安全方面的緣由,「應用程序」的編程與「程序片」的編程之間存在着顯著的差別。一個最明顯的問題是程序片不容許咱們進行磁盤的寫操做,由於這樣作會形成從遠程站點下載的、不明來歷的程序可能胡亂改寫咱們的磁盤。隨着 Java 1.1 對數字簽名技術的引用,這一狀況已有所改觀。根據數字簽名,咱們可確切知道一個程序片的所有做者,並驗證他們是否已得到受權。 Java 1.2 會進一步加強程序片的能力。
(42) 因爲 Java 在某些場合可能顯得限制太多,因此有時不肯用它執行象直接訪問硬件這樣的重要任務。Java 解決這個問題的方案是「固有方法」,容許咱們調用由其餘語言寫成的函數(目前只支持C 和 C++)。這樣一來,咱們就確定可以解決與平臺有關的問題(採用一種不可移植的形式,但那些代碼隨後會被隔離起來)。程序片不能調用固有方法,只有應用程序才能夠。
(43) Java 提供對註釋文檔的內建支持,因此源碼文件也能夠包含它們本身的文檔。經過一個單獨的程序,這些文檔信息能夠提取出來,並從新格式化成 HTML。這無疑是文檔管理及應用的極大進步。
(44) Java 包含了一些標準庫, 用於完成特定的任務。 C++則依靠一些非標準的、由其餘廠商提供的庫。這些任務包括(或不久就要包括):
■連網
■數據庫鏈接(經過 JDBC)
■多線程
■分佈式對象(經過 RMI 和 CORBA)
■壓縮
■商貿
因爲這些庫簡單易用,並且很是標準,因此能極大加快應用程序的開發速度。
(45) Java 1.1 包含了 Java Beans 標準,後者可建立在可視編程環境中使用的組件。因爲遵照一樣的標準,因此可視組件可以在全部廠商的開發環境中使用。因爲咱們並不依賴一家廠商的方案進行可視組件的設計,因此組件的選擇餘地會加大,並可提升組件的效能。除此以外, Java Beans 的設計很是簡單,便於程序員理解;而那些由不一樣的廠商開發的專用組件框架則要求進行更深刻的學習。
(46) 若訪問 Java 句柄失敗,就會丟棄一次異常。這種丟棄測試並不必定要正好在使用一個句柄以前進行。根據 Java 的設計規範,只是說異常必須以某種形式丟棄。許多C++運行期系統也能丟棄那些因爲指針錯誤形成的異常。
(47) Java 一般顯得更爲健壯,爲此採起的手段以下:
■對象句柄初始化成 null(一個關鍵字)
■句柄確定會獲得檢查,並在出錯時丟棄異常
■全部數組訪問都會獲得檢查,及時發現邊界違例狀況
■自動垃圾收集,防止出現內存漏洞
■明確、「傻瓜式」的異常控制機制
■爲多線程提供了簡單的語言支持
■對網絡程序片進行字節碼校驗
可愛博主:AlanLee
博客地址:http://www.cnblogs.com/AlanLee
本文出自博客園,歡迎你們加入博客園。