比较C#和Java
本文對(duì)比C#與Java編程語(yǔ)言。 因?yàn)檫@兩種語(yǔ)言都具有自動(dòng)垃圾回收以及運(yùn)行時(shí)編譯執(zhí)行的特點(diǎn),并且他們的語(yǔ)法都是繼承自C語(yǔ)言/C++,因此二者有很多相似之處。
但由于C#也被描述為一個(gè)C++和Java的混合體,并添加了一些新特性,引入了一些變化,因此C#和Java自然也有很多不一樣的地方。
這個(gè)條目描述了二者總體上的相似性,并列舉了二者的不同點(diǎn)。
?
目錄
- 1語(yǔ)言
- 1.1對(duì)象處理
- 1.1.1引用
- 1.2數(shù)據(jù)類型
- 1.2.1數(shù)組
- 1.2.2內(nèi)部類
- 1.2.3部分類
- 1.2.4泛型
- 1.3符號(hào)和特殊功能
- 1.3.1特殊功能關(guān)鍵字
- 1.3.2回調(diào)和事件處理
- 1.4數(shù)值應(yīng)用
- 1.4.1運(yùn)算符重載
- 1.5方法
- 1.5.1顯式接口實(shí)現(xiàn)
- 1.5.2開(kāi)包
- 1.5.3Lambdas和表達(dá)樹(shù)
- 1.5.4部分方法
- 1.5.5擴(kuò)展方法
- 1.5.6發(fā)生器方法
- 1.6條件編譯
- 1.7名字空間和源文件
- 1.8異常處理
- 1.8.1Finally塊和未捕捉的異常
- 1.9底層的代碼
- 1.1對(duì)象處理
- 2參考文獻(xiàn)
- 3外部鏈接
語(yǔ)言[編輯]
對(duì)象處理[編輯]
C#和Java都被設(shè)計(jì)成一個(gè)使用動(dòng)態(tài)調(diào)度的類似于C++語(yǔ)法的完全的面向?qū)ο笳Z(yǔ)言。(C++又是源自于C)。但是,這兩種語(yǔ)言都不是c或者c++的一個(gè)擴(kuò)展集。C#和Java都使用垃圾回收作為一種回收內(nèi)存資源的手段,而不是直接的釋放內(nèi)存。C#和Java都包含線程同步機(jī)制作為他們語(yǔ)法的一部分。
引用[編輯]
C#允許指針的有限功能的使用,指針和運(yùn)算指針在一個(gè)操作的環(huán)境中是存在潛在的不安全性的,因?yàn)樗麄兊氖褂每梢员荛_(kāi)對(duì)象的一些嚴(yán)格訪問(wèn)規(guī)則。C#中使用指針的代碼段或者方法的地址要用unsafe關(guān)鍵字進(jìn)行標(biāo)記,這樣,這些代碼的使用者就會(huì)知道這個(gè)代碼相比其他的代碼而言是不具有安全性的。編譯器需要unsafe關(guān)鍵字時(shí)將使用此代碼的程序轉(zhuǎn)換成是允許被編譯的。一般來(lái)說(shuō),不安全代碼的使用可能是為了非托管的API(應(yīng)用程序編程接口)的更好互用,或者是為了(存在內(nèi)在不安全性的)系統(tǒng)調(diào)用,也有可能是出于提高性能等方面的原因。而Java中不允許指針或者算術(shù)指針的使用。
數(shù)據(jù)類型[編輯]
java和C#語(yǔ)言都有原始數(shù)據(jù)類型的概念,C#/.NET語(yǔ)言中支持的原始數(shù)據(jù)(所有的,除了string類型)都是值類型。但C#比java支持更多的原始數(shù)據(jù)類型,比如整型和十進(jìn)制浮點(diǎn)數(shù),尤其是java缺少無(wú)符號(hào)的BYTE類型,而C#的BYTE類型默認(rèn)是無(wú)符號(hào)的。在兩種語(yǔ)言中string其值都是不可改變的一個(gè)類,但是特殊的是C#為其提供了特殊的構(gòu)造方法,同時(shí)C#還可以像值類型一樣的使用string的值就而不需要進(jìn)行拆箱操作。 既允許自動(dòng)裝箱和拆箱,把它們從對(duì)象類型轉(zhuǎn)換為原始數(shù)據(jù)。實(shí)際上,這使得原始類型成為對(duì)象類型的子類型。在C#中這也意味著,原始類型可以定義方法,如覆蓋的對(duì)象的ToString()的方法。在Java中,單獨(dú)的原始包裝類提供這種功能。在Java中原始值不含隱式裝箱和一個(gè)顯示的類型轉(zhuǎn)換都需要一個(gè)實(shí)例稱為原始值的((Integer)42).toString()而不是C#中調(diào)用實(shí)例 42.ToString()。另一個(gè)不同之處在于,java使大量使用裝箱類型(見(jiàn)下文),這樣可以讓一個(gè)隱式拆箱轉(zhuǎn)換(在C #這需要一個(gè)類型轉(zhuǎn)換)。由于這些隱性拆箱轉(zhuǎn)換可能會(huì)拋出空指針例外,現(xiàn)代集成開(kāi)發(fā)環(huán)境和編譯器可以配置為突出它們。 值類型 C#允許程序員用關(guān)鍵字struct創(chuàng)建用戶自定義的值類型(value type)。 從程序員的角度來(lái)講,它們可以被看做輕量級(jí)的類。
不同于一般類,而像標(biāo)準(zhǔn)基本類,這種值類型被分配在棧內(nèi)存(stack)而不是堆內(nèi)存(heap)。 結(jié)構(gòu)體通常有一系列的限制,因?yàn)榻Y(jié)構(gòu)體沒(méi)有空值的概念并且可以在數(shù)組中無(wú)需初始化而直接使用,這種類型也有必須用0來(lái)初始化內(nèi)存空間的默認(rèn)構(gòu)造函數(shù)。 程序員只能定義另外的帶有一個(gè)或多個(gè)參數(shù)的構(gòu)造函數(shù)。
這也意味著結(jié)構(gòu)體缺少一個(gè)虛方法表,正因?yàn)檫@樣(還有固定的內(nèi)存空間),它們不允許繼承(但可以實(shí)現(xiàn)接口)。
數(shù)組[編輯]
數(shù)組和集合類同樣在語(yǔ)法中給出了重要意義,感謝基于迭代器的預(yù)聲明循環(huán)。在C#里一個(gè)數(shù)組反映為一個(gè)數(shù)組類的對(duì)象,而在JAVA每個(gè)數(shù)組都是一個(gè)直接的對(duì)象集的子集(但是可以映射為一個(gè)以它真正的成員類為父類的一個(gè)數(shù)組),并且不實(shí)現(xiàn)任何的集合界面。C#擁有真正的多維數(shù)組,如同Java中可用到的數(shù)組的數(shù)組(在C#中通常稱為鋸齒數(shù)組)。多維數(shù)組可以因?yàn)樵鰪?qiáng)位置(就像有一個(gè)單一的指示器解除參照,代替數(shù)組的每一維作為鋸齒數(shù)組的容器)在某些情況下增強(qiáng)性能。另一個(gè)優(yōu)點(diǎn)是整個(gè)多維數(shù)組可以用單一的new操作符申請(qǐng)而賦值,而鋸齒數(shù)組需要對(duì)每一維進(jìn)行循環(huán)和賦值。注意,盡管Java為分配多維的鋸齒數(shù)組提供依據(jù)句法的整齊的數(shù)組長(zhǎng)度(在C#術(shù)語(yǔ)中是一個(gè)矩形數(shù)組),循環(huán)和多樣的分配被虛擬機(jī)完成不需要外在的來(lái)源。
內(nèi)部類[編輯]
java與C#都允許設(shè)置內(nèi)部類,即在一個(gè)類內(nèi)部定義的另一個(gè)類。在java中,這些內(nèi)部類可以訪問(wèn)外部類的靜態(tài)和非靜態(tài)成員(除非這個(gè)內(nèi)部類定義為靜態(tài)的,在這種情況下只能訪問(wèn)外部類的靜態(tài)成員)。局部?jī)?nèi)部類可以定義在一個(gè)方法中并訪問(wèn)這個(gè)方法中聲明為final類型的局部變量,匿名局部類允許構(gòu)造類的實(shí)例用來(lái)重寫(xiě)類的方法。
C#也提供內(nèi)部類,與Java不同的是它需要外部類的非靜態(tài)成員的一個(gè)明確引用。同時(shí)C#提供匿名類作為一個(gè)結(jié)構(gòu)用來(lái)訪問(wèn)局部變量和方法(參見(jiàn)事件處理)。局部類和匿名類不能被訪問(wèn)。
部分類[編輯]
C#使用部分類允許一個(gè)類的定義分割在幾個(gè)源文件中。每一個(gè)部分必須用關(guān)鍵字partial標(biāo)記。作為一個(gè)單一的匯編的部分所有的部分都必須提交給編譯器。每個(gè)部分可以引用其它部分的成員。每個(gè)部分都可以實(shí)現(xiàn)接口,并且某個(gè)部分可以定義一個(gè)基類。這個(gè)功能在代碼生成時(shí)非常有用,也就是一個(gè)代碼發(fā)生器提供一部分代碼,開(kāi)發(fā)商提供另一部分代碼,兩種代碼在一起編譯。因此開(kāi)發(fā)商可以編輯他們的部分代碼而不用冒著代碼發(fā)生器在以后覆蓋這部分代碼的危險(xiǎn)。和類擴(kuò)展機(jī)制不同,部分類在它的部分之間允許循環(huán)依賴,因?yàn)樗鼈冊(cè)诰幾g的時(shí)候都保證被解決。Java沒(méi)有類似的概念。
泛型[編輯]
泛型編程
現(xiàn)在的編程語(yǔ)言都支持泛型編程,但它們卻采用了不同的實(shí)現(xiàn)方式。
Java中的泛型僅是語(yǔ)言層面上的一種結(jié)構(gòu),它們只能通過(guò)編譯器來(lái)實(shí)現(xiàn)。生成的類文件中所包含的類簽名僅由元數(shù)據(jù)組成(允許編譯器對(duì)這些新類進(jìn)行反編譯)。運(yùn)行時(shí)并不知道通用類型系統(tǒng),這意味著JVM只需要進(jìn)行一小部分的更新便可處理新的類格式。
為了實(shí)現(xiàn)這個(gè)目標(biāo),編譯器用泛型類型的上界來(lái)替換它們,并且在用到這些泛型的各個(gè)地方適當(dāng)?shù)夭迦胍恍敖巧薄=Y(jié)果生成的字節(jié)碼將不包含任何對(duì)這些泛型類型的引用或?qū)⑺鼈冏鳛閰?shù)。這種實(shí)現(xiàn)泛型的技術(shù)被稱作類型擦除。這意味著實(shí)際上的類型的信息在運(yùn)行時(shí)不可用,并且強(qiáng)行加入了一些限制,例如不能創(chuàng)建泛型的新實(shí)例或數(shù)組。(參見(jiàn)Java中的泛型)。
C#采用了另一種實(shí)現(xiàn)方式。它對(duì)泛型的支持是集成在虛擬執(zhí)行系統(tǒng)中的,并且最早出現(xiàn)在.NET2.0中。這門語(yǔ)言后來(lái)就發(fā)展為在執(zhí)行系統(tǒng)中支持基本泛型的前端。而在Java中,編譯器提供了靜態(tài)類型安全檢查,但是,加之又有即時(shí)編譯器(JIT)加載來(lái)核實(shí)其正確性。關(guān)于泛型類型的信息在運(yùn)行時(shí)完全被保護(hù)起來(lái)了,并且允許完全的反射和實(shí)例化泛型類型。
Java不允許用基本數(shù)據(jù)類型來(lái)聲明為泛型類,然而C#卻允許不管是引用類型還是值類型被聲明為泛型,包括基本數(shù)據(jù)類型。Java卻允許被封裝的類型作為泛型類的類型參數(shù)來(lái)使用(例如:用List<Integer>代替List<int>),但是由于所有這一類的值需要在堆上分配而需付出一定的“代價(jià)”。 在Java和C#兩者中,泛型的定義都使用了不同的引用類型來(lái)分享等效的底層代碼,但是對(duì)C#來(lái)說(shuō)公共語(yǔ)言運(yùn)行時(shí)(CLR)為值類型的實(shí)例化動(dòng)態(tài)的生成優(yōu)化代碼。
符號(hào)和特殊功能[編輯]
特殊功能關(guān)鍵字[編輯]
| checked,?unchecked | 在C#里,?checked?聲明塊或表達(dá)式可以在運(yùn)行時(shí)檢查算術(shù)的溢出。 |
| get,?set | C#實(shí)現(xiàn)屬性作為語(yǔ)言語(yǔ)法的一部分,而且選用相應(yīng)的get?和set?訪問(wèn)器, 而Java的訪問(wèn)方法, 不是一種語(yǔ)言功能,而是基于方法命名公約的編碼方式。 |
| goto | C#中支持goto關(guān)鍵字。goto有時(shí)候是有用的, 舉個(gè)例子,實(shí)現(xiàn)有限的狀態(tài)機(jī)或者生成的代碼, 但是通常建議使用更加合理控制流程的結(jié)構(gòu)化方法(見(jiàn)goto語(yǔ)句的評(píng)論)。 Java 允許使用breaks和continues彌補(bǔ)了goto語(yǔ)句的的許多用途。 switch(color) {case Color.Blue:Console.WriteLine("Color is blue"); break;case Color.DarkBlue:Console.WriteLine("Color is dark");goto case Color.Blue;// ... } |
| out,?ref | C#支持輸出參數(shù)和引用參數(shù)。這使得c#可以從一個(gè)方法返回多個(gè)值或者通過(guò)引用傳遞多個(gè)值。 |
| strictfp | Java 使用關(guān)鍵字?strictfp?確保跨平臺(tái)時(shí)浮點(diǎn)運(yùn)算的結(jié)果保持不變。 |
| switch | 在C#里, switch 語(yǔ)句也操作于string型和long型,但是只允許失敗的空白語(yǔ)句。 Java switch 語(yǔ)句在Java7之后才支援操作strings;不能操作于long?的原始類型 但是能通過(guò)所有的空白語(yǔ)句(不包括那些含有 'break'的語(yǔ)句)。 |
| throws | Java中要求每個(gè)方法都要聲明它能拋出檢測(cè)異常或者檢測(cè)異常的父類。任何方法也可以隨意的定義它所拋出的非檢測(cè)異常,C#中卻沒(méi)有這樣的語(yǔ)法規(guī)則。 public int readItem() throws java.io.IOException {// ... } |
| using | C#中的using指令使得對(duì)象的Dispose方法(通過(guò)IDisposable接口被執(zhí)行)定義為在代碼塊執(zhí)行之后或者在代碼塊之中的異常被拋出時(shí)才被執(zhí)行。 //創(chuàng)建一個(gè)小文件"test.txt",寫(xiě)一個(gè)字符串, //... 并且把它關(guān)閉(即使發(fā)生了異常) using (StreamWriter file = new StreamWriter("test.txt")) {file.Write("test"); } |
| yield | C#語(yǔ)言中允許使用yield關(guān)鍵字來(lái)表示迭代器。在Java中,迭代器只能用類(可以是匿名的)來(lái)定義,且需要很多的樣板代碼。下面是一個(gè)能夠讀取可迭代的輸入(可以是數(shù)組)并且返回所有偶數(shù)成員的迭代器的例子。 public static IEnumerable<int> GetEven(IEnumerable<int> numbers) {foreach (int i in numbers){if (i % 2 == 0)yield return i;} } |
回調(diào)和事件處理[編輯]
數(shù)值應(yīng)用[編輯]
多種語(yǔ)言特色的存在是為了充分的支持應(yīng)用程序在數(shù)學(xué)和金融領(lǐng)域計(jì)算。[1]在這一類中,Java提供關(guān)鍵字strictfp可以在代碼段中使浮點(diǎn)運(yùn)算嚴(yán)格執(zhí)行。這可以保證運(yùn)算在所有的平臺(tái)上都返回相同精確的結(jié)果。 與此不同C#為確保十進(jìn)制小數(shù)浮點(diǎn)運(yùn)算準(zhǔn)確,在?decimal?類型中內(nèi)嵌了這種機(jī)制。但在二進(jìn)制小數(shù)浮點(diǎn)運(yùn)算中舍棄了這種機(jī)制(float,?double)。 在二進(jìn)制所有的類型中描述十進(jìn)制數(shù)因?yàn)椴痪_會(huì)存在舍入誤差。所以在金融應(yīng)用方面十進(jìn)制小數(shù)類型的精確顯得很重要。 Java中BigDecimal類也提供了這些特性。任意精度小數(shù)算法 (BigDecimal) 和任意精度整數(shù)算法 (BigInteger?) 的類為其提供任意精度的數(shù)值運(yùn)算。 盡管有第三方實(shí)現(xiàn)了這些類,但是.NET框架(3.5)的現(xiàn)行版本當(dāng)前并沒(méi)有提供這些。(參見(jiàn)Arbitrary-precision arithmetic) Java不能為庫(kù)定義類型(高精度小數(shù)、復(fù)數(shù)等原始類型)提供一個(gè)統(tǒng)一標(biāo)準(zhǔn),為了達(dá)到這個(gè)目的,C#提供了如下內(nèi)容:
- 能夠提供方便語(yǔ)法的運(yùn)算符重載和索引(看下面)。
- 隱性和顯性轉(zhuǎn)換;允許諸如嵌入式int 類型隱性轉(zhuǎn)換為long類型的存在。
- 值類型和基于值類型的屬性;在Java中每個(gè)常規(guī)類型必須被存放在堆棧中,它對(duì)常規(guī)類型和存儲(chǔ)類型的性能是不利的。
除此之外,C#能用checked和unchecked運(yùn)算符幫助數(shù)學(xué)計(jì)算,當(dāng)在一段代碼中出現(xiàn)算數(shù)溢出時(shí)它能夠檢測(cè)出是否能夠繼續(xù)運(yùn)行。它也提供在內(nèi)嵌數(shù)組的某些應(yīng)用方面有優(yōu)勢(shì)的矩陣。[1]
運(yùn)算符重載[編輯]
相比Java,C#包含了許多可數(shù)的便利。其中,例如運(yùn)算符重載、用戶自定義類型,許多都被大批的C++程序員所熟悉。 它還具有“外在的成員實(shí)現(xiàn)”,這樣可以讓一個(gè)類明確的實(shí)現(xiàn)一個(gè)接口中的方法,與自己類中的方法分離。或者為分別來(lái)自兩個(gè)接口中,具有相同函數(shù)名和簽名的函數(shù)提供不同的實(shí)現(xiàn)。 C#包含了“索引器”,它可以當(dāng)作是一種特殊的運(yùn)算符(像C++中的operator[]),或者是用?get/set?訪問(wèn)器來(lái)訪問(wèn)類屬性。一個(gè)索引器用this[]來(lái)標(biāo)明, 并且需要至少一個(gè)索引參數(shù),該參數(shù)可以為任意類別:
myList[4] = 5; string name = xmlNode.Attributes["name"]; orders = customerMap[theCustomer];Java沒(méi)有提供運(yùn)算符重載是為了阻止特征濫用,還有為了語(yǔ)言的簡(jiǎn)單。[2]?C#允許運(yùn)算符重載(以確定的幾個(gè)限制來(lái)確保邏輯上的一致為條件),如果小心地使用,可以使代碼更加簡(jiǎn)潔和易讀。
?
方法[編輯]
在C#中,方法在默認(rèn)狀態(tài)下是非虛擬的,如果希望得到一個(gè)虛方法則必須明確地用 virtual 修飾符進(jìn)行聲明,而在Java當(dāng)中,所有非靜態(tài)、非私有的方法都是虛方法。虛方法保證被調(diào)用的總是該方法最近被重寫(xiě)的那個(gè)實(shí)現(xiàn)。但是,由于各個(gè)重載方法之間不能被正常地進(jìn)行內(nèi)聯(lián),而使得在方法調(diào)用上需要花費(fèi)一個(gè)相當(dāng)長(zhǎng)的運(yùn)行時(shí)間,并且需要通過(guò)虛方法列表進(jìn)行間接的調(diào)用。然而,包括Sun公司所推薦的實(shí)現(xiàn)方法在內(nèi)的一些Java虛擬機(jī)的實(shí)現(xiàn)方法,則會(huì)對(duì)最普遍被調(diào)用的那些虛方法執(zhí)行內(nèi)聯(lián)。在java中,方法在默認(rèn)狀態(tài)下是虛擬的。(盡管他們能通過(guò)使用“final“修飾符來(lái)密封以使他不允許被覆蓋)。沒(méi)有什么辦法讓subclass或derived class以同樣的名字定義一個(gè)新的、無(wú)關(guān)聯(lián)的方法。 這就會(huì)產(chǎn)生一個(gè)問(wèn)題,即當(dāng)一個(gè)基類由一個(gè)不同的人定義,這時(shí)就有可能出現(xiàn)一個(gè)與派生類中已經(jīng)定義過(guò)的一些方法有著相同的名字和標(biāo)簽的新的版本的方法定義。在Java中,這種情況將意味著派生類中的同名方法會(huì)隱式的重寫(xiě)基類中的方法,盡管這種結(jié)果不是所有設(shè)計(jì)者的真正意圖。為了防止這種版本問(wèn)題,C#中要求將派生類中需要重寫(xiě)虛方法的部分進(jìn)行顯示的聲明。 如果一個(gè)方法需要被重寫(xiě),那么必須指定override修飾符。如果不希望進(jìn)行方法重寫(xiě),而且類的設(shè)計(jì)者僅僅希望引出一個(gè)新的方法以影射舊的方法,那么就必須含有new關(guān)鍵字。New關(guān)鍵字和override關(guān)鍵字也避免了由于基類中的protecte方法或public方法在它的某一個(gè)派生類中被使用時(shí)所帶來(lái)的問(wèn)題。Java中重新編譯將導(dǎo)致編譯器把派生類中的這種方法當(dāng)做是基類方法的重寫(xiě),而這可能并不是基類的開(kāi)發(fā)者想要的。
而C#編譯器將會(huì)把這種方法默認(rèn)為new關(guān)鍵字已經(jīng)被指定,但仍會(huì)對(duì)這種結(jié)果發(fā)出警告。為了部分地容納這些版本問(wèn)題, Java 5.0中引入了@override注釋,但為了保護(hù)它的向后兼容這種做法不會(huì)被當(dāng)作是強(qiáng)制性的,所以它并不能阻止上述意外的重寫(xiě)情況。然而對(duì)于C#中的override關(guān)鍵字,它能有助于確保基類中具有相同簽名的方法仍然存在,并且能被正確的重寫(xiě)。
顯式接口實(shí)現(xiàn)[編輯]
如果在多個(gè)接口中有一個(gè)方法(或C #中的屬性)具有相同名稱和簽名,當(dāng)一個(gè)類在實(shí)現(xiàn)這些接口時(shí)這些重名的成員就會(huì)產(chǎn)生沖突。一個(gè)解決方法是通過(guò)為所有接口實(shí)現(xiàn)一個(gè)默認(rèn)共同的方法。如果必須要分開(kāi)來(lái)實(shí)現(xiàn)(因?yàn)檫@個(gè)方法確實(shí)要實(shí)現(xiàn)某個(gè)特殊的目的,或者是因?yàn)楦鱾€(gè)接口的返回值不一樣)。C#顯示接口的實(shí)現(xiàn)將解決這一問(wèn)題。在java中消除命名沖突的問(wèn)題只能通過(guò)重構(gòu)或者是定義更多的接口來(lái)避免。C#的顯示接口實(shí)現(xiàn)還能隱藏底層基礎(chǔ)的類和接口,因此使得減少類和接口的復(fù)雜性。
開(kāi)包[編輯]
當(dāng)一個(gè)函數(shù)作為一個(gè)參數(shù)來(lái)傳遞并為后面的程序調(diào)用,這時(shí)候會(huì)出現(xiàn)一個(gè)問(wèn)題:當(dāng)這個(gè)方法調(diào)用了它自己作用域內(nèi)的變量時(shí)會(huì)怎樣呢?C#中有真正的開(kāi)包功能,方法的引用會(huì)完全的獲得它自己作用域范圍內(nèi)的變量。Java中,匿名內(nèi)部類只能調(diào)用到作用域內(nèi)的常方法,想要調(diào)用和更新內(nèi)部類的話,就必須通過(guò)開(kāi)發(fā)人員的手工聲明額外的間接的父類來(lái)實(shí)現(xiàn)。
Lambdas和表達(dá)樹(shù)[編輯]
C#中的一個(gè)特殊類型稱" lambdas"。 他們不是方法也不可能構(gòu)成類接口的部分; 他們只是在功能模塊中。 在lambda函數(shù)頂部可以定義的一個(gè)詳細(xì)結(jié)構(gòu)體稱為表達(dá)樹(shù)。 不管他們是被當(dāng)成執(zhí)行函數(shù)還是數(shù)據(jù)結(jié)構(gòu)都起決于編輯器類型,并且不管什么類型變量或參量都要賦值。 Lambdas和表示樹(shù)在LINQ中都是重要角色。 Java中沒(méi)有以lambdas或表達(dá)樹(shù)為特色的; 它的主要機(jī)制和方法定義是匿名內(nèi)部類句法。
部分方法[編輯]
與"部分類"相關(guān) C#允許部分方法在部分類之內(nèi)指定。 一個(gè)部分方法是方法的一個(gè)故意聲明并且在簽名上有一定的約束。 這些約束指定,如果任何類成員沒(méi)有被定義,那么可以安全地刪除。 這個(gè)特點(diǎn)允許代碼提供大量的監(jiān)聽(tīng)點(diǎn)(像GoF設(shè)計(jì)模式中的"模板方法")而不用花費(fèi)多余時(shí)間,如果另一個(gè)類成員在編譯時(shí)沒(méi)有引用它們。而 Java沒(méi)有對(duì)應(yīng)的概念。
?
擴(kuò)展方法[編輯]
用一個(gè)特殊的this指定在一個(gè)方法的第一個(gè)參數(shù)C#允許這個(gè)方法扮演成第一個(gè)參數(shù)類型的一個(gè)成員函數(shù)。這個(gè)外來(lái)類的“擴(kuò)展”是完全句法的。這個(gè)擴(kuò)展方法需要變?yōu)殪o態(tài)的,而且定義在一個(gè)完全的靜態(tài)類中。它必須服從在外部靜態(tài)方法上的任何限定,因此它不能摧毀對(duì)象封裝。這個(gè)“擴(kuò)展”僅僅是在靜態(tài)宿主類的命名空間被引進(jìn)的范圍內(nèi)是活躍的。在java里面,相同的效果可以通過(guò)一個(gè)另一個(gè)類的一般方法得到,但語(yǔ)法將是一個(gè)函數(shù)調(diào)用,而不是方法調(diào)用類的C#語(yǔ)法擴(kuò)展。
發(fā)生器方法[編輯]
發(fā)生器方法是一個(gè)C#方法 ,這個(gè)方法被聲明為返回IEnumerable,IEnumerator接口或者這些接口的一般版本,該方法可以用?yield語(yǔ)法實(shí)現(xiàn)。它是一個(gè)無(wú)限的表現(xiàn)形式, 編譯器生成的補(bǔ)遺集,可大大減少所需的代碼遍歷或生成序列;雖然代碼只是通過(guò)編譯器生成。這個(gè)特征過(guò)去也經(jīng)常被用作實(shí)現(xiàn)無(wú)窮大的序列,就像斐波那契數(shù)列。java是沒(méi)有相應(yīng)的概念。
條件編譯[編輯]
與Java不同,C#使用預(yù)編譯指令實(shí)現(xiàn)了條件編譯的功能。它還提供了條件屬性,使方法只有在定義了編譯常量的時(shí)候才被執(zhí)行。這樣一來(lái),只有在定義了DEBUG常量時(shí),Debug.Assert()方法才會(huì)執(zhí)行,斷言成為了framework的特色。從1.4版本開(kāi)始,Java開(kāi)始提供斷言,默認(rèn)情況下在運(yùn)行時(shí)被關(guān)閉,但也可以在調(diào)用JVM時(shí)使用 "-enableassertions" 或者 "-ea" 打開(kāi)。
名字空間和源文件[編輯]
C#的命名空間和C++類似,但不同于Java的包機(jī)制,C#命名空間不會(huì)以任何方式依賴于源文件的位置,這與Java不同,Java的常規(guī)結(jié)構(gòu)要求源文件的位置必須和包目錄結(jié)構(gòu)相符。 這兩種語(yǔ)言都允許引入類庫(kù)( 例:import java.util.* ,Java方式),在引入類庫(kù)后,使用類時(shí)就可以直接通過(guò)類名引用。不同名字空間或包中可以具有相同名字的類,這樣類在使用時(shí)可以通過(guò)全限定名來(lái)引用,或者通過(guò)不同的名字只引入必要的類。基于這個(gè)問(wèn)題,Java允許引入單個(gè)類(例:import java.util.List)。C#允許在引入類庫(kù)時(shí) 使用語(yǔ)句: using Console = System.Console來(lái)為一個(gè)類庫(kù)定義一個(gè)新名,它同樣允許以u(píng)sing IntList = System.Collections.Generic.List<int>的形式,引入特殊類庫(kù)。
Java有允許使用某些或所有,具有較短名字的靜態(tài)方法/領(lǐng)域的靜態(tài)import句法在類中(例如,允許foo(bar)可以從另一個(gè)類中被靜態(tài)的引進(jìn)).C#有靜態(tài)類句法(不與Java的靜態(tài)內(nèi)在類混淆),制約類只包含靜態(tài)方法。 C# 3.0介紹的引申方法允許用戶靜態(tài)地增加方法到類型(比如,允許foo.bar 的地方可以是研究foo的種類的一個(gè)引進(jìn)的引申方法)。
Sun Microsystems?軟件公司的Java編譯器要求,源文件的文件名必須匹配在它里面的唯一的公開(kāi)類,而C#允許在同一個(gè)文件的多公開(kāi)類,并且投入制約。 C# 2.0和以后的版本允許類定義被分割成幾個(gè)文件,通過(guò)使用在原始代碼的關(guān)鍵字partial。
異常處理[編輯]
Java支持檢查異常(checked exception)。C#中只支持非檢查異常情況。檢查異常強(qiáng)制程序員要么在方法中聲明一個(gè)異常拋出,要么用try-catch來(lái)捕獲異常。檢查異常可以有助于良好的編程習(xí)慣,以確保所有的錯(cuò)誤都得到處理。但是Anders Hejlsberg,C#語(yǔ)言首席設(shè)計(jì)師,和其他人爭(zhēng)辯說(shuō),他們都在一定程度上對(duì)Java進(jìn)行了拓展但是它們沒(méi)有被證明是有價(jià)值的除了幾個(gè)程序中的小例子。有一個(gè)評(píng)論介紹在檢查異常時(shí)鼓勵(lì)程序員使用空的catch塊,安靜的吃掉異常而不是讓異常傳播到更高水平的常規(guī)的異常處理:catch (Exception e) {}.另一種對(duì)于檢查異常的評(píng)論說(shuō)一個(gè)新方法的執(zhí)行可能會(huì)引起意想不到的檢查異常被拋出,這是一個(gè)合同突破性變化.這可能發(fā)生在一個(gè)方法實(shí)現(xiàn)一個(gè)接口或者當(dāng)一個(gè)方法的基本實(shí)現(xiàn)改變時(shí),此接口僅聲明有限的異常。為這種意料之外的的異常被拋出,一些程序員簡(jiǎn)單的聲明這種方法能拋出任何類型的異常(“拋出異常”),這使檢查異常的目的無(wú)法實(shí)現(xiàn)。不過(guò)在某些情況下,異常鏈(exception chaining)能用于代替,捕獲異常后再拋出一個(gè)異常異常.例如,如果一個(gè)對(duì)象訪問(wèn)數(shù)據(jù)庫(kù)而不是文件時(shí)被改變,那么可以捕獲?SQLException異常并且作為IOException異常重新拋出. 因?yàn)檎{(diào)用者也許并不需要知道對(duì)象內(nèi)部的工作方式。
在處理try-finally的聲明時(shí)兩種語(yǔ)言也是有差別的。即使try塊包含像throw和return的control-passing語(yǔ)句,finally塊也總是要執(zhí)行。在Java中,這可能導(dǎo)致意外的行為,如果try塊最后有return語(yǔ)句返回一個(gè)值,然后執(zhí)行后的finally塊也會(huì)有return語(yǔ)句返回一個(gè)不同的值。 C#利用禁止任何像return或者break的control-passing語(yǔ)句來(lái)解決這一問(wèn)題。 使用try-finally 塊的普遍原因是為了保護(hù)管理代碼的資源,所以珍貴的資源被保證在finally 塊中發(fā)布。作為句法速記為共同的設(shè)想的using語(yǔ)句在C#中處于顯著地位,其中using的對(duì)象的Dispose()方法總是被調(diào)用。
Finally塊和未捕捉的異常[編輯]
(C# 派生的異常特點(diǎn))對(duì)CLI(公共語(yǔ)言基礎(chǔ))的ECMA(歐洲電腦廠商協(xié)會(huì))標(biāo)準(zhǔn)指出在堆棧的兩次搜索中處理異常。ECMA-355 4th Edition 12.4.2.5 Overview of exception handling首次通過(guò)嘗試找到一個(gè)匹配的 catch 塊,如果沒(méi)有找到就終止該程序。只有當(dāng)找到匹配的 catch 塊時(shí),才會(huì)在第二步執(zhí)行,從而運(yùn)行干預(yù)的finally塊。這使得問(wèn)題在程序狀態(tài)還沒(méi)第一次被finally塊修改前被診斷;它也消除了當(dāng)程序在未知狀態(tài)下,finally塊可能有副作用的風(fēng)險(xiǎn)(例如,外部數(shù)據(jù)的損壞或進(jìn)一步引發(fā)的異常)。
Java語(yǔ)言規(guī)范中指出finally塊中的代碼總會(huì)執(zhí)行即使異常沒(méi)有被捕獲,并且舉出實(shí)例代碼演示期待的結(jié)果。[3]
底層的代碼[編輯]
Java Native Interface?(JNI)的特征是允許Java代碼調(diào)用非Java代碼。然而,JNI要求被調(diào)用的代碼必須遵循Java提供的一些在類型和名稱上的約定。這種方法是為了適應(yīng)Java和其他代碼更好的交互。這些代碼必須是非Java代碼,常常是C或者C++代碼。JNA提供一種更加方便的Java代碼與其他代碼的交互,僅僅需要寫(xiě)一些Java編寫(xiě)的接口代碼,但是性能會(huì)付出一點(diǎn)代價(jià)。
另外,第三方類庫(kù)為JAVA-COM提供橋接,像JACOB?(自由軟件),J-Integra for COM?(專有軟件)
.NET平臺(tái)調(diào)用(P/Invoke)通過(guò)允許從C#調(diào)用微軟稱之為不受托管代碼提供同樣的的功能,通過(guò)元數(shù)據(jù)屬性程序員可以精確的控制如何調(diào)用參數(shù)和結(jié)果,因此可以避免額外編譯代碼的需要。平臺(tái)調(diào)用允許幾乎完全的對(duì)程序的API的訪問(wèn)(像Win32或POSIX)但是限制對(duì)c++類庫(kù)的訪問(wèn)。另外,.NET框架也提供一個(gè).NET-COM網(wǎng)橋,允許對(duì)COM組件的的訪問(wèn)就像是訪問(wèn)本地的.NET組件。
C#中還允許程序員禁用正常類型檢查和CLR中其他的安全保證功能 ,這樣就使得指針變量的使用成為可能。當(dāng)此功能被使用時(shí),程序員必須用unsafe關(guān)鍵字將相應(yīng)的代碼段進(jìn)行標(biāo)記。JNI ,P/Invoke,和“unsafe”的代碼段是相當(dāng)冒險(xiǎn)的部分,它揭露了可能的安全漏洞和應(yīng)用不穩(wěn)定。使用unsafe的一個(gè)優(yōu)勢(shì)是,通過(guò)P/Invoke或JNI運(yùn)行于托管運(yùn)行環(huán)境中的代碼是讓程序員在比較熟悉的C #環(huán)境中繼續(xù)工作以完成某些任務(wù),否則將需要調(diào)用非托管代碼。使用不安全代碼的程序或程序集必須通過(guò)進(jìn)行特殊的轉(zhuǎn)換才能被編譯并且將依此被標(biāo)記。這使得運(yùn)行時(shí)環(huán)境在潛在地執(zhí)行有危險(xiǎn)的代碼前要采取特別的預(yù)防措施。
參考文獻(xiàn)[編輯]
外部鏈接[編輯]
- Contrasting C# and Java Syntax
- Java vs. C# - Code for Code Comparison
- Nine Language Performance Round-up
- Java and C-Sharp Compared
- MSDN:?The C# Programming Language for Java Developers
- Standard ECMA-334 C# Language specification
- Java Language Specification (Sun)
- 31 Differences between C# and Java
from:?https://zh.wikipedia.org/wiki/%E6%AF%94%E8%BC%83C%E2%99%AF%E5%92%8CJava
總結(jié)
- 上一篇: JAVA程序员看C#的精华与糟粕
- 下一篇: c#与java对比