《Go語言實戰》Go 類型:基本類型、引用類型、結構類型、自定義類型

Go 語言是一種靜態類型的編程語言,因此在編譯器進行編譯的時候,就要知道每一個值的類型,這樣編譯器就知道要爲這個值分配多少內存,而且知道這段分配的內存表示什麼。編程

提早知道值的類型的好處有不少,好比編譯器能夠合理的使用這些值,能夠進一步優化代碼,提升執行的效率,減小 bug 等等。數組

基本類型

基本類型是 Go 語言自帶的類型,好比 數值浮點字符串布爾數組錯誤 類型,他們本質上是原始類型,也就是不可改變的,因此對他們進行操做,通常都會返回一個新建立的值,因此把這些值傳遞給函數時,其實傳遞的是一個值的副本。安全

funcmain() {
	name := "張三"
	fmt.Println(modify(name))
	fmt.Println(name)
}

funcmodify(sstring)string{
	s = s + s
	return s
}
張三張三
張三

以上是一個操做字符串的例子,經過打印的結果,能夠看到,原本 name 的值並無被改變,也就是說,咱們傳遞的時一個副本,而且返回一個新建立的字符串。數據結構

基本類型由於是拷貝的值,而且在對他進行操做的時候,生成的也是新建立的值,因此這些類型在多線程裏是安全的,咱們不用擔憂一個線程的修改影響了另一個線程的數據。多線程

引用類型

引用類型和原始的基本類型偏偏相反,它的修改能夠影響到任何引用到它的變量。在 Go 語言中,引用類型有 切片(slice)字典(map)接口(interface)函數(func) 以及 通道(chan) 。編程語言

引用類型之因此能夠引用,是由於咱們建立引用類型的變量,實際上是一個標頭值,標頭值裏包含一個指針,指向底層的數據結構,當咱們在函數中傳遞引用類型時,其實傳遞的是這個標頭值的副本,它所指向的底層結構並無被複制傳遞,這也是引用類型傳遞高效的緣由。函數

本質上,咱們能夠理解函數的傳遞都是值傳遞,只不過引用類型傳遞的是一個指向底層數據的指針,因此咱們在操做的時候,能夠修改共享的底層數據的值,進而影響到全部引用到這個共享底層數據的變量。優化

funcmain() {
	ages := map[string]int{"張三": 20}
	fmt.Println(ages)
	modify(ages)
	fmt.Println(ages)
}

funcmodify(mmap[string]int) {
	m["張三"] = 10
}

這是一個很明顯的修改引用類型的例子,函數 modify 的修改,會影響到原來變量 ages 的值。spa

結構類型

結構類型是用來描述一組值的,好比一我的有身高、體重、名字和年齡等,本質上是一種聚合型的數據類型。線程

type person struct {
	age int
	name string
}

要定義一個結構體的類型,經過 type 關鍵字和類型 struct 進行聲明,以上咱們就定義了一個結構體類型 person ,它有 age , name 這兩個字段數據。

結構體類型定義好以後,就能夠進行使用了,咱們能夠用過 var 關鍵字聲明一個結構體類型的變量。

var p person

這種聲明的方式,會對結構體 person 裏的數據類型默認初始化,也就是使用它們類型的零值,若是要建立一個結構體變量並初始化其爲零值時,這種 var 方式最經常使用。

若是咱們須要指定非零值,就可使用咱們字面量方式了。

jim := person{10, "Jim"}

示例這種咱們就爲其指定了值,注意這個值的順序很重要,必須和結構體裏聲明字段的順序一致,固然咱們也能夠不按順序,可是這時候咱們必須爲字段指定值。

jim := person{name: "Jim", age: 10}

使用冒號 : 分開字段名和字段值便可,這樣咱們就不用嚴格的按照定義的順序了。

除了基本的原始類型外,結構體內的值也能夠是引用類型,或者本身定義的其餘類型。具體選擇類型,要根據實際狀況,好比是否容許修改值自己,若是容許的話,能夠選擇引用類型,若是不容許的話,則須要使用基本類型。

函數傳參是值傳遞,因此對於結構體來講也不例外,結構體傳遞的是其自己以及裏面的值的拷貝。

funcmain() {
	jim := person{10, "Jim"}
	fmt.Println(jim)
	modify(jim)
	fmt.Println(jim)
}

funcmodify(p person) {
	p.age = p.age + 10
}

type person struct {
	age int
	name string
}

以上示例的輸出是同樣的,因此咱們能夠驗證傳遞的是值的副本。若是上面的例子咱們要修改 age 的值能夠經過傳遞結構體的指針,咱們稍微改動下例子

funcmain() {
	jim := person{10, "Jim"}
	fmt.Println(jim)
	modify(&jim)
	fmt.Println(jim)
}

funcmodify(p *person) {
	p.age = p.age + 10
}

type person struct {
	age int
	name string
}

這個例子的輸出是

{10 Jim}
{20 Jim}

很是明顯的, age 的值已經被改變。若是結構體裏有引用類型的值,好比 map ,那麼咱們即便傳遞的是結構體的值副本,若是修改這個 map 的話,原結構的對應的 map 值也會被修改,這裏再也不寫例子,你們能夠驗證下。

自定義類型

Go 語言支持咱們自定義類型,好比剛剛上面的結構體類型,就是咱們自定義的類型,這也是比較經常使用的自定義類型的方法。

另一個自定義類型的方法是基於一個已有的類型,就是基於一個現有的類型創造新的類型,這種也是使用 type 關鍵字。

type Duration int64

咱們在使用 time 這個包的時候,對於類型 time.Duration 應該很是熟悉,它其實就是基於 int64 這個基本類型建立的新類型,來表示時間的間隔。

可是這裏咱們注意,雖然 Duration 是基於 int64 建立,以爲他們其實同樣,好比均可以使用數字賦值。

type Duration int64

var i Duration = 100
var j int64 = 100

可是本質上,他們並非同一種類型,因此對於Go這種強類型語言,他們是不能相互賦值的。

type Duration int64

var dur Duration
dur = int64(100)
fmt.Println(dur)

上面的例子,在編譯的時候,會報類型轉換的異常錯誤。

cannot use int64(100) (type int64) as type Duration in assignment

Go 的編譯器不會像 Java 的那樣,幫咱們作隱式的類型轉換。

有時候,你們會迷茫,已經有了 int64 這些類型了,能夠表示,還要基於他們建立新的類型作什麼?其實這就是 Go 靈活的地方,咱們可使用自定義的類型作不少事情,好比添加方法,好比能夠更明確的表示業務的含義等等。

相關文章
相關標籤/搜索