為 Java 開發(fā)者準(zhǔn)備的 Go 教程 01:漫游了
閱讀本文大概需要 9?分鐘。
大家好,我是 polarisxu。
在正式工作之前,一直使用 Java,雖然這些年對(duì) Java 的關(guān)注變少了,但很顯然,Java 用戶群體特別大。不過(guò),我也知曉,有不少 Java 用戶在學(xué) Go。我嘗試寫一系列文章,為 Java 開發(fā)者講解 Go 語(yǔ)言。
這是第一篇,從大的層面簡(jiǎn)單對(duì)比下 Go 和 Java,算作是一次漫游。
整體上,Java 和 Go 之間有許多明顯而微妙的區(qū)別,包括語(yǔ)言層面和運(yùn)行時(shí)層面。我們這里主要在語(yǔ)言層面漫游。
這里的比較,沒有貶低哪門語(yǔ)言的意思,旨在客觀指出各自的特點(diǎn)。
Go 和 Java 都是 C 系語(yǔ)言,但 Go 更接近 C,包括風(fēng)格、庫(kù)等。
01 Go 是編譯型語(yǔ)言,而 Java 是半解釋的
與 C/C++ 一樣,Go 語(yǔ)言源碼會(huì)直接編譯成目標(biāo)計(jì)算機(jī)體系結(jié)構(gòu)的機(jī)器語(yǔ)言。而 Java 源碼編譯成虛擬機(jī)語(yǔ)言,即字節(jié)碼,并由 Java 虛擬機(jī)(JVM)進(jìn)行解釋(interpreted)。為了提高性能,字節(jié)碼通常在運(yùn)行時(shí)被動(dòng)態(tài)編譯成機(jī)器語(yǔ)言。JVM 本身是為特定的操作系統(tǒng)和硬件體系結(jié)構(gòu)構(gòu)建的。
而且 Go 是靜態(tài)編譯,一旦編譯完成,Go 程序只需要一個(gè)操作系統(tǒng)就可以運(yùn)行。Java 程序在運(yùn)行之前需要計(jì)算機(jī)上安裝有 JRE(特定版本)。許多 Java 程序可能還需要額外的第三方代碼。
所以,雖然 Go 和 Java 都是跨平臺(tái)的,但實(shí)現(xiàn)形式還是很不一樣。
02 Go 和 Java 程序結(jié)構(gòu)類似
這兩門語(yǔ)言都支持包含方法和字段的數(shù)據(jù)結(jié)構(gòu)的概念。在 Go 中,它們被稱為 struct(結(jié)構(gòu)體),而在Java中,它們被稱為 class(類)。這些結(jié)構(gòu)被收集到稱為包(package)的分組中。在這兩門語(yǔ)言中,包都可以分層排列(即嵌套包)。
Java 包頂層只包含類型聲明。Go 包可以各種聲明,如變量、常量、函數(shù)以及派生類型聲明。
兩門語(yǔ)言都通過(guò)導(dǎo)入(import)來(lái)訪問(wèn)不同包中的代碼。在 Java 中,可以選擇使用導(dǎo)入的類型(String 或 Java.lang.String)。在 Go 中,所有導(dǎo)入的名稱必須始終是限定的(雖然可以本地導(dǎo)入,但不建議使用)。
03 Go 和 Java 代碼風(fēng)格的差異
這方面涉及到的內(nèi)容不少,無(wú)法一一列出。這里提一些:
1)Go 與眾不同,變量聲明,類型放在后面。語(yǔ)言通常省略分號(hào)。
Java:int x, y, z;
Go:var x, y, z int
2)Java 方法只能返回一個(gè)值。Go 函數(shù)/方法可以返回許多值。
3)Java 方法和字段必須在它們所屬的類型內(nèi)聲明。Go 方法是在所屬類型之外定義的。Go 支持獨(dú)立于任何類型的函數(shù)和變量。Java 沒有真正的靜態(tài)共享變量;靜態(tài)字段只是某些類(相對(duì)于實(shí)例)的字段。Go 支持在可執(zhí)行映像中分配的真正靜態(tài)(全局)變量。
4)Java 只允許其他類型(類、枚舉和接口)的類型擴(kuò)展,而 Go 可以基于任何現(xiàn)有類型創(chuàng)建新類型,包括基本類型(如整數(shù)和浮點(diǎn))和其他用戶定義的類型。Go 可以支持這些自定義類型中的任何一種。
5)Go 和 Java 接口的工作方式非常不同。在 Java 中,類(或枚舉)實(shí)現(xiàn)接口時(shí),必須顯式指定。在 Go 中,任何類型都可以通過(guò)實(shí)現(xiàn)接口的方法來(lái)實(shí)現(xiàn)接口,即隱式實(shí)現(xiàn)接口,所謂的鴨子類型。
6)Java 通過(guò) try/catch 處理異常。Go 中是 error,另外有 panic 和 recover。
04 Java 是面向?qū)ο笳Z(yǔ)言,而 Go 不完全是
面向?qū)ο蟮娜筇匦裕悍庋b、繼承和多態(tài)。
Go 沒有繼承的概念,認(rèn)為組合優(yōu)于繼承。不過(guò),在 Go 中,可以通過(guò)內(nèi)嵌來(lái)模仿部分類似繼承的功能,但本質(zhì)還是組合。
此外,Go 只在接口層面有多態(tài),沒有方法重載。
05 Java 不少特性是基于 Annotation 的,Go 沒有 Annotation
許多 Java 庫(kù)(特別是框架,比如Spring),都充分利用了 Java 的注解(Annotation)。注解提供元數(shù)據(jù)(通常在運(yùn)行時(shí)使用),以修改庫(kù)提供的行為。Go 沒有注解,因此缺少此功能。Go 可以使用代碼生成(go generate)獲得與注釋類似的結(jié)果。Go 有一種簡(jiǎn)單的注解形式,稱為 tag,可用于自定義某些庫(kù)行為,如 JSON 或 XML 格式。
06 Go 和 Java 都使用 GC 管理內(nèi)存
這兩門語(yǔ)言都使用 stack 和 heap 來(lái)保存數(shù)據(jù)。棧主要用于函數(shù)局部變量,堆用于其他動(dòng)態(tài)創(chuàng)建的數(shù)據(jù)。在 Java 中,所有對(duì)象都在堆上分配。在 Go 中,堆上只分配可在函數(shù)生命周期之外使用的數(shù)據(jù)(通過(guò)逃逸分析確認(rèn))。在 Java 和 Go 中,堆都是垃圾收集的;堆對(duì)象由代碼顯式分配,但總是由垃圾收集器回收。
Java 沒有指向?qū)ο蟮闹羔樀母拍睿灰梦挥诙阎械膶?duì)象。Go 允許訪問(wèn)指向任何數(shù)據(jù)值的指針(或地址)。在大多數(shù)情況下,Go 的指針可以像 Java 引用一樣使用。
Go 的垃圾收集實(shí)現(xiàn)比 Java 的更簡(jiǎn)單,通常 Go GC 需要調(diào)優(yōu)的情況較少(沒有太多選項(xiàng)可配置)。
07 Go 和 Java 都支持并發(fā),但方式不同
Java 有線程(Thread)的概念。而 Go 是 Goroutine,它是由語(yǔ)言提供的。Goroutine 通常被稱為輕量級(jí)線程。Go 運(yùn)行時(shí)支持使用比 JRE 支持的線程多得多的 Goroutine。
Java 支持同步控制。Go 具有類似的庫(kù)函數(shù)。Go 和 Java 都支持跨 Thread/Goroutine 安全更新的原子值的概念。兩者都支持顯式鎖定庫(kù)。
Go 提供了通信順序進(jìn)程(CSP)的概念,作為 Goroutine 在沒有顯式同步和鎖定的情況下進(jìn)行交互的主要方式。Goroutine 通過(guò) channel 進(jìn)行通信,channel 是有效的管道(FIFO 隊(duì)列),通常與 select 語(yǔ)句相結(jié)合使用。
08 Go 運(yùn)行時(shí)比 JRE 更簡(jiǎn)單
Go 的運(yùn)行時(shí)比 JRE 提供的運(yùn)行時(shí)小得多。沒有 JVM 等價(jià)物,但兩者中都存在類似的組件,如垃圾收集。Go 沒有字節(jié)碼解釋器。
Go 擁有大量的標(biāo)準(zhǔn)庫(kù)。Go 社區(qū)提供了更多庫(kù)。但是 Java 標(biāo)準(zhǔn)庫(kù)和社區(qū)庫(kù)在功能的廣度和深度上都遠(yuǎn)遠(yuǎn)超過(guò)了當(dāng)前的 Go 庫(kù)(畢竟 Java 比 Go 早太多年了,而且生態(tài)確實(shí)好)。盡管如此,Go 庫(kù)仍然足夠豐富,可以開發(fā)許多有用的應(yīng)用程序,特別是服務(wù)端程序。
所有使用過(guò)的庫(kù)都會(huì)嵌入到 Go 可執(zhí)行文件中,即前面提到的靜態(tài)編譯。可執(zhí)行文件是運(yùn)行程序所需的一切(而 Java 庫(kù)在首次使用時(shí)動(dòng)態(tài)加載)。這使得 Go 程序二進(jìn)制文件通常比 Java 二進(jìn)制文件(單個(gè) “main” 類)大,但當(dāng)加載 JVM 和所有依賴類時(shí),Java 的總內(nèi)存占用通常更大。
09 Go 程序的構(gòu)建過(guò)程是不同的
Java程序是在運(yùn)行時(shí)構(gòu)造的類的合并,通常來(lái)自多個(gè)源(供應(yīng)商)。這使得Java程序非常靈活,特別是當(dāng)通過(guò)網(wǎng)絡(luò)下載時(shí)。Go 程序是在執(zhí)行之前靜態(tài)構(gòu)建的。啟動(dòng)時(shí),可執(zhí)行映像中的所有代碼都可用。這提供了更大的完整性和可預(yù)測(cè)性,部署特別方便,但犧牲了一些靈活性。這使得 Go 更適合集裝箱化部署。
Go 程序通常由 “go builder” 構(gòu)建,這是一種結(jié)合了編譯器、依賴項(xiàng)管理器、鏈接器和可執(zhí)行構(gòu)建器等的工具。它包含在標(biāo)準(zhǔn) Go 安裝中。Java 類被單獨(dú)編譯(由 Java 開發(fā)工具包(JDK)提供的 javac 工具編譯),然后通常被組裝成包含相關(guān)類的歸檔文件(JAR/WAR)。程序是從一個(gè)或多個(gè)存檔中加載的。檔案的創(chuàng)建,特別是包括任何依賴項(xiàng),通常由獨(dú)立于標(biāo)準(zhǔn) JRE 的程序(如 Maven)完成。
其他方面,比如 Java 和 Go 都是過(guò)程式語(yǔ)言,此外,在函數(shù)式編程方面,Go 一開始,函數(shù)就是一等公民,而 Java 8 才有較好的支持。
參考
這個(gè)系列主要參考以下資料:
Go for Java Programmers: https://talks.golang.org/2015/go-for-java-programmers.slide Java to Go in-depth tutorial:https://yourbasic.org/golang/go-java-tutorial/ Go for Java Programmers: https://www.oreilly.com/library/view/go-for-java/9781484271995/
我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗(yàn)!2012 年接觸 Go 語(yǔ)言并創(chuàng)建了 Go 語(yǔ)言中文網(wǎng)!著有《Go語(yǔ)言編程之旅》、開源圖書《Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)》等。
堅(jiān)持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場(chǎng)心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長(zhǎng)!也歡迎加我微信好友交流:gopherstudio
