網上的一篇《C# 和 Java 的比較》(或者叫《Java 和 C# 的比較》)寫的挺不錯的,今天忽然搜索到。
自己剛剛接觸C#,也不由自主地隨時都拿來和Java做對比,所以就心血來潮在原作者的每一條之後斗膽都寫了些文字。就當是給自己再加深一遍印象吧。
【非常抱歉,由於網上此文章已經被轉載多次,所以真的找不到原出處了,所以沒法貼出作者原貼的連接】
開始吧...
2007年11月1日 1。訪問控制方面:C#有public、internal、protected、private,比java多了個internal,其實它跟java的包訪問差不多,internal表示同一個編譯集合(如exe、dll)下的類可以互訪。 對於protected,java和C#有區別。在java中,protected和包訪問級別差不多,即不是私有的。而在C#中,protected和private差不多,即它標志的成員是私有的。 有這樣一種情況:類中的一個成員,需要它能被子類訪問到,同時能被同一個集合中(無論是java的包還是C#中的編譯集合)的其他類訪問到,怎麼辦呢?在java中,只要用protected就行了。在C#中,可以同時指定internal protected(二者的順序隨意)。 在有這樣一種情況:類中的一個成員,需要它能被子類訪問到,但不能被同一個集合中(無論是java的包還是C#中的編譯集合)的其他類訪問到,怎麼辦呢?在C#中,可以指定protected(二者的順序隨意)。但java就無能為力了。
評論:我很喜歡C#的internal,在Java如果一個為API中某些類提供服務的類,為了不對外暴露,只能用包級私有,並和被服務的類放在一個包中,如果有多個包中的類都要被服務,就沒法辦了。而C#的internal就很好的僅限當前“集合”(可能有多個namespace)中的類訪問。
但是,我又不喜歡C#的“集合”的感念。反射的時候還必須給出DLL或EXE的文件名(如果不在當前DLL或EXE中),而Java是不需要的,只有在WEB-INF/classess和lib 下的都可以。特別是當API要反射它的調用者時,作為類庫的DLL怎麼知道哪個EXE在調用自己?
2。C#中有static constructor的概念,這跟java中的靜態初始模塊一樣。 C# : static [類名]{} java :static{}
批注:原來是這樣寫,我找了半天也沒搜到C#中的靜態代碼段如何個寫法,如今得來全不費工夫。
3。Java中的main函數必須是public static void main(String[] args)的樣子,否則虛擬機拒絕運行。C#中,Main函數可以是private的(甚至可以是protected),可以沒有參數,可以返回int值。有點像C語言。 4。發現csc.exe有一個功能很好,100後面加一個小寫的L,它會警告:“l”後綴容易與數字“1”混淆;為清楚起見,請使用“L”。
批注:MS的人性化關懷呀。
5.C#提供了一種機制,使得某個變量可以被動態賦值一次,以後就不能再改了。那就是readonly關鍵字的功能。
批注:這個機制可能在特定場合下很有用。比如:抽簽,這樣你就不能耍賴了。
6.java在繼承、多態方面,比C#強多了。Java默認的多態,C#要求加上virtual(被繼承的方法)和override(繼承的方法),而且C#要求不能改變原來的訪問修飾符,不像java那樣,可以指定更加寬松的訪問方式。如果有人利用C#來寫程序,必須經常帶上virtual和override,還必須照抄原來的訪問控制符,不會很郁悶嗎?難道有人用C#的面向對象特性時,會捨棄多態的特性?這會引起多大的混亂啊。 多態是面向對象的精髓,像java那樣默認不是更好嗎?
批注:C#號稱和Java有90%的相似性,但它必將是從C++發展而來的,在虛函數的概念上還是延續著C++的方式。習慣了Java之後,真的是感覺別扭。Java多好:默認情況下都是虛函數,允許被子類重寫,但是對於有意不想讓子類重寫的方法用final關鍵字來修飾。
7. C#中new還可以用來指定子類的某個方法要隱藏父類的具有相同簽名的方法。這是不是多余的?你不用也可以,不過csc.exe會警告你,如“lan.Other.Main(string[])”隱藏了繼承的成員“lan.HelloWorld.Main(string[])”。如果是有意隱藏,請使用關鍵字 new。 像java那樣默認多好啊。 但是話又說回來,C#這樣做也是有原因的。如果類B繼承了類A,B接下來有添加了一個方法叫做hi(),那是B特有的。然後類A(假設是別人來維護的,你不能看到源碼)突然也增加了一個方法hi()。如果B自己那個hi()跟A那個hi()的返回值不一樣,當你更新類庫A後,可能導致程序運行錯誤或不能編譯。C#就很好就地避免了這種問題。(雖然這種問題出現的概率挺小的…)
批注:其實這個還是和6有關,總之對於Javaer來說就是兩個字“別扭”。那些非虛函數干脆就不允許改寫就得了,像Java的final那樣。干嘛“既要做婊子又要立牌坊”呀,當警告超過一定數量時,估計就掩埋了,不容易被注意到。而真等到了使用的時候,哪一個是多態?哪一個是“另起爐灶”?使用者不暈菜才怪呢!
8.C#中,防止一個類被繼承,要用關鍵字sealed。而定義一個常量時,要用const。 像java統一用final多好啊。
批注:這倒沒什麼,關鍵字都一樣了,顯得人家MS多沒水平嗎。我們只是需要再多記住一些東西就是了,多無奈呀。
9.在C#中,要比較兩個引用變量是否指向同一個對象,不能用java中的= =,而要用Object裡的ReferenceEquals方法。C#中,不能用一個類的實例去調用該類的類方法,必須用類名。所以java中的o1= =o2等價於C#中的Object.ReferenceEquals(o1,o2)。
批注:都是操作符重載惹的禍。Equals就是Equals;ReferenceEquals就是ReferenceEquals。但 == 可要注意了,有的時候是 Equals 有的時候是 ReferenceEquals。== 被重載了,我反而更不敢用了,還是稍微費點事寫 Equals 和 ReferenceEquals 吧,即不會犯錯誤,有意義清晰。
10.C#中沒有原始類型的包裝類,但是也提供自動裝拆箱的功能,和java有的一樣。區別是,C#的裝箱是自動的,拆箱就要強制轉換了。 int i=100; object obj=i; i=(int)obj; 具體怎麼裝和拆,我們不知道。只知道CLR將int轉換成object了。
批注:包裝類還是有的吧?如:int -> Int32;string -> String;甚至 object -> Object。是我的理解有誤嗎?至少從VS中關鍵字高亮的顏色就能區分開。
11.java的內部類有時候幫助很大。到了C#那,就只提供靜態的內部類了。這意味著外部類只相當於是一個命名空間而已。C#中的內部類能訪問外部類的私有成員,這可能會讓它有點用。
批注:這一點我沒什麼研究,在Java中也是盡量不使用嵌套類。其實靜態成員類+內部類(含:非靜態成員類、匿名類、局部類)=嵌套類。如果要用也最好優先使用靜態成員類,內部類最好少碰,尤其是匿名類(但有些地方還必須用它,矛盾呀)。
12.C#中雖然有運算符重載,但是為了整個.net的一致性,應該不會鼓勵使用。因為有的.net語言沒有運算符重載,而.net的一個目標就是消除各種語言的差別。
批注:用慣了Java的人想必也不會要這個“上天所賜”的。僅一個 == 就被MS自己重載成了9中的樣子(其實,人家重載得還是很不錯的,只是咱們在用的時候頭腦不靈光)。你希望像 BigDecimal 這樣的值類重載 + - * / 嗎?又是 仁者見仁,智者見智了吧?
13.C#多了一個struct值類型,它就跟原始類型一樣。微軟在必要的時候會幫你將struct封裝成Object,就像封裝int類型一樣。以至於你可以認為struct也是由Object繼承而來,雖然struct本身並不支持繼承。(struct可以不用new來初始化,但它裡面的內容必須初始化後才能調用其方法;struct沒有析構方法;struct沒有默認的構造方法)。
批注:struct在C#中出現的原因我還真不敢亂猜,從C/C++沿襲而來?還是像MS說的在性能上略微優於class?總之,只要知道在構建值類的時候,可以使用之就可以了。
2007年11月2日 1.java中類的訪問控制符只能是public,或者沒有(即默認的包訪問)。但是C#中,class和interface的訪問控制符可以是public / private / internal / protected / internal protected。當然你必須先取得對類的訪問,才可能訪問到類的成員。
一個C#集合中可以包含多個public的類或接口,跟文件名沒有關系。
批注:MS沒有像Sun那樣給namespace的命名方法一個官方的建議(Sun建議package的命名法為域名的逆序,均為小寫字母)。而且.Net也不要求按目錄及文件名保存類(Java最早也沒有此要求)。哪種方式更好呢?我想還是一個習慣問題,至少我還是覺得Java的方式更清晰明了。而不少.Net的工程,打開後所有的Source文件都在一個“大平層”,很難受的說。
2.C#中的接口不能包含常量,而java可以。
批注:接口中應該/可以包括常量的定義嗎?公說公有理,婆說婆有理。個人認為,即使Java運行,也盡量別這麼做。
3.C#中的as和java中的instanceof功能一樣。但C#提供一個據說是效率更高的as關鍵字。
批注:原作者筆誤吧?是否應該是 is ? as 是強制類型轉換,和更常見的 變量a = (類型)變量b; 不同的是,如果轉換失敗as會返回null,而括號式轉換回拋出異常。as的語法和AS3語言中是相同的。
4.接口和抽象類在C#和java中都差不多,這裡提一下接口設計和抽象類設計的區別之處。如果你更改了一個接口的設計,比如增加了一個方法,使用你以前的代碼的用戶將不得不改變他們的代碼,否則不能運行和編譯。但是如果是一個抽象類,你可以提供一個含默認實現的方法,用戶的代碼則不需要改變。
批注:這個事實在Java和C#中都是一樣的。但是,就接口和抽象類(即使包括骨架類)的選用依據可不是這個,這只是一個表現而已。再有,接口一旦發布了,就是你對外的一種承諾。之後即使是版本升級也不能再做任何改動,哪怕是增加新方法。那非要增加怎麼辦?如果同時提供的骨架類也控制在你的手裡,可以通過在這個骨架類(其實就是抽象類的一種用法,骨架類實現接口,實際類再繼承這個骨架類,骨架類中可以為實際類實現一些通用的、或默認的方法)提供一份新增方法的默認實現來達到目的。但這並不是明智的,因為版本升級時需要增加的新方法,往往是一些實實在在的干活兒的方法,在骨架類中給出一個默認實現往往沒什麼實際意義。更好的做法,是寫一個新的接口去繼承原來的接口,把新增的方法在子接口中聲明。這樣可以保持100%向前兼容,需要實現新方法的類屬於伴隨此次接口升級或日後要取實現的類。
5.C#中一個類實現一個接口時,它的相關方法不必指明override;但一個類繼承一個抽象類的抽象方法時,必須加上override,否則視為隱藏。(事實上,只有抽象方法或者是virtual、或者是接口方法才能被覆蓋即override。不能無緣無故地override。)
批注:這個也沒什麼好說的,各家有各家的寫法,如果非要說也還是習慣的問題。同上半篇的6。
6。C#中存在一個“多態起始點”的問題。如果一個類實現了接口的某個方法,只是接口到該類才有多態的功能,若要這種多態繼承下去,該類必須指明是virtual,多態起始了,接下來的子類提供override就能多態了,不需要更多的virtual。
但是抽象類的抽象方法默認就是一個多態起始點,後續的子類只要override就行了。
批注:感覺還是同上半篇的6。也許是我還沒深刻理解?
7.當一個類實現了兩個接口,兩個接口有一個相同的方法定義,C#有一種解決機制,叫做顯示實現。Java干脆就不處理這種情況,反正實現之後就能調用了,不必指明是哪個接口的,留給程序員自己考慮。 當然C#中的顯示實現還有其他功能。舉個例子,接口A有一個方法叫做f(),類B實現了A。按理說B的實例就能自由調用f()了,但是如果有這樣的要求:B的實例只有被cast成A之後才能調用f()。在java中,這樣無理的要求是不允許的。但是C#可以做到,就是通過顯示實現的方式。有誰會使用這樣的特性?
批注:兩個接口如果定義了相同的方法,方法名、參數個數、參數類型、返回值類型、可能拋出的異常(這個C#真沒有)都一樣的話,那實現它的時候,還區分是來自哪個接口的干嗎?有這個必要嗎?Java在這裡“犯傻”我看挺好。
推薦個文章:從海底撈的火鍋和路邊的煎餅談談用戶體驗
#include<stdio.h>
void main()
{
int a,b,c;
printf("please input 3 nums:\n");
scanf("%d,%d,%d",&a,&b,&c);
printf("1st:%d 2st:%d 3st:%d\n",a,b,c);
}這個每個數輸入的時候要用逗號隔開
#include<stdio.h>
void main()
{
int a,b,c;
printf("please input 3 nums:\n");
scanf("%d %d %d",&a,&b,&c);
printf("1st:%d 2st:%d 3st:%d\n",a,b,c);
}這個要用空格隔開
#include<stdio.h>
void main()
{
int a,b,c;
printf("please input 3 nums:\n");
scanf("%d%d%d",&a,&b,&c);
printf("1st:%d 2st:%d 3st:%d\n",a,b,c);
}這個空格回車都可以
www.cia-china.com/train/infor_2_1.asp
上海大學CIA,c程序設計 游戲程序設計研修班(二年制)
上海大學網站:www.shu.edu.cn
上海大學成教學院網站:www.shu.edu.cn/ad