热门问题
时间线
聊天
视角

Java和C++的對照

来自维基百科,自由的百科全书

Remove ads

本條目為Java語言C++語言之間的比較

設計目標

C++和Java語言之間的不同可以追溯到它們各自的傳統,它們有着不同的設計目標。

  • C++ 被設計成主要用在系統性應用程式設計上的語言,對C語言進行了擴展。基於C語言這個為執行效率設計的過程式程式設計語言,C++添加了對這些特性的支持:靜態類型面向對象程序設計的支持、異常處理RAII以及泛型。另外它還添加了一個包含泛型容器和算法的C++庫函數
  • Java 最初被設計用來支持網絡計算。它依賴一個虛擬機來保證安全可移植性。Java包含一個可擴展的庫以提供一個完整的下層平台抽象。Java是一種靜態面向對象語言,它使用的語法和C++類似,但並不與之兼容。為了使更多的人到使用更易用的語言,它進行了全新的設計。

不同的開發目標導致C++和Java這兩種語言的不同的規則以及設計上的平衡點不同。如下列出不同點:

更多信息 C++, Java ...

C++ 是一門強大的語言,設計用在系統程式設計方面。Java語言是設計成簡單易用易學習,並有一個強大的跨平台的庫。Java函式庫對一個函式庫來說相當的大。但Java並不會提供所在平台的所有特性和接口。C++函式庫簡單健壯,提供容器關聯數組的支持。[2]

Remove ads

語言特性

語法

  • Java語法上下文無關文法,可以用一個簡單的LALR語法分析器來分析。而分析C++就複雜多了;例如 Foo<1>(3); ,如果 Foo 是一個變數,那麼它是一個比較的運算式,但如果 Foo 是一個類範本的名字,那麼它會創建一個物件。
  • C++允許名字空間級別的常量,變數和函數. 而所有這樣的 Java 聲明必須在一個類或者介面當中。
  • 在 C++ 的聲明中,一個類名可以用來聲明一個此類物件的值。 Java 裡沒辦法做到這點,在Java裡對象不是值。在 Java 的聲明中,一個類名聲明的是對此類的一個物件的引用。而在 C++ 裡與之等價的做法是用 "*" 來聲明一個指標。
  • 在 C++ 裡,"."操作符將一個物件作為一個左指令引數來訪問這個物件的成員。因為對象在 Java 裡不是值,所有的對象都通過引用來訪問,剛才的做法在 Java 裡是無法實現的。在 Java 裡,"." 操作符是將一個物件的引用作為左指令引數來訪問這個物件的成員,在C++中和這種做法等價的是 ->
更多信息 C++, Java ...
  • 在 C++ 裡,定義一個指向常量的指標(唯讀指標)是可能的,也就是說,你不能修改這個指標指向的物件的內容。函數和方法也都保證不會修改用 "const" 關鍵字的指標指向的物件的內容,是強制常量正確性英語const-correctness的。在 Java 裡這是不可能做到的,你可以定義一個引用為 "final"(就像在 C++ 裡定義一個指標"常量"),但這只是阻止你重新綁定這個引用;你還是可以修改這個 "final" 引用指向的物件的。
更多信息 C++, Java ...
  • C++ 支持 goto 語句;Java 強制結構化流程控制structured control flow),依賴break標籤continue標籤 語句來提供類似於 goto 的部分。一些評論者指出這些標籤化的流程控制打破了結構化程式設計的單退出點的。點.[3]
  • C++ 提供了一些 Java 缺乏的低級特性。在 C++ 裡,指標可以用來操作特定的記憶體位置,這是在寫低級作業系統模組的時候必須用到的。類似的,許多 C++ 編譯期支持內聯彙編,在 Java 裡,這樣的代碼只能放在外來的庫中,而且在調用的時候只能通過JNI來訪問這些外來庫提供的介面.
Remove ads

語意

  • C++ 允許給函數/方法的參數設置預設值,Java 不提供這個特性。但是方法重載可以達到同樣的效果。
  • C++ 里最小的編譯單位是一個函數;Java 里最小的編譯單位是一個類. 在 C++ 里,函數可以被單獨編譯。在 Java 里,要編譯和維護單獨的方法需要把它們移到超類子類或者使用其他的代碼重構的技巧。
  • C++ 允許基本類型之間的一些隱式的轉換,也允許程式設計師對於用戶自定義類型相關的隱式轉換規則。在 Java 里,只有基本類型之間變寬類型的轉換可以是隱式的;其餘的轉換需要顯式的類型轉換語法。
    • 這造成的一個後果是,雖然在 Java 和 C++ 里循環的條件(ifwhilefor 里的退出條件)預期的都是一個布爾表達式,但 if(a = 5) 這樣的代碼在 Java 里會導致編譯錯誤,因為沒有從整型到布爾的隱式變窄轉換。如果代碼是 if(a == 5) 的輸錯的情況那麼是很方便發現這個錯誤的。而目前的 C++ 編譯器一般來說只會針對這種情況產生一個警告。
  • 對於傳參數給函數的情況,C++ 支持引用傳遞值傳遞。在 Java 里,參數總是值傳遞的。[4] 但在 Java 里,所有的非基本類型的值都只是對於對象的引用(用 C++ 的術語來說,它們是智能指針)。對象在 Java 里不是作為值直接被使用的,只有對象的引用可以被直接操作;習慣於將對象當做值直接使用的 C++ 開發者經常會把這個跟引用傳遞搞混。
  • Java 內建的類型在字節寬度和取值範圍上是被虛擬機定義好的;在 C++ 里,內建的類型有定義一個最小取值範圍,但是其他的部分(字節寬度)可以被映射成具體平台上支持的原生類型。
    • 舉個例子,Java 字符是16位的Unicode字符,字符串是由這樣的字符組成的序列。 C++ 提供窄和寬兩種字符,但實際的字符寬度是和平台相關的,視所用的字符集而定。字符串可以由這兩種字符中的一種組成。
  • 浮點數及其操作的精度和捨入方式在 C++ 里是平台相關的. Java 提供了一個可選的嚴格的浮點數模型英語strictfp,保證跨平台的一致性,不過可能會導致運行時效率比較差.
  • 在 C++ 里,指針可以作為內存地址直接操作. Java 沒有指針 — 它只有對象引用和數組引用,這兩者都不允許直接用來訪問內存地址。在 C++ 里可以構造一個指向指針的指針,而 Java 的引用只能指向對象。。
  • 在 C++ 里,指針可以指向函數或者方法(函數指針)。在 Java 里的等價物是對象或者接口的引用。
  • 雖然有使用棧內存分配的對象,C++ 還是支持區域資源管理,一個用來自動管理內存和其他系統資源的技術,此技術支持確定性對象銷毀(deterministic object destruction)。不過,區域資源管理在 C++ 里是不被保證的;它只是一個設計模式,所以需要依賴程式設計師遵守相關的規則. Java 通過使用垃圾搜集來支持自動內存管理,但對於其他的系統資源(窗口,通訊埠,執行緒),如果垃圾搜集器無法決定它們是否不再被用到,那通常還是需要顯式的釋放的。
  • C++ 的用戶可自定義操作符重載的特性在 Java 里是不支持的。唯一在 Java 里可以重載的操作符是 "+" 和 "+=" 操作符,在字符串里重載為連接字符串。
  • Java 的標準應用程序接口支持反射動態加載任意代碼。
  • C++ 支持靜態和動態的庫連接。
  • Java 支持泛型,其主要目的是提供類型安全的容器. C++ 支持模板,在泛型編程方面提供了更強的支持。
  • Java 和 C++ 都對基本類型(也叫"內建"類型)和用戶自定義類型(也叫"複合"類型)。在 Java 里,基本類型只有值的語義,複合類型只有引用的語義. 在 C++ 里所有的值都有值語義,可以創建對於任何類型的引用,這樣就允許通過引用語義來操作對象。
  • C++ 支持任意類型的多重繼承. 在 Java 里一個類只能從單個的類繼承而來,但一個類可以實現多個的接口(換句話說,它支持類型的多重繼承,但對於實現只能單繼承)。
  • Java 對於類和接口是顯式區分的。在 C++ 里多重繼承和純虛函數使得定義出類似於 Java 的接口的類是可能的,不過會有少許區別。
  • Java 在語言和標準庫都對多執行緒有良好的支持。synchronized 這個 Java 的關鍵字為了支持多執行緒應用提供了簡單而安全的的互斥鎖,但同步(synchronized)區只能用 LIFO 的順序離開。Java 也為更高階的多執行緒同步提供了健壯而複雜的庫。在 C++ 里沒有專門為多執行緒定義的內存模型;但第三方庫提供了和 Java 差不多的功能;不過這些 C++ 庫之間差異較大,一致性不好。
  • C++ 方法可以聲明為虛函數,虛函數是在運行期根據對象的類型才確定的。 C++ 方法缺省情況下不是虛的. 在 Java 里,方法缺省情況下是虛的,但可以使用final關鍵字使之聲明為非虛的。
  • C++ 枚舉屬於基本類型,支持和其他整數類型之間的轉換和比較。Java 枚舉實際上是類的實例(它們從 java.lang.Enum<E> 擴展而來),象其他的類一樣可以定義構造函數,數據成員及方法。
Remove ads

資源管理

  • Java 提供了自動化的垃圾搜集。在 C++ 里內存管理通常通過構造函數,析構函數以及智能指針。C++ 標準允許垃圾搜集,但並不強制要求;實際使用上垃圾搜集極少被用到。強制使用自動垃圾搜集導致了在 Java 里編寫實時軟體是困難的。[3]
  • C++ 可以申請任意的內存塊。Java 只能通過對象實例化來申請內存。(注意:在 Java 里,程序員可以通過創建一個字節數組模擬申請任意的內存塊,不過 Java 數組仍然是對象。)
  • Java 和 C++ 在資源管理上使用不同的習語。Java 主要依賴只能回收內存的垃圾搜集機制,因為該機制如果用於回收使用中的非內存的系統資源可能是非常危險的。而 C++ 主要依賴 RAII (資源的獲取就是初始化)。這反映了這兩種語言的幾方面的不同:
    • 在 C++ 里在棧里申請複合類型的對象是很平常的,一旦退出棧的範圍就會被銷毀。在 Java 里複合類型的對象總是在堆里申請的內存,而後被垃圾搜集器搜集(除非在虛擬機裡使用了逃逸分析技術來將堆的內存申請轉成棧的。)
    • C++ 有析構函數,而 Java 有finalizer(finalizer). 兩者都會在對象釋放之前被調用,但是它們有顯著的不同. 一個 C++ 對象的析構函數必須被隱式(棧變量對象的情況)或者顯式地調用來釋放對象. 析構函數在對象釋放之前同步地執行. 同步,協調的反初始化以及釋放在 C++ 里滿足 RAII 的要求. 在 Java 里,對象的釋放是被垃圾搜集器隱式處理的. 一個 Java 對象的 finalizer 在它被最後一次訪問之後和在實際釋放之前的某個時間點被異步非同步)地調用,這個調用有可能一直不產生. 非常少的對象需要 finalizer;只有那些在釋放前必須保證一些清理工作一定要做的對象來說才是需要的 — 典型的情況是:釋放對 JVM 來說是外部的資源. 在 Java 里,企圖安全同步的釋放某些系統資源,只能用顯式的 try/finally 結構來進行.
    • 在 C++ 里是有可能有一個迷途指針的 – 過時的對一個已釋放的對象的引用參照);試圖使用一個迷途指針的結果是導致程序錯誤. 在 Java 里,垃圾搜集器不會銷毀一個正在被引用的對象.
    • 在 C++ 里未初始化過的基本類型對象是有可能存在的,Java 強制要做缺省初始化.
    • 在 C++ 里有可能申請了一個對象,但對它沒有任何引用. 這樣的不可達對象不可訪問內存)是無法被銷毀的,導致了內存泄漏. 作為對比,在 Java 里一個對象不會被回收直到它變得不可達(對於用戶程序來說). (注意: 弱引用(弱引用)是被支持的,這個特性讓 Java 的垃圾搜集器能夠識別不同 程度的可達性.)在 Java 里垃圾搜集阻止了很多內存泄漏的情況,但某些情況下泄漏仍然是可能的.[5]
    • Java 更容易泄漏非內存資源,而 C++ 的慣用做法更不會導致這種泄漏.
Remove ads

  • C++ 對於許多平台相關的特性提供了跨平台的訪問方式. 從 Java 到原生的操作系統和硬件相關的函數的直接訪問需要用到JNIJava本地接口)。

運行時

  • C++ 通常來說會直接被編譯成機器碼,被操作系統直接執行。Java 通常會被編譯成字節碼,被Java虛擬機解釋器或者即時編譯器編譯成機器碼然後執行。
  • 因為表達方式不受限制,低級的 C++ 語言特性(例如:不被檢查的數組訪問,原始指針,類型雙關語(type punning英語type punning)不能在編譯期間或者運行期間可靠地被檢查. 相關的編程錯誤會導致低級的緩存溢出段錯誤記憶體區段錯誤). 標準模板庫 提供了高級的抽象(例如 vector,list 和 map)來幫助避免這樣的錯誤.在 Java 里,低級錯誤不會發生或者會被JVM檢測到並以異常的形式報告給應用.
  • Java 語言在越界訪問數組的時候一般來說會對數組進行邊界檢查(bounds checking). 這消除了導致程序不穩定的一個可能因素,但這是以執行速度更慢一些作為代價的. 在一些情況下,編譯器分析compiler analysis英語compiler analysis)可以檢測到不必要的邊界檢查並去掉。C++ 對於原生數組的越界訪問沒有要求特定的處理,所以需要對於原生數組確認不越界. 但C++ 標準庫里的一部分庫象 std::vector 也提供了可選的邊界檢查. 總的來說,Java 數組是"總是安全;嚴格限制;開銷較多" ,而 C++ 原生數組是"可選的開銷; 完全不限制;有潛在的不安全."
Remove ads

模板 vs. 泛型

C++ 和 Java 都提供泛型編程的能力,分別是模板泛型(Generics in Java英語Generics in Java). 雖然它們被創造用來解決類似的問題,有類似的語法,但實際上很不一樣.

更多信息 C++ 模板, Java 泛型 ...
Remove ads

雜項

  • Java 和 C++ 在使代碼在不同的文件分開方面使用了不同的技術. Java 使用了一個包系統,這個系統對所有的程序都要指定了文件名和路徑. 在 Java 里,編譯器負責導入可執行的類文件. C++ 使用了頭文件源代碼的包含系統來在不同的文件共享聲明.
  • 編譯好的 Java 代碼一般來說比 C++ 文件小,因為Java字節碼(Java bytecode)一般來說比機器碼要更緊湊[來源請求],Java 程序都不是靜態鏈接的.
  • C++ 編譯多了一個文本預處理過程,Java 是沒有的. 因此一些使用者在他們的編譯過程之前增加了一個預處理的過程,這樣能更好的支持需要條件編譯的情況.
  • 兩個語言裡數組都是定長的. 在 Java 里,數組是頭等對象,而在 C++ 里它們只是它們的基本類型元素的連續的序列,經常用一個指向第一個元素的指針和一個可選的長度來引用. 在 Java 里,數組是被邊界檢查的,而且知道它們的長度,而在 C++ 里你可以將任意的序列當成一個數組. C++ 和 Java 都提供了相關的容器類(分別為std::vectorjava.util.ArrayList),可以改變大小.
  • Java 的除法和模除操作符是定義成零截斷的. C++ 沒有定義這兩個操作符是零截斷的還是"負無窮截斷"的. 在 Java 里-3/2 總是得到 -1,但一個 C++ 編譯器可能會返回 -1 或 -2,視平台而定. C99 定義了和 Java 一樣的除法方式. 兩種語言都保證對於所有的 a 和 b(b!=0)(當 a 和 b都是整型的時候)(a/b)*b + (a%b) == a. C++ 版本有時候會更快,因為它允許直接使用處理器的截斷方式.
  • 整型的長度在 Java 里是已定義好的(int 為 32-bit, long 為 64-bit),而在 C++ 里整型和指針的長度是和編譯器以及應用二進制接口相關的. 因此仔細編寫的 C++ 代碼可以利用64位處理器的能力而又可以在32位處理器上工作. 但是需要很仔細的用可移植的方式編寫. 作為對比,Java 的固定整型大小使得程序員無法做到這樣,沒辦法利用處理器的字長會導致 Java 在64位處理器上表現較差.

性能

想運行一個編譯好的 Java 程序,計算機上要運行JVM;而編譯好的 C++ 程序不需要額外的應用。比較早期的 Java 版本在性能上比靜態編譯的語言如 C++ 差得很多,這是因為用 C++ 是直接編譯成一些機器指令,而當 Java 編譯成字節碼以後用 JVM 解釋執行的時候又牽涉了不少額外的機器指令。 例如:

更多信息 Java/C++ 語句, C++ 生成的代碼 (x86) ...

C++ 在大部分的情況下都比 Java 要快,[7] 有幾個數值方面的基準測試的研究爭辯說 Java 在某些情況下可能會比 C++ 的性能好得多。[8][9][10] 但有人說數值方面的基準測試對於語言的評估是不合適的,因為編譯器都可以做相關的優化,甚至可能將被測試的代碼徹底刪除。[11][12][13] 如果涉及到一個真正現實應用的程序,Java 會因為很多原因導致性能變差:[14][15][16]

  • 所有的對象都在堆里被申請。對於使用小對象的函數來說會導致很大的性能損失,因為在棧里申請內存幾乎沒有性能損失。
  • 方法缺省是虛的。這對於小對象來說會因為虛表增加好幾倍的內存使用。它也會引起性能損失,因為 JIT 編譯器不得不對查虛表的過程做額外的優化。
  • 即使使用標準的容器依然會有很多的類型轉換,這會引起性能損失,因為需要遍歷整個繼承樹。
  • 虛擬機更進一步增加了內存的使用,因此降低了內存的局部性,增加了緩存命中失敗率,從而導致整個程序變慢。
  • 缺乏低級細節的操作方式使得開發者無法將程序進一步優化,因為編譯器不支持。[17]

有人爭論說,和 Java 相比 C++也有很多劣勢:

  • 指針使得優化變得困難,因為它們可能指向任意的數據。當然現在這一點也並非完全正確,因為一些現代的編譯器引入了 "嚴格別名" 的規則 [18] 並且支持 C99 的關鍵字 restrict,從而嚴格限制了指針的使用,使其只能用於指向已知的變量 [19]
  • Java 的垃圾搜集和使用malloc/new來申請內存相比能擁有更好的緩存連貫性,因為它的申請一般來說是順序的。然而,始終有爭論認為二者同樣會導致內存的「零碎化」(即多次分配和回收之後內存空間會變得不連續),且並沒有哪一個比對方有更明顯的緩存優勢。
  • 運行時編譯可能可以更好的優化代碼,因為可以利用運行時的額外的信息,例如知道代碼是在什麼樣的處理器上運行。然而當今的情況也並非完全如此,因為目前最先進的 C++ 編譯器也會針對不同系統生成不同的目標代碼,以期充分利用該系統的計算能力 [20]

此外,有爭議的是,花在更複雜的 C++ 代碼上的 debug 時間太多,用 Java 開發完全可以把這些時間用來優化 Java 代碼。當然對於一個給定的程序來說兩種語言能優化到什麼程度也是一方面。最後,對於處理器負擔很重的情況,例如視頻渲染,C++ 能直接訪問硬件,在同樣一個硬件規格下 C++ 總是會比 Java 的表現好很多。

Remove ads

所有權控制

C++不是任何一個公司或者組織的商標,不被任何個人擁有。[21]Java原是Sun的商標,現在由甲骨文公司擁有。[22]

C++語言由 ISO/IEC 14882 定義,是一個ISO標準,由 ISO/IEC JTC1/SC22/WG21 委員會發布。Java語言由 Java Language Specification 定義,這是一本Sun公司(已被甲骨文收購)出版的書。[23]

其他

兩者的對象訪問格式也不一樣。

參考文獻

外部連結

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads