Go VS Java:一位資深程式設計師對兩種語言的解讀

2021-02-13 21CTO

導讀:對於軟體開發的程式語言,其實沒有萬能靈藥。

本文作者詳細介紹了他使用Java和Go這兩種程式語言,一個是傳統語言,一個是新興語言的工作方式。

Go VS Java

實話說,我很喜歡Java這門語言。近幾年來,我在公司裡積累了大量關於EJB2,DB2與Oracle等後端開發的專業知識。

現在我轉向到基於自然語言處理的開發方向,如Spring Boot、Redis、RabbitMQ、Open NLP還有UIMA等技術。

因此說來,我選擇的語言是Java,這個語言一直有旺盛的生命力,寫起程序來也很有意思。

開始測試Go!

在2017年初,我接到了一個很有意思的項目。這個項目圍繞著自動化系統來監控農作物和花花草草等植物們。

原先的開發團隊原始碼部署在三個不一樣的系統中:Windows,MacOS和ARM,他們使用Go語言做為網關。

坦白講,當時我對Go語言一點兒也不熟悉,隨著項目的發展,我的工作內容包括對新技術的學習以及產品實施。項目現有代碼庫結構複雜,我面臨的挑戰比較大。使用Go寫的針對於物聯網平臺支持程序,要在三個異構系統上部署、測試與運維。這個系統使用單例模式編寫,系統耦合嚴重,模塊之間相互雜揉,可以說是一處錯誤,多處崩潰。我用Java的開發風格重新構建了網關的新版本,但後讓我弄的比原來更醜陋、混亂了。

我後來升職為一家公司任技術總監。

我試著不再使用Java的方式來開發Go了,儘量更多的使用Go的語言特性來做,全面擁抱該語言。

如此下來我才真正發現,Go的確是一個創新且全面的程式語言,我的團隊開始將它用它來開發各種各樣的項目。

不容置疑的是,與任何程式語言一樣,Go有優點也有不少缺點,我這個人不太會撒謊,有時候我還蠻想念Java的。

依照以往我的編程經驗,軟體開發方面沒有靈丹妙藥,程式語言就是典型。以下我來講一下心得體會,使用一門傳統語言和創新語言新手是怎麼幹活的。

Go與Java的相似之處

Go語言與Java都從屬於C語言家族,既然是遠房親戚,它們有著相似的語法。所以,Java開發者閱讀Go寫的代碼並不會太難,反之亦然。

Go語句在語句末尾並不使用;分號結束。不用擔心,除了極少數情況,我閱讀Go寫的代碼反倒感覺更清晰易讀。

Go與Java都使用我最喜歡的功能之一,那就是垃圾收集器(GC),以防止內存洩漏。

C/C++程式設計師都知道,自己在寫程序時要注意避免內存洩漏,垃圾回收機制讓內存管理自動化,從而簡化程式設計師在代碼中處理類似的功能。

Go語言的GC工具的表現很優秀,它的處理時間在1.8版本中不到1ms即可完成。

在Go語言中,其GC的設置參數並不多,有一個唯一的GC變量用來設置初始時垃圾回收目標的百分比。在Java中,有4個不同的垃圾收集器,每一個都要有大量的設置工作。

Java和Go都被視為跨平臺語言,但是Java需要Java虛擬機(JVM)來解析編譯後的代碼,而Go可以簡單地將代碼編譯為給定平臺的二進位文件。

Java與Go相比,Go比Java依賴度更低,因為Go每次為一個平臺編譯代碼時都要創建一個二進位文件。從測試和DevOps角度來看,分別編譯不同平臺的二進位文件非常的費時,有時候使用GO組件時,跨平臺的Go編譯在一些情況下並不起作用。但是,使用Java則可以在有JVM的任何地言使用一個Jar。

Go佔用內存很小,而且並不需要安裝和管理虛擬機的關聯依賴,以及複雜的注意事項。

接下來談一下反射功能。Java的反射功能很強大,而Go的反射更偏複雜。Java是一種純面向對象語言,所有內容都被視為對象。

在Java使用反射,需要為對象創建一個類,並從此類中獲取所需要的信息。如下代碼所示:

Class cls = obj.getClass();
Constructor constructor = cls.getConstructor();
Method[] methods = cls.getDeclaredFields();

接下來就可以訪問構造函數、方法和屬性,然後去調用和設置它們。

在Go語言中,則沒有類的概念,它的結構中僅包含已聲明的欄位。因此,我們需要它的「反射」包來提供相關信息。

type Foo struct {
A int `tag1:"First Tag"
tag2:"Second Tag"`
B string
}


f := Foo{A: 10, B: "Salutations"}
fType := reflect.TypeOf(f)
switch t.Kind(fType)
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
// ...
}
}

我認為,這並不是一個大問題,因為Go中沒有用於結構的構造函數,因此結果是許多原始類型,且必須分別處理,還需要認真考慮指針。

在Go中,我們還可以通過指針或值傳遞某些東西。Go語言結構具有欄位的功能,而不是方法。

再來說一下兩個語言的輔助功能。

Java具有Private、Protected以及Public這3個修飾符,用來對數據、方法和對象提供不同範圍的存取。

Go語言中,有著和Java類似的語句,它們是exported/unexported修飾符。在Go中如果沒有修飾符,以大寫字母開頭的所有內容都能夠導出,並且在其它軟體包中可見。unexported,以小寫變量或函數僅在當前包中可見。

Go與Java大方面的差異

Go語言並不是一種面向對象(OOP)語言。它沒有像Java中的繼承,沒有通過繼承這一特性實現系統的多態性。

Go語言沒有對象,只有結構體。但它可以通過提供接口實現一些面向對象的模式。同樣道理,可以將將結構彼此嵌入,但是嵌入式結構無法訪問宿主結構的數據和方法。

Go中使用組合,而不使用繼承來組合一些所需的行為和數據。

Go是一種命令式語言,而Java則是一種聲明式語言。

Go中沒有像依賴式注入那樣的東西,在Go中必須將所有內容明確的包裝在一塊。因此,在Go中編程末儘量少用一些魔術方法,一切都是可見的。

Go程式設計師要了解Go代碼如何使用內存、文件系統以及其它關聯資源的全部機制。

從另一方面,Java需要開發人員更多的關注自定義開發的業務邏輯部分,諸如如何創建、過濾、更改和存儲數據。

從系統基礎架構和資料庫管理系統而言,所有這些都是通過配置和通過Spring Boot等通用框架來完成。

人們總是矛盾心理,我們對基礎架構的部分感到枯燥乏味,因此這部分功能交給框架。這會給我們帶來方便,但對於程式設計師來說,控制權在框架,也限制人們對整個流程優化的能力。

再討論一下兩個語言的變量定義,以及順序。在Java中定義變量類似於以下:

在Go語言,這樣來寫:

我在第一次使用Go時,沒有;號的代碼讓我有些困惑,就像寫文章沒寫句號一樣,感覺沒有寫完。

使用Go語言的優勢

Go有著簡單且且優雅的並發能力。Go語言有著強大的並發模型,稱之為「通信順序過程」或CSP。在Go中使用n-to-m嗅探器,該嗅探器允許n個系統線程中發生m個並發執行。

可以利用Go語言的關鍵字:go(與該語言的名字相同)啟動例程。比如下面的字符串:

此時函數doMyWork()將同時開始執行。

在Go中進程之間的通信可以通過共享內存(並不推薦)和共享通道來完成。它允許開發者使用GoMaxProcs環境變量定義一個多內核的健壯且流暢有並發系統。

Go提供了一種特殊模式-race(競賽)來運行二進位文件,並同時檢查運行情況。通過此機制來證明軟體是並發安全的。

以上命令格式,即以競賽檢測模式運行一個應用程式。

我個人很喜歡Go提供的即開即用功能(golang.org/dl),它提供一個很好的例子,如sync同步功能(golang.org/pkg/sync)包。

比如針對實現Singleton模式實現,可以如下代碼表示:

package singleton import ("sync")
type singleton struct { }
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}

同步包還為並發實現映射、互斥鎖、條件變量和等待提供了一種結構。它的另一個包atomic(golang.org/pkg/sync/atomic)提供了並發安全轉換與數學運算。

實際上,Go提供了並發代碼所需要的全部。

Duck typing


「如果你說話像鴨子,走路像鴨子,那它一定就是只鴨子」。這句話在Go的世界裡是正確的。

在Go語言中,不需要定義某種接口,直接實現結構即可。在Java中,對象比較顯式聲明並實現了接口。

探查器。Go提供的性能分析工具,使用非常方便、快捷和容易。Go中的事件探查器有助於顯示程序中的內存分配和CPU使用情況,還可以在可視化圖形中加以說明,這對優化應用程式特別有用。

Java可以從Java VisualVM這些探查器中來開始,但它們不像Go的探查器那樣簡單,而且這些工具的功效取決於JVM,其獲取的統計信息和垃圾收集與JVM的具體工作有關。

C 與Go

Go中允許對C語言進行簡單且強大的集成。開發者可以在Go項目中編寫有C語言代碼的平臺相關應用。從底層來講,CGo可以讓開發人員創始調用C代碼的Go程序包。為了排除/包含指定平臺的C代碼段,如各種構建器選項,這些代碼段可以實現應用程式的多平臺化。

函數作為參數使用

Go函數可以用作變量,傳遞給另一個函數或用作結構的欄位。Go語言的這種多功能性的確讓人耳目一新。

而Java是從1.8開始結合了lambda的使用,但它並不是真正的函數,而是一個單功能的對象。它的這一特性就是想實現Go中使用函數的行為,而Go在一開始就

存在於語言特性中。

明晰的代碼風格。在Go語言社區中,有著眾多熱情和能力強大的支持者,社區中有大量對Go的使用示例和操作的最佳實踐和方法。

其地址為https://golang.org/doc/effective_go.html

在Go的函數中可以返回多個參數,這是一個非常有用和優秀的方式。如下代碼:

package main
importt "fmt"


func returnMany() (int,string,error){


return 1,"example",nil
}


func main(){
i,s,err :=returnMany()
fmt.Printf("Returned %s %s %v",i,s,err)
}

Go語言的缺點

Go除了接口外,沒有多態特性。Go中沒有多態性,這表示如果在同一個程序包中,有兩個函數具有不同的參數,但是含義相同,這時必須給它們修改為不同的名稱。請看如下之代碼:

func makeWorkIntt(number int){
fmt.Printf("Work done number %d",number)
}
func makeWorkStr(title string){
fmt.Printf("Work done title %s",title)
}

其實這些方法都做同樣的事情,只是方法名稱不同,而且代碼比較醜陋。

此外,在Go中沒有繼承的多態性。如果嵌入到結構,則嵌入式結構只知道自己的方法,而不會知道宿主結構的方法。這對於一些有其它語言經驗開發者有一定的挑戰性,他們在之前使用的OOP語言主要使用繼承處理,當使用GO語言時會有一些困惑。

隨著時間的流轉,我自己開始意識到繼承式的多態化處理只是一種思維方式,結構式處理很可靠,也更明顯,且運行時間可變。

關於錯誤處理,處理什麼返回錯誤信息,以及如何返回錯誤。作為開發者,我們需要每次都返回錯誤並傳遞相關錯誤。有時候錯誤會隱藏,這確實是麻煩的所在,要記住檢查錯誤並把它們傳遞出去。

在Java中異常處理要方便得多,比如RuntimeException,都可以不加入函數的籤名中即可使用。

public void causeNullPointerException() {
throw new NullPointerException("demo");
}
/*
...
*/


try {
causeNullPointerException();
} catch(NullPointerException e) {
System.out.println("Caught inside fun().");
throw e; // rethrowing the exception
}

此外,Go語言沒有泛型支持,雖然這一特性會增加複雜性,Go的創建者也認為代價高昂。在Go語言創建時,必須針對不同的類型重複使用自己的或生成代碼。

沒有注釋

在Go中可以用代碼生成部分注釋,但是不幸的是,在運行時注釋根本不能替換,這是有原因的——Go不是聲明性的,且代碼中不應包含任何魔術。

我喜歡在Java中使用注釋,它們讓代碼更加優雅、簡單、簡約。這些注釋可以生成大量文件,在做HTTP結點提供某些元數據時非常有用。在GO語言中,必須手動或直接製作Swagger文件,或作為結點功能提供特殊注釋。Java中的注釋是一種魔術方式,人們通常不必關心它們的工作方式。

再來談一下Go中的依賴管理。在本文之前我曾寫過一篇GO依賴項管理的文章。Go依賴管理環境在相當長一段時間中存在一些麻煩。最初,除了「Gopgk」外沒有任何依賴項管理,後來又發布「Vendor」後被「vgo」取代。現在可以手動和使用go命令(如go get命令)來修改go.mod文件描述符來處理。

不幸的是,這種修改會引發依賴關係不穩定。

Go沒有開箱即用的源鏡像依賴關係管理機制,而Java有著諸如Maven和Gradle之類著名的依賴管理聲明類工具。它們也可能用來構建,部署和處理其它集成用途。

我們在實際開發中使用Makefile、Docker-composer和bash腳本自定義構建所需的依賴關係管理,會讓持續交付和集成的過程、穩定性變複雜。

在Go中,引用軟體包的名稱要包含託管的GitHub域名。例如:

import 「github.com/pkg/errors」

這塊實在有點奇怪,而且非常不方便,如果不更改整個項目代碼庫的導入,就無法用自己的實現替換其它人的代碼。

而在Java中,導入通常以公司名字開頭。像這樣:

import by.spirascout.public.examples.simple.Helper;

當然,我想在GO中的這些依賴管理問題都只是暫時的,將來會以最有效的方式解決。

總結

Go語言中最有趣的一塊是,它遵循一種代碼命名準則,稱為代碼可讀性方法的心理學。使用Go語言,可以使用單獨的方法來構建清晰且可維護的代碼,雖然看起來像是一些單詞組合在一起,但實際上清晰可讀。

一些使用Go進行Web開發的公司,它們將項目展示給我,代碼體現了Go的快速,強大且易於理解,非常適合小型服務和並發處理。而對大型,複雜的系統,比如功能更複雜的服務以及微服務系統,Java暫時保持在世界頂級程式語言的地位。

儘管Java SE已經成為Oracle的收費商品,而Go則是技術社區的孩子。已經產生多個品牌的JVM,而Go的工具集則是相同的。

可以確定的是,不同的任務需要不同的工具,語言也是如此。

關聯閱讀:

可能是世界上最簡單的用 Go 來寫 WebAssembly 的教程

Golang與Python,哪種程式語言更適合您?

PHP VS Golang,是一個艱難選擇嗎?

相關焦點

  • 後端程式設計師一定要看的語言大比拼:Java vs. Go vs. Rust
    這不是基準測試,更多是對可執行文件大小、內存使用率、CPU使用率、運行時要求等的比較,當然還有一個小的基準測試,可以看到每秒處理的請求數量,我將嘗試對這些數字進行有意義的解讀。為了嘗試將蘋果與蘋果進行比較(也許是?),我在此比較中使用每種語言編寫了一個Web服務。Web服務非常簡單,它提供了三個REST服務端點(endpoint)。
  • Java程式設計師最容易犯的10個錯誤
    都說Java語言是一門簡單的程式語言,基於C++演化而來,剔除了很多C++中的複雜特性,但這並不能保證Java程式設計師不會犯錯。在開發 Java 軟體時可能會遇到許多類型的錯誤,但大多數可以避免。 本文根據java開發人員在編碼過程中容易忽視或經常出錯的地方進行了整理,總結出Java程式設計師最常犯的10大錯誤,具有一定的參考借鑑價值,需要的朋友可以參考下。
  • 頂級程式語言之對比:Rust VS Go
    現如今新程式語言層出不窮,從如此多的程式語言中選擇一款最適合的變得相當困難。因此,我們在本文中將討論兩種最受開發者歡迎的程式語言:Rust和Go語言。除了介紹這語言外,還會比較兩種語言之優缺點。 性能和工作效率 - Rust vs Go性能是兩個語言比較的因素之一。Rust和Go兩種語言的運行速度幾乎與C++/C相當。當人們進行編碼工作時,Go的開發速度比Rus要快。在軟體開發的多個步驟中,與Rust相比,Go的性能有所下降。
  • 程式設計師技術選型:寫Go還是Java?
    我發現它是一種創新且全面的語言,我們的團隊現在仍然每天在各種項目中使用它。但是,與任何一門程式語言一樣,Go 也有它的缺點,而且我不想撒謊——有時候我真的很想念 Java。如果說我的編程經驗教會了我什麼,那一定是——軟體開發沒有銀彈。我將在這篇文章裡詳細分享我使用一門傳統語言和一門新語言的經歷。
  • 專訪OAM和DAPR創始人白海石:一位33年資深程式設計師的樸素想法
    這引起了一位有著長達33年編程經驗的資深程式設計師的重視,他就是微軟的首席程式設計師白海石。,只要通過http或gRPC進行交流,所有現代語言均可使用DAPR。DAPR雖不依賴K8s運行,但能夠完美支持K8s,使得應用能夠在雲計算和邊緣計算兩種不同的平臺中運行。
  • 盤點Java程式設計師最常犯的10個錯誤
    都說Java語言是一門簡單的程式語言,基於C++演化而來,剔除了很多C++中的複雜特性,但這並不能保證Java程式設計師不會犯錯。那麼對於廣大的Java程式設計師來說,它們最常犯的10個錯誤是什麼呢?本文通過總結出Java程式設計師最常犯的10大錯誤,可以有效地幫組Java後來者少走彎路,少加班,並寫出更健壯的應用程式。1.
  • 2021年程式設計師後端程式語言排名終於出來了,看看你用的語言排第幾
    截至到今天,我看到的數據,python仍然在市場上排名第一,java,go和php 緊隨其後,佔據著一部分市場,c++,c依然保持著超越平均水平的範圍
  • Go語言入門教程(一)
    它具有C語言的語法特性,但功能上又具備內存安全,垃圾回收,結構形態和並行計算等特點。對於廣大的開發者的一個福音是go完全開源。在雲原生,雲計算的今天,Go語言又被稱之為雲計算時代的C語言,它誕生的目的是為了讓廣大程式設計師們具有快速的開發效率,因為它專門針對多處理器系統應用應用程式提供了優化。
  • 雲原生時代,Java還是Go?
    在這篇文章中,我將使用兩種語言來寫同樣的服務。比較它們的CPU使用率、RAM、延遲和運行速度。這些服務將在容器中啟動,資源分配相同,使用ab來測試。對於我的案例來說,這是一個 "足夠好 "的基準,因為我不假設找到最好/最差的基準結果,而是在同一環境下執行運行兩個基準測試進行比較。
  • 解讀 Go 語言的 2019:Go 語言不行了嗎?
    而 Go 語言正是我們做這類事情時所需要的強大工具。本文是 InfoQ 「解讀 2019 」年終技術盤點系列文章之一。從 TIOBE Index 來看,Go 語言最近在全球的熱度似乎有所下滑。不過,如果看總體排名的話,截止到 2019 年的 12 月,Go 語言依然排在第 15 位,仍處於主流之列。雖然中途存在一些起落,但總體上還是與去年同期持平的。
  • Go語言無孔不入的2016:躋身主流程式語言、國內大熱、極速提升、尖端應用……
    如果你關注TIOBE的程式語言排行榜就會發現,截止到2016年11月,Go語言從原先的第50多位經過多次上竄已經躍到了第13位,躋入絕對主流的程式語言的行列!這份排行榜每月都會更新,並基於網際網路上的程式設計師老鳥、教學課程和相關廠商的數量進行排名。在國內,從我這幾年運營Go語言北京用戶組的經歷來看,可以明顯地感覺到Go語言的在國內的大熱。
  • Java vs Kotlin,Android開發人員應該選擇哪種語言?
    自 Google 於 2017 年宣布 Kotlin 成為 Google IO 的 Android 開發官方語言以來,想要成為Android開發人員的程式設計師正陷入兩難境地。在討論這個問題前,我首先要明確一點,不要陷入程式語言戰爭,不要進行語言鄙視,只要能賺錢的程式語言就是好語言。
  • Uber 把公司內部的《Go 語言風格指南》開源了
    程式設計師書庫(ID:CodingBook) 猿妹綜合編譯項目地址:https://github.com/uber-go/guide/blob/master
  • Go語言迎來全迸發時代,未來全面覆蓋程序開發領域丨解讀2015
    2015年,整個IT技術領域發生了許多深刻而又複雜的變化,InfoQ策劃了「解讀2015」年終技術盤點系列文章,希望能夠給讀者清晰地梳理出技術領域在這一年的發展變化,回顧過去,繼續前行。讀者可參照專欄去深入Go語言,也可參照國內Go項目匯總和Go命令教程學習Go語言。現今,21世紀的第2個十年已經過半,網際網路也真正進入了極速發展的階段。
  • 「Go 僅是 Google 的程式語言,而不是程式設計師的!」
    ,不少程式設計師也紛紛開始吐槽,無論是 Go 語言的管理權還是其功能模塊的更新,最終都需要取決於 Google 這位管控者,這極大地限制了 Go 的發展自由,最終他們還得出「Go 語言是 Google 的,而不是社區以及程式設計師」的結論。
  • VS Code Java 開發指南!
    紅帽對 Java 的語言支持想要獲得 VS Code 對 Java 的支持,只需安裝該擴展即可實現。其他的擴展能夠起到錦上添花的作用,你可以根據項目具體情況酌情使用。安裝語言支持後,你便可以閱讀和編輯 Java 原始碼了。
  • 程式設計師最不喜歡的語言為什麼是Java、Python?
    那麼,這些令人不快的、不受歡迎的程式語言都是哪些呢?為什麼程式設計師如此害怕使用這些程式語言呢?如果有機會的話,很難不會加入一些理論,甚至說一些不明智的話。或者為一些因為錯誤的原因而不喜歡的程式語言辯護。更準確地說,StackOverflow 統計的是「正使用該語言或技術進行開發,但沒有表示有興趣繼續使用的開發人員的百分比。」
  • 推薦VSCode多語言開發,支持一鍵JAVA
    /),想必每一個開發java 的小夥伴都曾經為配置 java 環境而頭痛不已,沒關係!如果這個時候,你說我,作為一個軟粉,對java為什麼這麼高興😮,我想說的是,現在慢慢到了一個微服務的時代了,語言已經不是之前那麼涇渭分明了,我也一直跟群裡的小夥伴說,未來的發展趨勢一定是多語言的大融合(這裡是後端編譯語言:c#,java,c++這三個,還有解釋語言 python),只需要提供各自的服務即可,我作為一個軟粉都已經支持 Java 的發展了,我承認 NetCore/c# 還有一些地方不是最優解
  • Rust vs Go:哪個更受歡迎?
    點擊上方藍色「Go語言中文網」關注我們,領全套Go資料,每天學習 Go 語言
  • Google程式設計師使用的頂級程式語言
    讓我們假設有一天你渴望作為程式設計師或開發人員在Google工作。你要問自己的第一個問題是什麼?最明顯的是,Google內部使用哪些程式語言?