結構體(struct)是用戶自定義的類型,它表明若干字段的集合,能夠用於描述一個實體對象,相似java中的class,是golang面向對象編程的基礎類型。java
type Coordinate struct { X, Y float32 }
語法:type <Name> struct{}
上述代碼定義個一個名爲Coordinate
的結構體,裏面包括了兩個float32的變量X
,Y
,該結構體可用於表示一個平面座標。golang
在go語言中,對象方法在結構體定義的外部添加mongodb
type Coordinate struct { X, Y float32 } //打印座標 func (coo *Coordinate) GetCoordinate() { fmt.Printf("(%.2f,%.2f)\n", coo.X, coo.Y) return }
其中,func
關鍵字後面的(coo *Coordinate)
,表示該函數傳入一個指向Coordinate
的指針,可經過指針變量coo
來操做結構體的值 編程
package main import ( "fmt" ) type Coordinate struct { X, Y float32 } //打印座標 func (coo Coordinate) GetCoordinate() { fmt.Printf("(%.6f,%.4f)\n", coo.X, coo.Y) return } func main() { p0 := Coordinate{1, 2} p0.GetCoordinate() }
輸出:(1.00,2.00)
,其中X=1,Y=2json
package main import ( "fmt" ) type Coordinate struct { X, Y float32 } func (coo *Coordinate) GetCoordinate() { fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y) } func main() { p0 := Coordinate{Y: 1, X: 2} p0.GetCoordinate() }
輸出:X: 2.00000 Y: 1.0000函數
其中,func
關鍵字後面的(coo *Coordinate)
,表示該函數傳入一個指向Coordinate
的指針,可經過指針變量coo
來操做結構體的值。ui
package main import ( "fmt" ) type Coordinate struct { X, Y float32 } func (coo *Coordinate) GetCoordinate() { fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y) } func main() { p0 := new(Coordinate) p0.X = 2.012 p0.Y = 3.2645 p0.GetCoordinate() }
輸出結果:X: 2.01200 Y: 3.26453d
其中,第一種與第二種,p0
均爲一個類型爲Coordinate
的實例,而第三種p0
爲一個指向Coordinate
的指針,至關於var p0 *Coordinate = new(Coordinate)
指針
通常在進行例如type T struct {a, b int}的結構體定義以後 習慣使用t := new(T)給該結構體變量分配內存,它返回指向已分配內存的指針。變量t是一個指向T的指針,此時結構體字段的值是它們所屬類型的零值。 聲明 var t T 也會給 t 分配內存,並零值化內存,可是這個時候 t是類型T。在這兩種方式中,t 一般被稱作類型T的一個實例(instance)或對象(Object)。var t *T = new(T)等價於t := new(T)。
package main import ( "fmt" ) type Coordinate struct { X, Y float32 } func (coo *Coordinate) GetCoordinate() { fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y) } func main() { //p0是類型Coordinate的一個實例 p0 := Coordinate{1, 2} //給該結構體p2變量分配內存,它返回指向已分配內存的指針 p2 := new(Coordinate) p2.X = 1 p2.Y = 2 p3 := &Coordinate{X: 1, Y: 2} p4 := &Coordinate{1, 2} fmt.Println("-------輸出p0-------") fmt.Printf("%v\n%T\n", p0, p0) fmt.Println("-------輸出p2-------") fmt.Printf("%v\n%T\n", p2, p2) fmt.Println("-------輸出p3-------") fmt.Printf("%v\n%T\n", p3, p3) fmt.Println("-------輸出p4-------") fmt.Printf("%v\n%T\n", p4, p4) }
結果輸出:code
-------輸出p0------- {1 2} main.Coordinate -------輸出p2------- &{1 2} *main.Coordinate -------輸出p3------- &{1 2} *main.Coordinate -------輸出p4------- &{1 2} *main.Coordinate
能夠看出來,p2,p3,p4均爲一個指針變量
剛纔說到了,添加一個對象方法,能夠經過func (t *T) functionname()
來建立,其中t
爲一個指針變量。咱們也能夠經過值拷貝的方式,添加一個對象方法,語法爲func(t T) functionname()
package main import ( "fmt" ) type Coordinate struct { X, Y float32 } func (coo *Coordinate) GetCoordinate() { fmt.Printf("X,Y is : %.4f %.5f\n", coo.X, coo.Y) } //值拷貝對象方法 func (coo Coordinate) SetPosition01(a float32, b float32) { coo.X = a coo.Y = b } //指針變量對象方法 func (coo *Coordinate) SetPosition02(a float32, b float32) { coo.X = a coo.Y = b } func main() { p0 := Coordinate{1, 2} fmt.Print("SetPosition02調用前:") p0.GetCoordinate() p0.SetPosition02(0, 0) fmt.Print("SetPosition02調用後:") p0.GetCoordinate() p0.SetPosition01(-1.22, 1.44) fmt.Print("SetPosition01調用後:") p0.GetCoordinate() }
結果輸出:
SetPosition02調用前:X,Y is : 1.0000 2.00000 SetPosition02調用後:X,Y is : 0.0000 0.00000 SetPosition01調用後:X,Y is : 0.0000 0.00000
從程序輸出中能夠看出,調用SetPosition01
方法,發生了值拷貝,即便在方法內改變了coo
的值,外部的p0
的值沒有被改變。而SetPosition02
方法中,coo
爲指向p0
地址的指針,因爲是經過指針變量修改了X,Y
的值,因此調用完畢後,外部p0
的值會被修改成(0,0)
package main import ( "fmt" ) func main() { p_3d := struct { X, Y, Z float32 }{1, 2, 3} fmt.Println("-------輸出p_3d-------") fmt.Printf("%v\n%T\n", p_3d, p_3d) }
輸出:
-------輸出p_3d------- {1 2 3} struct { X float32; Y float32; Z float32 }
p_3d
爲一個包含X,Y,Z
三個變量的匿名結構體
在Go語言中沒有構造函數的概念,對象的建立一般交由一個全局的建立函數來完成,以
NewXXX
來命名,表示「構造函數」:
這一切很是天然,開發者也不須要分析在使用了new以後到底背後發生了多少事情。在Go語言中,一切要發生的事情都直接能夠看到。
—— 《Go語言編程》
func NewRect(x, y, width, height float64) *Rect { return &Rect{x, y, width, height} }
Go語言對關鍵字的增長很是吝嗇,其中沒有private
、protected
、public
這樣的關鍵 字。要使某個符號對其餘包(package
)可見(便可以訪問),須要將該符號定義爲以大寫字母開頭,如:
type Rect struct { X, Y float64 Width, Height float64 }
這樣,Rect類型的成員變量就所有被導出了,能夠被全部其餘引用了Rect所在包的代碼訪問到。 成員方法的可訪問性遵循一樣的規則,例如:
func (r *Rect) area() float64 { return r.Width * r.Height }
這樣,Rect
的area()
方法只能在該類型所在的包內使用。
須要注意的一點是,Go語言中符號的可訪問性是包一級的而不是類型一級的。在上面的例 子中,儘管area()
是Rect
的內部方法,但同一個包中的其餘類型也均可以訪問到它。這樣的可訪問性控制很粗曠,很特別,可是很是實用。若是Go語言符號的可訪問性是類型一級的,少不 了還要加上friend這樣的關鍵字,以表示兩個類是朋友關係,能夠訪問彼此的私有成員。
在處理json格式字符串的時候,常常會看到聲明struct結構的時候,屬性的右側還有反引號括起來的內容。形如
type User struct { UserId int `json:"user_id" bson:"user_id"` UserName string `json:"user_name" bson:"user_name"` }
要比較詳細的瞭解這個,要先了解一下golang的基礎,在golang中,命名都是推薦都是用駝峯方式,而且在首字母大小寫有特殊的語法含義:包外沒法引用。可是由常常須要和其它的系統進行數據交互,例如轉成json格式,存儲到mongodb啊等等。這個時候若是用屬性名來做爲鍵值可能不必定會符合項目要求。
反引號中的內容在golang中叫標籤(Tag),在轉換成其它數據格式的時候,會使用其中特定的字段做爲鍵值。例如上例在轉成json格式:
u := &User{UserId: 1, UserName: "tony"} j, _ := json.Marshal(u) fmt.Println(string(j)) // 輸出內容:{"user_id":1,"user_name":"tony"}
完整示例:
package main import ( "encoding/json" "fmt" ) type User struct { UserId int `json:"user_id" bson:"user_id"` UserName string `json:"user_name" bson:"user_name"` } func main() { u := &User{UserId: 1, UserName: "tony"} j, _ := json.Marshal(u) k, _ := json.MarshalIndent(u, "", " ") fmt.Println(string(j)) fmt.Printf("%s\n%T\n", j, j) fmt.Println(string(k)) fmt.Printf("%s\n%T\n", k, k) }
輸出:
{"user_id":1,"user_name":"tony"} {"user_id":1,"user_name":"tony"} []uint8 { "user_id": 1, "user_name": "tony" } { "user_id": 1, "user_name": "tony" } []uint8
若是在屬性中不增長標籤說明
package main import ( "encoding/json" "fmt" ) type User struct { UserId int UserName string } func main() { u := &User{UserId: 1, UserName: "tony"} j, _ := json.Marshal(u) k, _ := json.MarshalIndent(u, "", " ") fmt.Println(string(j)) fmt.Printf("%s\n%T\n", j, j) fmt.Println(string(k)) fmt.Printf("%s\n%T\n", k, k) }
則輸出:
{"UserId":1,"UserName":"tony"} {"UserId":1,"UserName":"tony"} []uint8 { "UserId": 1, "UserName": "tony" } { "UserId": 1, "UserName": "tony" } []uint8
能夠看到直接用struct的屬性名作鍵值。
其中還有一個bson的聲明,這個是用在將數據存儲到mongodb使用的。
那麼當咱們須要本身封裝一些操做,須要用到Tag中的內容時,如何去獲取呢?這邊可使用反射包(reflect)中的方法來獲取:
t := reflect.TypeOf(u) field := t.Elem().Field(0) fmt.Println(field.Tag.Get("json")) fmt.Println(field.Tag.Get("bson"))
完整示例:
package main import ( "encoding/json" "fmt" "reflect" ) func main() { type User struct { UserId int `json:"user_id" bson:"userid"` UserName string `json:"user_name" bson:"username"` } // 輸出json格式 u := &User{UserId: 1, UserName: "tony"} j, _ := json.Marshal(u) //return ([]byte, error) fmt.Printf("%c\n", j) fmt.Println(string(j)) // 輸出內容:{"user_id":1,"user_name":"tony"} // 獲取tag中的內容 t := reflect.TypeOf(u) field := t.Elem().Field(0) fmt.Println(field.Tag.Get("json")) // 輸出:user_id fmt.Println(field.Tag.Get("bson")) // 輸出:user_id }
結果輸出:
[{ " u s e r _ i d " : 1 , " u s e r _ n a m e " : " t o n y " }] {"user_id":1,"user_name":"tony"} user_id userid
beego的ORM中也經過tag來定義參數的。
package main import ( "fmt" "reflect" ) type Job struct { AlarmStatus *string `json:"alarm_status" name:"alarmstatus"` CPUTopology string `json:"cpu_topology" name:"cputopology"` } func main() { a := "abc" s := Job{&a, "hello"} st := reflect.TypeOf(s) field := st.Field(1) fmt.Println(field.Tag.Get("json"), field.Tag.Get("name")) }
package main import ( "fmt" "reflect" // 這裏引入reflect模塊 ) type User struct { Name string "user name" //這引號裏面的就是tag Passwd string "user passsword" } func main() { user := &User{"chronos", "pass"} s := reflect.TypeOf(user).Elem() //經過反射獲取type定義 for i := 0; i < s.NumField(); i++ { fmt.Println(s.Field(i).Tag) //將tag輸出出來 } }