更新:2007 年 11 月
這個主題討論 Java 和 C# 之間在資料表示、配置和記憶體回收方式上某些主要的相似與差異性。
複合資料型別
將類別當做使用欄位、方法和事件之複合資料型別的概念方面,Java 和 C# 都很相似 (類別繼承會在標題為繼承和衍生類別 (C# 與 Java 的比較) 的主題中分開討論)。C# 引入結構的概念,可以當做不支援繼承的已配置堆疊之複合資料型別。在大部分其他方面,結構與類別非常相似。結構提供將相關欄位和方法群組在一起的輕量方法,以便在緊密迴圈和非常要求效能的其他案例中使用。
C# 可讓您建立解構函式方法,此方法的呼叫時機是在類別的執行個體被記憶體回收之前。在 Java 中, finalize 方法可用來包含在物件由記憶體回收之前清除資源的程式碼。在 C# 中,這項功能是由類別解構函式執行。解構函式就像是不含引數和前置字元為波狀符號 (~) 的建構函式。
內建資料型別
C# 可提供 Java 中的所有資料型別,並且額外支援不帶正負號的數字以及新的 128 位元高精度浮點型別。
對於 Java 中的每個基本資料型別,核心類別庫會提供代表此資料型別的包裝函式類別做為 Java 物件。例如,Int32 類別包裝 int 資料型別,而 Double 類別則是包裝 double 資料型別。
另一方面,C# 中的所有基本資料型別就是 System 命名空間中的物件。對於每個資料型別,會提供一個簡短名稱或別名。例如,int 是 System.Int32 的簡短名稱,而 double 則是 System.Double 的簡短形式。
下表中提供了 C# 資料型別及其別名的清單。您可以發現,清單中的前八個可對應到 Java 中的基本型別。但是請注意,Java 的 boolean 在 C# 中是稱為 bool。
簡短名稱 |
.NET 類別 |
型別 |
寬度 |
範圍 (位元) |
|---|---|---|---|---|
byte |
不帶正負號的整數 |
8 |
0 至 255 |
|
sbyte |
帶正負號的整數 |
8 |
-128 至 127 |
|
int |
帶正負號的整數 |
32 |
-2,147,483,648 至 2,147,483,647 |
|
uint |
不帶正負號的整數 |
32 |
0 至 4294967295 |
|
short |
帶正負號的整數 |
16 |
-32,768 至 32,767 |
|
ushort |
不帶正負號的整數 |
16 |
0 至 65535 |
|
long |
帶正負號的整數 |
64 |
-922337203685477508 至 922337203685477507 |
|
ulong |
不帶正負號的整數 |
64 |
0 至 18446744073709551615 |
|
float |
單精度浮點數型別 |
32 |
-3.402823e38 至 3.402823e38 |
|
double |
雙精度浮點數型別 |
64 |
-1.79769313486232e308 至 1.79769313486232e308 |
|
char |
單一 Unicode 字元 |
16 |
用於文字中的 Unicode 符號 |
|
bool |
邏輯布林型別 |
8 |
true 或 false |
|
object |
所有其他型別的基底型別 |
|||
string |
字元的順序 |
|||
decimal |
以 29 位有效數字表示十進位數字的精確小數型別或整數型別 |
128 |
±1.0 × 10e-28 至 ±7.9 × 10e28 |
由於 C# 將所有基本資料型別以物件表示,因此您可以在基本資料型別上呼叫物件方法。例如:
static void Main()
{
int i = 10;
object o = i;
System.Console.WriteLine(o.ToString());
}
這項作業可在自動 Boxing 和 Unboxing 的協助下完成。如需詳細資訊,請參閱 Boxing 和 Unboxing (C# 程式設計手冊)。
常數
Java 和 C# 都能夠宣告在編譯階段指定值並且無法在執行階段變更的變數。Java 會使用 final 欄位修飾詞 (Modifier) 宣告這類變數,而 C# 則會使用 const 關鍵字。除了 const 以外,C# 也提供 readonly 關鍵字以宣告能夠在執行階段指定一次值的變數 (不論是在宣告陳述式或建構函式中)。在初始化後,readonly 變數的值就無法變更。當分開編譯的模組需要共用像是版本號碼的資料時,這種情況下 readonly 變數就很有用。如果使用新的版本號碼更新和重新編譯模組 A,模組 B 就能夠使用這個新常數值進行初始化而不需要重新編譯。
列舉型別
列舉型別 (或列舉) 是用來群組具名常數,作法與在 C 和 C++ 中的用法類似,但是它們無法用於 Java。下列程式碼範例將會定義簡單的 Color 列舉型別。
public enum Color
{
Green, //defaults to 0
Orange, //defaults to 1
Red, //defaults to 2
Blue //defaults to 3
}
整數值也可以指派給列舉型別,如以下列舉宣告所示:
public enum Color2
{
Green = 10,
Orange = 20,
Red = 30,
Blue = 40
}
下列程式碼範例會呼叫 Enum 型別的 GetNames 方法,以顯示列舉型別的可用常數。接著程式碼將指派一個值給列舉型別並顯示該值。
class TestEnums
{
static void Main()
{
System.Console.WriteLine("Possible color choices: ");
//Enum.GetNames returns a string array of named constants for the enum.
foreach(string s in System.Enum.GetNames(typeof(Color)))
{
System.Console.WriteLine(s);
}
Color favorite = Color.Blue;
System.Console.WriteLine("Favorite Color is {0}", favorite);
System.Console.WriteLine("Favorite Color value is {0}", (int) favorite);
}
}
輸出
Possible color choices:
Green
Orange
Red
Blue
Favorite Color is Blue
Favorite Color value is 3
字串
字串型別在 Java 和 C# 中展現類似的行為,其中僅有些微不同。兩者的字串型別是不變的,也就是說字串一旦建立後便無法變更字串值。在兩者的執行個體中,似乎可以使用方法來修改字串的實質內容,但實際上這個方法是建立新字串以供傳回之用,它並不會變更原來的字串。C# 與 Java 會使用不同的方法比較字串值。若要在 Java 中比較字串值,程式開發人員必須在 == 運算子根據預設比較參考型別時,呼叫字串型別上的 equals 方法。在 C# 中,程式開發人員則可以直接使用 == 或 != 運算子來比較字串值。即使字串在 C# 中是參考型別,根據預設,== 和 != 運算子仍會比較字串值,而非參考。
和在 Java 中一樣,C# 程式開發人員不應使用字串型別來串連字串,避免每次串連字串時都建立新字串類別的負荷。程式開發人員可以使用 StringBuilder 類別來代替,此類別在功能上等同於 Java 的 StringBuffer 類別。
字串常值
C# 提供避免在字串常數內使用逸出序列 (例如定位字元是 "\t" 或反斜線字元是 "\") 的能力。若要這樣做,只要在指派字串值之前使用 @ 符號來宣告逐字規範字串即可。下列範例說明如何使用逸出字元以及如何指派字串常值:
static void Main()
{
//Using escaped characters:
string path1 = "\\\\FileShare\\Directory\\file.txt";
System.Console.WriteLine(path1);
//Using String Literals:
string path2 = @"\\FileShare\Directory\file.txt";
System.Console.WriteLine(path2);
}
轉換和轉型
Java 和 C# 在資料型別自動轉換和轉型時都遵守類似的規則。
C# 和 Java 一樣,同時支援隱含的和明確的型別轉換。進行擴展轉換時,此轉換是隱含轉換。例如,以下從 int 到 long 的轉換是隱含轉換,和在 Java 中一樣:
int int1 = 5;
long long1 = int1; //implicit conversion
以下是 .NET Framework 資料型別之間的隱含轉換清單:
來源型別 |
目標型別 |
|---|---|
位元組 |
short、ushort、int、uint、long、ulong、float、double 或 decimal |
Sbyte |
short、int、long、float、double 或 decimal |
整數 |
long、float、double 或 decimal |
Uint |
long、ulong、float、double 或 decimal |
短整數 |
int、long、float、double 或 decimal |
Ushort |
int、uint、long、ulong、float、double 或 decimal |
長整數 |
float、double 或 decimal |
Ulong |
float、double 或 decimal |
浮點數 |
double |
字元 |
ushort、int、uint、long、ulong、float、double 或 decimal |
使用與 Java 相同的語法來轉型想要明確轉換的運算式:
long long2 = 5483;
int int2 = (int)long2; //explicit conversion
下表列出明確轉換。
來源型別 |
目標型別 |
|---|---|
位元組 |
sbyte 或 char |
Sbyte |
byte、ushort、uint、ulong 或 char |
整數 |
sbyte、byte、short、ushort、uint、ulong 或 char |
Uint |
sbyte、byte、short、ushort、int 或 char |
短整數 |
sbyte、byte、ushort、uint、ulong 或 char |
Ushort |
sbyte、byte、short 或 char |
長整數 |
sbyte、byte、short、ushort、int、uint、ulong 或 char |
Ulong |
sbyte、byte、short、ushort、int、uint、long 或 char |
浮點數 |
sbyte、byte、short、ushort、int、uint、long、ulong、char 或 decimal |
Double |
sbyte、byte、short、ushort、int、uint、long、ulong、char、float 或 decimal |
字元 |
sbyte、byte 或 short |
Decimal |
sbyte、byte、short、ushort、int、uint、long、ulong、char、float 或 double |
實值和參考型別
C# 支援兩種變數型別:
實值型別
這些是內建的基本資料型別 (例如 char、int 和 float) 以及結構 (Struct) 宣告的使用者定義型別。
參考型別
類別以及從基本型別建構的其他複雜資料型別。這類型別的變數不包含該型別的執行個體,而只包含執行個體的參考。
如果建立兩個實值型別的變數 i 和 j (如下所示),那麼 i 和 j 彼此之間完全無關聯:
int i = 10;
int j = 20;
它們的記憶體位置並不相同:
.gif)
如果變更其中一個變數的值,另一個變數當然不會受到影響。例如,如果運算式如下所示,那麼變數之間仍然不會有關聯:
int k = i;
也就是說,如果變更 i 的值,k 將會仍然是當初指派給 i 的值。
i = 30;
System.Console.WriteLine(i.ToString()); // 30
System.Console.WriteLine(k.ToString()); // 10
不過,參考型別就完全不同了。例如,您可以如以下所示宣告兩個變數:
Employee ee1 = new Employee();
Employee ee2 = ee1;
現在,由於在 C# 中類別是參考型別,因此 ee1 也稱為 Employee 的參考。上述兩行程式碼中的第一行會在記憶體中建立 Employee 的執行個體,並且將 ee1 設定為參考這個執行個體。因此,當您將 ee2 設為等於 ee1 時,它會包含記憶體中這個類別參考的複本。此時如果變更 ee2 上的屬性,ee1 上的屬性將會反映這些變更,這是因為這兩個都指向記憶體中的同一個物件,如下所示:
.gif)
Boxing 和 Unboxing
將實值型別轉換成參考型別的過程稱為 Boxing。而反向的作法,即將參考型別轉換成實值型別,則稱為 Unboxing。這在下列程式碼中說明:
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
Java 要求您以手動方式執行這類的轉換。基本資料型別便可轉換成包裝函式類別物件,作法是建構這類物件或 boxing。同樣地,可從包裝函式類別物件擷取基本資料型別的值,作法是呼叫這類物件上的方法或 Unboxing。如需 Boxing 和 Unboxing 的詳細資訊,請參閱 Boxing 和 Unboxing (C# 程式設計手冊)。