嘗試Java,從入門到Kotlin

以前一直使用C#開發,最近因爲眼饞Java生態環境,並藉着工做服務化改造的契機,直接將新項目的開發都轉到Java上去。積攢些Java開發經驗,應該對.NET開發也會有所啓發和益處。json

歡迎工做一到八年的Java工程師朋友們加入Java高級交流:854630135網絡

本羣提供免費的學習指導 架構資料 以及免費的解答架構

不懂得問題均可以在本羣提出來 以後還會有直播平臺和講師直接交流噢
 函數

從理論上說,Java和C#語言差異不大,畢竟難聽地說,C#就是抄Java出來的。程序語言簡史如是介紹這兩種語言:學習

然而隨着時間流逝語言發展,我的認爲,C#在語言層面已經大大領先了Java。下面我總結一下我在趟過的坑,以供轉型或學習的同窗參考。spa

本文並不是要比出這些語言誰優誰劣。有時候,好或壞是很是主觀的判斷,不一樣人有着不一樣的見解,強行判定好壞只會引發無畏的爭論。這些語言有着各自的特色,有各自適合的場景。就像下面要談到的Checked Exception特性,這是個很好的特性,可是在一些狀況下也會引發很多麻煩。code

Checked Exception

Java是Checked Exception的。這就是說,若是你寫了一個方法,這個方法會拋出一些異常,那麼你須要用throws關鍵字標明這個方法會拋出哪些異常。這個特性很難說是好仍是很差。Checked Exception本質上是一種類型系統,它明確規定了一個方法除了返回值類型之外,還可能拋出什麼異常。這樣調用方函數就可以明確地知曉應該處理或者傳遞哪些異常。這個特性在用得好的人手裏,對正確處理各類邊邊角角的異常十分有用。然而,若是在你沒法本身選隊友,沒法控制開發人員的水平的狀況下,你極可能會發現,全部的方法都被標記爲throws Exception對象

Lambda,以及與Checked 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處理異常。可是不少時候,異常沒辦法在這個時刻處理,必需要拋出。那麼還有另外一種方案:將異常轉換爲RuntimeExceptionRuntimeException是所謂的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的實例。

Getter/Setter

在面向對象哲學中,字段屬於實現細節,應該設爲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,經過DataGetterSetter等註解,讓編譯器在編譯時自動生成getter方法和setter。

相關文章
相關標籤/搜索