C++和java都號稱是面向對象的語言,雖然C++不完全算是。學習過C++如何快速對java有個大體的掌握,可以通過對比來進行了解。
首先還是來高大上一下,看看他們的使命:
· C++ 被設計成主要用在系統性應用程序設計上的語言,對C語言進行了擴展。對於C語言這個為運行效率設計的過程式程序設計語言, C++ 特別加上了以下這些特性的支持:靜態類型的面向對象程序設計的支持、異常處理、RAII以及泛型。另外它還加上了一個包含泛型容器和算法的C++庫函數。
· Java 最開始是被設計用來支持網絡計算。它依賴一個虛擬機來保證安全和可移植性。Java 包含一個可擴展的庫用以提供一個完整的的下層平台的抽象。Java 是一種靜態面向對象語言,它使用的語法類似C++,但與之不兼容。為了使更多的人到使用更易用的語言,它進行了全新的設計。
總體的了解一下java和C++在語義上的區別:
·C++ 允許給函數/方法的參數設置缺省值, Java 不提供這個特性. 但是方法重載可以達到同樣的效果.
·C++ 里最小的編譯單位是一個函數; Java 里最小的編譯單位是一個類. 在 C++ 里, 函數可以被單獨編譯. 在 Java 里, 要編譯和維護單獨的方法需要把它們移到超類或子類或者使用其他的代碼重構的技巧.
·C++ 允許基本類型之間的一些隱式的轉換, 也允許程序員對於用戶自定義類型相關的隱式轉換規則. 在 Java 里, 只有基本類型之間變寬類型的轉換可以是隱式的; 其余的轉換需要顯式的類型轉換語法.
·這造成的一個后果是,雖然在 Java 和 C++ 里循環的條件(if, while 和 for 里的退出條件)預期的都是一個布爾表達式, 但 if(a = 5) 這樣的代碼在 Java 里會導致編譯錯誤,因為沒有從整型到布爾的隱式變窄轉換. 如果代碼是 if(a == 5) 的輸錯的情況那么是很方便發現這個錯誤的. 而目前的 C++ 編譯器一般來說只會針對這種情況產生一個警告.
·對於傳參數給函數的情況, C++ 支持引用傳遞和值傳遞. 在 Java 里, 參數總是值傳遞的. 但在 Java 里,所有的非基本類型的值都只是對於對象的引用 (用 C++ 的術語來說, 它們是智能指針). 對象在 Java 里不是作為值直接被使用的,只有對象的引用可以被直接操作; 習慣於將對象當做值直接使用的 C++ 開發者經常會把這個跟引用傳遞搞混.
·Java 內建的類型在字節寬度和取值范圍上是被虛擬機定義好的; 在 C++ 里, 內建的類型有定義一個最小取值范圍, 但是其他的部分(字節寬度)可以被映射成具體平台上支持的原生類型.
·舉個例子, Java 字符是16位的Unicode字符, 字符串是由這樣的字符組成的串行. C++ 提供窄和寬兩種字符,但實際的字符寬度是和平台相關的, 視所用的字符集而定. 字符串可以由這兩種字符中的一種組成.
·浮點數及其操作的精度和舍入方式在 C++ 里是平台相關的. Java 提供了一個可選的嚴格的浮點數模型,保證跨平台的一致性,不過可能會導致運行時效率比較差.
·在 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 里一個類只能從單個的類繼承而來,但一個類可以實現多個的接口(換句話說,它支持類型的多重繼承,但對於實現只能單繼承(it supports multiple inheritance of types, but only single inheritance of implementation))。
·Java 對於類和接口是顯式區分的. 在 C++ 里多重繼承和純虛函數使得定義出類似於 Java 的接口的類是可能的,不過會有少許區別.
·Java 在語言和標准庫都對多線程有良好的支持. synchronized 這個 Java 的關鍵字為了支持多線程應用提供了簡單而安全的互斥鎖 ,但同步(synchronized)區只能用 LIFO 的順序離開. Java 也為更高階的多線程同步提供了健壯而復雜的庫. 在 C++ 里沒有專門為多線程定義的內存模型; 但第三方庫提供了和 Java 差不多的功能; 不過這些 C++ 庫之間差異較大,一致性不好.
·C++ 方法可以聲明為虛函數, 虛函數是在運行期根據對象的類型才確定的. C++ 方法缺省情況下不是虛的. 在 Java 里, 方法缺省情況下是虛的, 但可以使用final關鍵字使之聲明為非虛的.
·C++ 枚舉屬於基本類型,支持和其他整數類型之間的轉換和比較. Java 枚舉實際上是類的實例(它們從 java.lang.Enum<E> 擴展而來),象其他的類一樣可以定義構造函數,數據成員及方法.
看完上面這些可能與實際的語言語法比較脫離,比如什么“反射”這種動態語言的特性在C++里面是不存在的,下面來看一下簡單的總結:
main 函數
C++
int main( int argc, char* argv[])
{
printf( "Hello, world" );
}
Java
// 每個函數都必須是一個類的一部分;當java <class>運行是一個特定類的主函數會被調用
// (因此你可以讓每個類都有一個main函數,這在寫單元測試是很有用)
class HelloWorld
{
public static void main(String args[])
{
System.out.println( "Hello, World" );
}
}
類的聲明
除了 Java 不要求用分號外幾乎是相同的。
C++
class Bar {};
Java
class Bar {}
方法聲明
都相同的, 除了在Java,方法必須總是 某個類的一部分並且可能public/private/protected 作為修飾
構造函數和析構函數
構造函數都是相同的 (即類的名字), Java沒有准確意義上的的析構函數
靜態成員函數和變量
方法聲明是相同的, 但 Java 提供靜態初始化塊來來初始化靜態變量 (不需要在源文件中聲明):
class Foo
{
static private int x;
// 靜態初始化塊
{ x = 5; }
}對象的聲明C++ // 在棧中
myClass x;
//或者在堆中
myClass *x = new myClass;
Java
// 總是在對堆中聲明
myClass x = new myClass();
繼 承
C++
class Foo : public Bar
{ ... };
Java
class Foo extends Bar
{ ... }
訪問級別 (abstraction barriers)
C++
public:
void foo();
void bar();
Java
public void foo();
public void bar();
虛函數
C++
virtual int foo(); // 或者非虛函數寫作 int foo();
Java
// 函數默認的就是虛函數; 用final關鍵字防止重載
int foo(); // 或者, final int foo();
內存管理
大體上是相同的--new 來分配, 但是 Java沒有 delete,因為它有垃圾回收器。
NULL vs null
C++
// 初始化一個指針為 NULL
int *x = NULL;
Java
// 編譯器將捕獲使用未初始化的引用
//但是如果你因需要初始化一個引用而賦一個null,那么這是無效的
myClass x = null;
布爾型
Java有一點羅嗦: 你必須寫 boolean而不止是 bool.
C++
bool foo;
Java
boolean foo;
常 量
C++
const int x = 7;
Java
final int x = 7; (final 關鍵字修飾類的方法的時候表示該方法不可被子類重新定義,因為在java中的方法都是默認virtual的)
拋異常
首先,Java在編譯器強制拋異常—如果你的方法可能會拋異常你必需明確報告
C++
int foo() throw (IOException)
Java
int foo() throws IOException
數 組
C++
int x[10];
// 或
int *x = new x[10];
// 使用 x,然后歸還內存
delete[] x;
Java
int[] x = new int[10];
// 使用 x, 內存有垃圾回收器回收或
//或在程序生命周期盡頭歸還給系統
集合和迭代器
C++
迭代器是類的成員。范圍的開始是<容器>.begin(), 結束是 <容器>.end()。 用++ 操作符遞增, 用 *操作符訪。
vector myVec;
for ( vector<int>::iterator itr = myVec.begin();
itr != myVec.end();
++itr )
{
cout << *itr;
}
Java
迭代器只是一個接口。 范圍的開始是 <集合>.iterator,你必須用itr.hasNext()來查看是否到達集合尾。 使用itr.next()(是在C++中使用操作符++ 和*操作的結合)來獲得下一個元素。
ArrayList myArrayList = new ArrayList();
Iterator itr = myArrayList.iterator();
while ( itr.hasNext() )
{
System.out.println( itr.next() );
}
// 或, 在Java 5中
ArrayList myArrayList = new ArrayList();
for( Object o : myArrayList ) {
System.out.println( o );
}
抽象類
C++
// 只需要包含一個純虛函數
class Bar { public: virtual void foo() = 0; };
Java
// 語法上允許顯示的聲明!
abstract class Bar { public abstract void foo(); }
// 或者你也可以聲明一個接口
interface Bar { public void foo(); }
// 然后讓一個類繼承這個接口:
class Chocolate implements Bar
{
public void foo() { /* do something */ }
}
引用 vs 指針
C++
//引用不可改變,通過使用指針來獲得更多的靈活性
int bar = 7, qux = 6;
int& foo = bar;
Java
// 引用是可變的,僅存儲對象地址;
//沒有指針類型
myClass x;
x.foo(); // error, x is a null ``pointer''
// 注意你要總是用 . 來訪問域,也就是說不會出現 ->
注 釋
兩種語言是一樣的 (// 和 /* */ 可以用)