以前一直使用C#開發,最近因爲眼饞Java生態環境,並藉着工做服務化改造的契機,直接將新項目的開發都轉到Java上去。積攢些Java開發經驗,應該對.NET開發也會有所啓發和益處。json
歡迎工做一到八年的Java工程師朋友們加入Java高級交流:854630135網絡
本羣提供免費的學習指導 架構資料 以及免費的解答架構
不懂得問題均可以在本羣提出來 以後還會有直播平臺和講師直接交流噢
函數
從理論上說,Java和C#語言差異不大,畢竟難聽地說,C#就是抄Java出來的。程序語言簡史如是介紹這兩種語言:學習
然而隨着時間流逝語言發展,我的認爲,C#在語言層面已經大大領先了Java。下面我總結一下我在趟過的坑,以供轉型或學習的同窗參考。spa
本文並不是要比出這些語言誰優誰劣。有時候,好或壞是很是主觀的判斷,不一樣人有着不一樣的見解,強行判定好壞只會引發無畏的爭論。這些語言有着各自的特色,有各自適合的場景。就像下面要談到的Checked Exception特性,這是個很好的特性,可是在一些狀況下也會引發很多麻煩。code
Java是Checked Exception的。這就是說,若是你寫了一個方法,這個方法會拋出一些異常,那麼你須要用throws
關鍵字標明這個方法會拋出哪些異常。這個特性很難說是好仍是很差。Checked Exception本質上是一種類型系統,它明確規定了一個方法除了返回值類型之外,還可能拋出什麼異常。這樣調用方函數就可以明確地知曉應該處理或者傳遞哪些異常。這個特性在用得好的人手裏,對正確處理各類邊邊角角的異常十分有用。然而,若是在你沒法本身選隊友,沒法控制開發人員的水平的狀況下,你極可能會發現,全部的方法都被標記爲throws Exception
。對象
Java的Lambda本質上仍然是一個對象。事實上,Java的Lambda函數是一個知足Functional Interface接口的對象。好比下面代碼,聲明瞭一個具備一個int
參數,返回一個int
參數的函數。blog
@FunctionalInterface interface AFunction { int invokeBalaBala(int a); }
咱們能夠這樣定義一個這個函數的變量:AFunction f = x -> 2 * x;
。接口
Java的Lambda和Checked Exception結合在一塊兒後,產生了一個很是棘手的問題。因爲Checked Exception是類型系統的一部分,一個不拋出異常的函數和一個會拋出異常的函數,它們的類型是不相同的。這就致使了Java的Lambda泛用性大大減小並且不是很好用。以對List的map
操做爲例,咱們能夠用以下代碼將list
裏的每一個元素翻倍:
list = list.stream().map(x -> 2 * x).collect(Collectors.toList());
這裏map
接收一個類型爲輸入一個int
參數,返回一個int
值的函數。然而,若是咱們須要給它的函數有可能拋出異常,好比這個函數會去讀取文件、訪問網絡服務、或者作Json反序列化,則因爲類型不一樣,Java編譯器將會報錯。
// 這個編譯器會報錯 list.stream().map(x -> JsonUtil.parse(x)).collect(Collectors.toList());
解決方案一種是在函數體中使用try cache處理異常。可是不少時候,異常沒辦法在這個時刻處理,必需要拋出。那麼還有另外一種方案:將異常轉換爲RuntimeException
,RuntimeException
是所謂的Unchecked Exception,它不是類型系統的一部分,不須要用throws
標註,因此不會致使函數類型變化。另外一方面,編譯器也沒法檢測出是否可能會拋出RuntimeException。不管採用哪一種方案,都使得這個Lambda函數變得沒那麼好看。
Java的泛型原理和C#不一樣。C#是運行時泛型,在程序運行的時候仍然能獲取泛型的類型信息。而Java的泛型是類型擦除(Type Erasure)式泛型。名稱聽起來很高大上,意思是Java的泛型僅僅用於編譯時類型檢查,類型檢查完成後,類型信息就被編譯器擦除。在最後生成的字節碼中中,泛型類型都被改成Object
類型。
好比這句:
HashMap<TK, TV> map = new HashMap<TK, TV>();
編譯後變成:
HashMap map = new HashMap();
Type Erasure方式的影響主要有兩個:
像下面兩句:
x instanceof T new T()
在Java中都會編譯出錯。而這在C#中都是很常見的代碼。在C#中,咱們能夠有這樣的Json反序列化方法:
T parse<T>(string jsonStr)
這個方法將jsonStr
反序列化爲類型T
的一個對象。這種寫法看起來十分天然。然而在Java中沒法實現。由於在parse
方法中須要在運行時實例化T
的一個對象,而Java在運行時這些泛型都已經被擦除,沒法獲取類型T
的信息,從而沒法實例化。要在Java實現相似的方法,須要額外將一個Class
對象放到參數:
T parse(String jsonStr, Class<T> type)
這樣Java才能使用這個type
,在運行時使用反射的方式生成類型T
的實例。
在面向對象哲學中,字段屬於實現細節,應該設爲private
使它隱藏在類的內部。可是在實際中,有不少字段須要直接訪問和修改。從功能實現上講,直接把字段設爲public
也是能夠的。可是這樣作的壞處在於將來功能擴展時,這個字段的含義、存儲方式可能發生變化,致使每一個使用了這個字段的代碼都須要修改。所以,應該將字段的訪問封裝的方法中,即便只是很簡單的訪問和設置,也應該實現getter方法和setter方法。
C#和Python有property特性支持快速定義和調用getter方法和setter方法。Ruby則依靠函數調用能夠省略括號的特性,使getter方法看起來很像直接訪問字段。Java沒有使用特性支持getter和setter方法,而是約定必須實現字段名前加get
的getter方法(然而這裏有個不一致的地方,若是字段是布爾類型,則加is
)和字段名前加set
的setter方法。這致使的一個問題是開發時須要編寫大量的getter方法和setter方法。爲Java冗長的特色貢獻了一份力量。遵循這個規範很重要,覺得在不少經常使用庫,好比Json序列化,會以getter方法做爲字段存在的依據。
爲了減小開發工做量,可使用IDE自動生成getter方法和setter方法。常見的Java IDE都支持自動生成getter方法和setter方法。另外一個方案是使用Lombok,經過Data
,Getter
,Setter
等註解,讓編譯器在編譯時自動生成getter方法和setter。