Golang做爲Docker、Kubernetes和OpenShift等一些酷辣新技術的首選編程語言,愈來愈受歡迎。尤爲它們都是開源的,不少狀況下,開源是很是有價值的。深刻學習閱Golang等源代碼庫中的源文件,能夠更深地理解它們,同時也有利於其餘編程語言的開發者快速映射某些概念,好比Go與Java中經常使用概念的映射。本文的目的是幫助Java開發人員快速理解一些常見的Go習慣用法。java
Golang項目的一個常見約定是將全部cli二進制源文件或「main」包中的源文件放在根目錄cmd文件夾下。一般能夠在源根目錄的pkg文件夾中找到實現了不一樣功能的內聚類型、常量、變量和函數集的包。算法
Golang將其代碼組織成包,相似於Java。經過在源文件的頂部引入package來聲明源文件所在的包(以及它的全部常量、類型、函數等)。可是與Java不一樣,不須要輸入完整的包名+類名的路徑,只須要輸入包名便可。例如:編程
package apiapi
假設有一個包「api/endpoint」,那麼文件系統上就會有這個目錄結構(例如:/pkg/api/endpoint),可是endpoint包在endpoint目錄下的源文件中的聲明,應該是這樣的:數組
package endpoints 數據結構
使用如下命令能夠在程序中導入包,就像在Java中同樣:編程語言
import (函數
stderrs "errors"性能
"time"學習
"Golang.org/x/net/context"
"k8s.io/kubernetes/pkg/auth/user"
)
能夠根據包路徑中的最後一個包名在源代碼中使用包。例如,在上面的例子中,咱們導入k8s.io/kubernetes/pkg/auth/user,經過代碼,能夠用user.Foo()引用包中的元素。一樣也能夠在源文件中重命名包,這樣它就不會與其餘包名發生衝突,就像上面例子裏所示:
import (
stderrs "errors"
)
並在本身的程序源碼中直接引用stderrs.Foo()。
main包是Golang應用程序的入口點。main包必須有一個main()函數,該函數不接受參數,也不提供返回值。例如:
func main() { … }
如前所述,這個包一般位於根目錄的cmd文件夾中。
在Golang中,對於結構/類型/函數/變量在包外部的做用域和可見性,其標識符的首字符很是重要。例如,在一個foo包中,若是有一個名爲func Bar()的函數,那麼由於「Bar」的第一個字母是大寫的,因此它在包以外是可用的(注:相似於java中的public)。所以,若是導入了foo包,就可以調用foo.Bar()函數。若是「bar」是小寫的,它將被隱藏起來(相似於java中的private)。也就是說,第一個字母的大小寫決定了其做用域與可見性。
Golang中的函數或方法(二者有區別)能夠返回「元組」或多個值,與java有明顯差別。例如,調用一個返回多個值的函數以下所示:
internalCtx, ok := foo.bar(context.Context)
其中,internalCtx表示函數內容,ok可表示函數調用成功或失敗標識。
在Java中有類,但在Go中與之類似的概念是結構體(Struct)。struct也能夠有成員和方法。以下所示:
type Rectangle struct {
width int
height int
}
這是一個名爲「Rectangle」的數據結構,它有兩個成員變量(也能夠稱爲字段,原文中爲fields):寬度和高度。能夠像這樣建立實例:
r := new(Rectangle)
還能夠這樣引用它的成員變量(fields):
r.width = 10
r.height = 5
咱們能夠在「Rectangle」數據結構上編寫方法,以下所示:
func (r *Rectangle) area() int {
return r.width * r.height
}
這裏的方法名稱爲area,能夠這麼來調用上面的方法:
r := new(Rectangle)
r.area()
Golang在設計上未採用Java的「繼承(extends)」,它的繼承是經過組合來完成的。例如:
type Rectangle struct {
Shape
width int
height int
}
上面Rectangle結構中有一個類型爲Shape的匿名成員。Shape中包含的全部字段和方法在Rectangle對象上都是可見的。可是須要注意的是,不像在Java中,能夠將Rectangle傳遞給Shape爲參數的函數,這在Go中是行不通的。要得到這種類型的多態性,應該使用Go接口。
在Java中有特定的接口類型,這些接口類型定義了對象的行爲。在Go中,也有相似的概念,能夠經過intefaces來實現。例如,下面這個接口聲明瞭一個具備Print()方法的Shape類型:
type Shape interface {
Print()
}
當使用Go來建立結構時,不須要像在Java中那樣用「implementation」來聲明它。它是隱式的,只須要實現了該接口對應的方法,對應的結構體就能夠被傳遞給須要的函數:
type Rectangle struct {
width int
height int
}
func (r *Rectangle) Print() {
fmt.println("Rectangle!");
}
此時,Rectangle對象能夠傳遞給任何接收Shape類型的函數,由於它實現了該類型的全部方法。
Go中的For循環,樣例以下:
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
然而,當迭代一個數組(或相似於數組的東西,例如,字符串,映射,切片等),可使用range運算符(假設foo是一個列表List):
for v := range foo {
fmt.println("value="+v);
}
若是在遍歷列表時須要知道該列表的索引,則能夠這樣編寫代碼:
for i, v := range foo {
fmt.println("index " + i +"has value="+v);
}
Go中還能夠像這樣再次使用for循環:
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
或者實現一個無限while循環:
for {
something...
}
Golang中須要顯式地使用指針和引用,而Java一般隱藏這些。例如,Java中能夠這樣作:
Shape shape = new Shape();
shape.foo();
可是在Go中,必須直接處理指針:
type Rectangle struct {
width int
height int
}
func updateRectangle(r *Rectangle){
r.width = 5;
r.height = 10;
}
func main() {
r := Rectangle{20,30}
updateRectangle(&r)
}
當main函數執行完畢時,Rectangle對象中r.width=5,r.height=10。注意:必須顯式地引用指針。
Golang與java相似,也是一種垃圾收集語言。Go開發者不須要手動來釋放程序中再也不使用的變量和結構佔用的內存,在Go的運行時中有一個獨立的進程,即垃圾收集器(GC),會處理這些事,它會經過標記算法搜索再也不使用的變量而後釋放內存。
經過調用runtime.GC()函數能夠顯式的觸發GC,但這隻在某些特殊的場景下才會使用,好比當內存資源不足時調用runtime.GC(),它會在此函數執行的點上當即釋放內存,此時程序可能會有短時的性能降低(因爲GC進程的執行)。若是想知道當前的內存狀態,也可使用以下代碼:
var mruntime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("%dKb\n", m.Alloc / 1024)
上面的程序會給出已分配內存的總量,單位是 Kb。
原文做者:Christian Posta 譯者:江瑋
原文連接:https://dzone.com/articles/quick-go-lang-for-java-developers