1. 程式人生 > >挑戰 Java 霸主之位?C# 五個不可替代的特性瞬間秒殺 Java

挑戰 Java 霸主之位?C# 五個不可替代的特性瞬間秒殺 Java

Language 問題 互聯 結構體 項目經驗 dig async 網上商城 exce

完美的編程語言並不存在,相信這一點在眾多開發者的心中毋庸置疑。想要學會一門編程語言就走遍天下,至少目前看來並不現實。一門新語言的誕生往往是為了克服另一種語言的弊端,但在保證新語言健壯性的同時卻似乎又不可避免在另一方面出現不足。

縱觀大小 600 多種編程語言,我們究竟該如何選擇適合自己的開發語言?是根據興趣、工作需求、還是課程的推薦安排?筆者曾是軟件工程專業,學習了 Java、C 語言、C++、C# 等多門編程語言,最終卻也是略懂而不精。事實上,編程語言並非學得越多就越牛,術業有專攻,要掌握好適合自己的開發利器。本文以全世界通用的 C#、Java 兩門基礎編程語言為例,綜合部分經驗豐富的開發者的看法,分享該編程語言的一些特性,希望能給迷茫中的開發者指引一定的方向。


C# 與 Java 的不同之處

對於已有經驗的技術工程師而言,往往不會去糾結這個問題,但是在新入門的開發者看來,C# 與 Java 往往代表的是兩大流派。其實 C# 和 Java 都起源於 C/C++ 語言,它們在面向對象方面有許多相似之處。除了 Java JVM 和 C# .NET CLR 有許多相同結構上的相似性之外,它們各自的開發團隊都有各自的發展方向。

倘若從語言的角度上來講,C# 似乎更勝一籌,因為它易用、門檻低、優雅、較為簡潔,且 C# 出現的時間比 Java 晚,因此自然吸收、借鑒,同時又有它的創新,比如很早就支持 Lamda 表達式、event 和委托、var 及 LINQ。

如果從面向接口來講,Java 同樣可以做到 event 和 listener,只不過對象引用的傳遞比較直接。

如果從應用的角度來看,以 Java 和 C# 為編程語言自成的體系幾乎都涵蓋了主流的開發方向:

桌面、Web、服務端、數據庫、網絡、移動端、中間件。

而兩者最大的不同之處在於:

泛型不一樣,Java 泛型只是編譯時的,但 C# 的泛型在運行時也被維持,而且適用於 value types 和 reference types;
C# 沒有 checked exceptions;
Java 不允許建立user-defined 的value types;
Java 不允許運算符重載;
Java 沒有類似 LINQ 的特性;
Java 不支持委托;
C# 沒有匿名內部類;
C# 沒有像 Java 那樣的內部類,所有的 nested classes 其實都像 Java 的靜態 nested classses;

Java 沒有靜態類;
Java 沒有擴展方法(extension methods);
兩者的訪問修飾符有一定區別;
兩者初始化(initialization)的順序有一定不同。C#初始化變量後才調用父類的構造方法
Java 沒有類似“properties”的東西,而是約定俗成為 getter 和 setter;
Java 沒有類似與"unsafe"的特性;
兩者的枚舉(enums)有一定的不同,Java 的更加面向對象。

註:以上只是簡單列舉,歡迎大家補充。

C# 五個不可替代的特性瞬間秒殺 Java

編程語言各有千秋,如果我們可以同時擁有 C# 和 Java 最好的特性,那會是什麽樣呢?接下來,將羅列出 C# 開發者能用到而 Java 中沒有的那些特性。

1. LINQ

LINQ (Language-Integrated Query,語言集成查詢) 於 2007 年引入到 C#,以幫助開發人員從各種數據源查詢數據。使用它,我們可以在無需考慮正在調用的特定數據庫的語法來編寫查詢語句。LINQ provider 所提供的一個組件將查詢轉換為下層數據源可讀的格式。例如,如果我們需要從 SQL 數據庫查詢數據,LINQ to SQL provider 程序將把 LINQ 查詢轉換成 T-SQL,以便數據庫可以理解它。

要在 LINQ 中執行查詢操作,首先獲取數據庫,然後創建查詢,最後執行查詢。在 LINQ to Object 查詢中,這可能僅像一樣代碼一樣簡單,而不是為每個循環編寫嵌套的復雜叠代。

例如,我們來看看這個代碼,用於在 C# 中從列表中過濾 2 位數。

首先,在不使用 LINQ 的情況下:

List<int> FilterTwoDigitNumbersWithoutLinq(List<int> numbers){ var tens = new List<int>(); for (var i=0; i < numbers.Count(); i++) { if ((9 < numbers[i]) && (numbers[i] < 100)) { tens.Add(numbers[i]); } } return tens;}

如果使用 LINQ 查詢語法形式:

List<int> FilterTwoDigitNumbersWithLinq(List<int> numbers){ return (from a in numbers where (a > 9 && a < 100) select a).ToList();}

或者是方法語法形式:

List<int> FilterNonTwoDigitNumbersWithLinq2(List<int> numbers){ return numbers.Where(a => a > 9 && a < 100).ToList();}

這裏兩種語法都是正確的,唯一的區別就是查詢語法看起來更像是 SQL 語句而方法語法使用 lambda 表達式(當然,看起來很像我們在 Java 裏寫的某些代碼)

綜述:LINQ 所依賴的許多特性,如 lambda 表達式(就 LINQ 來說非常有用),已經在 Java 中有了等效的實現,盡管我們可以使用流和 lambda 來查詢數據,但 LINQ 簡化了整個過程並且移除了很多在 Java 中存在的冗余代碼。

2. Struct

C# 中的結構體類似於類。實際上,一個 struct 甚至可以被認為是一個“輕量級類”,因為它可以包含構造函數、常量、方法等等。一個結構體和一個類之間最大的區別在於結構是值類型,而類是引用類型。

相比於創建類,編寫結構體最重要的好處是在構造一個值類型時比在構造引用類型時更容易確保值語義。如 Microsoft 的文檔所述,“struct 類型的變量直接包含結構體的數據,而類類型的變量包含對數據的引用。”因此,對比使用類時,使用結構體的好處之一是,從代碼的其他部分更改其值的唯一方法是將其作為參考進行顯式傳遞。

微軟的開發人員建議對於那些小於 16 字節、生命周期短、不改變的而且不常裝箱的類型,使用結構體(struct)而不是類(class)。在這種情況下,使用結構體可能會比使用類更有效率,因為它會保存在棧而不是堆中。

比如:

public struct Point { public int X; public int Y; public Point(int X, int Y) { this.X = X; this.Y = Y; } public static Point operator +(Point p1, Point p2) { return new Point(p1.X + p2.X, p1.Y + p2.Y); } public override string ToString() { return ($"({X}, {Y})"); } } class Program { static void Main(string[] args) { Point point1 = new Point(1, 5); Point point2 = new Point(2, 3); Console.WriteLine("The addition of both points will result in: {0}", (point1 + point2)); Console.ReadKey(); }}

小結:很多情況下使用結構體可以節省內存分配和釋放的時間,這確實很有吸引力。然而事實是值類型擁有自己的存儲空間。無論結構體擁有如何明顯的優點和缺點,這在 Java 中都不需要操心。

3. Async/Await

在一段代碼中調用 async,或者更明確地調用方法,這個方法都會在另一個線程上執行,不會阻塞當前線程。當代碼運行到 await 命令的時候,它會繼續運行(await 的語句)。如果這時 async 代碼還沒有完成,那麽執行中的程序會返回到調用點。

這有助於提高應用程序總體的響應速度,以及減少性能瓶頸。在應用程序訪問 Web 和進行所有 UI 相關的活動時,使用異步程序非常重要。相對於以前的異步編程實現,使用 async/await 可以保留你代碼的邏輯結構,而編譯器則會擔負起以前由開發者擔負的重擔。

示例:

class Program { public static void Main() { Console.WriteLine("Hey David, How much is 98745 divided by 7?"); Task<int> david = ThinkAboutIt(); Console.WriteLine("While he thinks, lets chat about the weather for a bit."); Console.WriteLine("Do you think it‘s going to rain tomorrow?"); Console.WriteLine("No, I think it should be sunny."); david.Wait(); var davidsAnswer = david.Result; Console.WriteLine($"David: {davidsAnswer}"); Console.ReadKey(); } private static async Task<int> ThinkAboutIt() { await ReadTheManual(); Console.WriteLine("Think I got it."); return (98745 / 7); } private static async Task ReadTheManual() { string file = @"D:\HowToCalc.txt"; Console.WriteLine("Reading a manual."); using (StreamReader reader = new StreamReader(file)) { string text = await reader.ReadToEndAsync(); } Console.WriteLine("Done."); }}

輸出:

// Possible Output:Hey David, How much is 98745 divided by 7?Reading a manual.While he thinks, lets chat about the weather for a bit.Do you think it‘s going to rain tomorrow?No, I think it should be sunny.Done.Think I got it.David: 14106

概要:CompletableFutures 無疑可以使我們更趨近於擁有等效於 C# 和 Java 所擁有的異步編程中的能力。盡管如此,使用它所帶來的復雜性使其易用度不能與使用 async /await 關鍵字進行的實現相提並論。

4. Lazy<T> 類

無論使用 C# 還是 Java,很多人都已經實現了延遲初始化 (或實例化),因此對象要在第一次使用的時候才會被創建。有一種常見的例子是將延遲初始化用於應用程序啟動的時候加載大量對象,但實際需要初始化的對象可能只有少數幾個。這種情況下,我們希望辨別哪些是不需要在這裏初始化的。只初始化那些確實需要初始化的對象可以提升應用程序的性能。

小結:最近,Lambda 表達式引入到 Java 8 之後,在 Java 中實現延遲加載(還有不少其它事情)變得更容易了。不過,在 C# 中我們可以使用語義化的 Lazy<T> 封裝類來延遲初始化任何類庫或用戶指定的類型。

5. 一些等價的關鍵詞

語言中的有用功能不一定像在 C# 中的 LINQ 或 Java 中的模塊一樣大。這裏有一些可以幫助 C# 開發人員的關鍵字,它們在 Java 中並沒有:

as

C# 中的 as 關鍵字會嘗試安全地將對象轉換為某個類型,如果不能轉換的話,就返回 null。與 Java 的instanceof 幾乎等同,但它是一個布爾值,如果類型匹配則返回 true,否則返回 false。

Yield

在 C# 中使用 Yield 和 return yield 來進行自定義且狀態化的叠代,不需要顯式創建額外的類,也不需要創建臨時集合。在 Java 中我們實現叠代最好的選擇是使用外部庫或使用 Java 8 引入的 Lambda 表達式。

var

Var 是一種隱式類型,其實際類型由編譯器決定,其功能相當於寫一個顯式類型 (比如 int, string 等)。它除了可以減少一些按鍵之外,var 還允許用於匿名類型,而匿名類型在 LINQ 中很常用。我們期待看到“var”標識,備受矚目的 Java SE 9 將實現“將類型推導擴展到定義並初始化局部變量時。”

Checked

C# 中,我們使用 checked 關鍵字顯式啟用對整型表達式的溢出檢查。如果表達式的運算結果超出目標類型的範圍,我們可以使用 checked 強制要求運行時拋出 OverflowException。這十分有用,因為常量表達式會在編譯期進行溢出檢查,而非常量表達式不會。

工具生態系統

Java 和 C# 之間存在大量的不同之外,當然,其中一些源於 Java 和 .NET 框架的不同。這些不同之處也導致了一些工具在兼容性方面的差異,比如 OverOps 在生產監控和錯誤跟蹤方面的差異。

先學習 C# 還是 Java?

那麽究竟是選擇哪一種編程語言會更有前景呢?在此,也分享一些網友的看法:

軟件開發者 Jerome Terry:

語言其實都是相似的,學到的順序並不重要。企業中 Java 更受歡迎,也更容易找到工作。 如果學習了 Java 並熟悉 JVM,則可以輕松切換到 Scala 或 Clojure;C#適合寫 Windows 程序,上手要更加容易。如果你想要在 Windows 上開發,那麽首選必然為 C#。 但是如果只能選一種語言,我可能更傾向於 Java。

COBOL 開發者 Wim ten Brink:

首先學習 C 如何?先花一周的時間學習 C 語言的語法,然後再花兩周時間學習 C++。一旦你有了基本的這種語法的經驗,就可以開始看 Java 和 C#,比較兩者功能特性。你就會發現它們之間會與 C/C++ 有些相似。無論如何,學習這兩者並不是一個巨大的問題。如果你問我將來最流行的語言,因此會最有用,那麽最新的 TIOBE 編程語言排行榜來看,Java 仍會是 No.1,而 C# 處於 C 和 C ++ 之後的第四名。這基本上表明它們在互聯網上的受歡迎程度。

但根據個人的經驗來看,C# 更實用。

整體而言,在如今開發的大環境之下,更多人願意去選擇 Java 。一位已有 3 年 C# 開發經驗的 CSDN 博主也表示:

已經做 C# 開發快 3 年了,馬上 30 了。感覺沒啥前景,毫無出頭之日的迷茫。現在大點的互聯網公司都是用 Java(這只是我個人的看法)。所以現在想趁合同期結束之前,轉到 Java 去。SSH 框架目前正在學。主要是沒 Java 開發經驗,語法上沒什麽問題。我看了一些 Java 方面的招聘,絕大多數都要對 SSH、SSM 框架熟悉。所以我就想,先把框架花半個月看下,我是看的網上的視頻,學起來很快。然後再具體花一個月時間詳細研究一下 SSH 網上商城之類的源代碼。然後把這當成項目經驗寫進簡歷之類。發覺換工作真心難。工資低了不想幹,高了別人又看不起,說方向不匹配。實在覺得 Java 能做的事情非常多,工資待遇方面其實跟 C# 也基本持平。但是越往上,個人覺得還是 Java 更堅挺一些。

歡迎在下方留言,分享你的編程語言學習之路,希望可以給後來者一點啟示及思考。

挑戰 Java 霸主之位?C# 五個不可替代的特性瞬間秒殺 Java