面向?qū)ο缶幊?/h2>
- Go支持面向?qū)ο缶幊烫匦?,但和傳統(tǒng)面向?qū)ο缶幊淌怯袇^(qū)別的,Go并不是純粹的面向?qū)ο笳Z(yǔ)言。
- Go沒(méi)有類(lèi),其結(jié)構(gòu)體和其它編程語(yǔ)言中的類(lèi)具有同等的地位,可理解Go是基于結(jié)構(gòu)體來(lái)實(shí)現(xiàn)面向?qū)ο筇匦缘摹?/li>
- Go面向?qū)ο缶幊谭浅:?jiǎn)介,去掉了傳統(tǒng)面向?qū)ο笳Z(yǔ)言的繼承、方法重載、構(gòu)造函數(shù)、析構(gòu)函數(shù)、隱藏的
this指針等。
- Go仍然具有面向?qū)ο缶幊痰睦^承、封裝、多態(tài)的特性,只是實(shí)現(xiàn)方式和傳統(tǒng)面向?qū)ο笳Z(yǔ)言不同。
- Go面向?qū)ο蠛軆?yōu)雅,面向?qū)ο蟊旧砭褪钦Z(yǔ)言類(lèi)型系統(tǒng)的一部分,通過(guò)接口關(guān)聯(lián),耦合性低,也非常靈活。
- Go中面向接口編程是非常重要的特性
結(jié)構(gòu)體與結(jié)構(gòu)體變量
- 結(jié)構(gòu)體:自定義的數(shù)據(jù)類(lèi)型,表示一類(lèi)事物。
type struct_name struct {
field1 datatype
field2 datatype
}
//定義結(jié)構(gòu)體
type User struct {
Id int
Name string
Birth string
Gender int
}
- 結(jié)構(gòu)體變量:又稱(chēng)為實(shí)例或?qū)ο?,是具體的、實(shí)際的,代表一個(gè)具體變量。
//結(jié)構(gòu)體變量
var u User
u.Id = 1
u.Name = "alice"
u.Birth = "2020-12-21"
u.Gender = 1
結(jié)構(gòu)體字段
- 結(jié)構(gòu)體字段又稱(chēng)為屬性,字段是結(jié)構(gòu)體組成部分,一般是基礎(chǔ)數(shù)據(jù)類(lèi)型、數(shù)組,也可以是引用類(lèi)型。
- 字段聲明語(yǔ)法同變量一樣采用
字段名 字段類(lèi)型
- 字段類(lèi)型可分為:基本類(lèi)型、數(shù)組、引用類(lèi)型
- 創(chuàng)建結(jié)構(gòu)體變量后若沒(méi)有給字段賦值,都會(huì)對(duì)應(yīng)一個(gè)零值作為默認(rèn)值。
- 不同結(jié)構(gòu)體變量的字段是獨(dú)立互不影響,一個(gè)結(jié)構(gòu)體變量的更改不會(huì)影響到另外一個(gè),因?yàn)榻Y(jié)構(gòu)體是值類(lèi)型默認(rèn)是值拷貝。
this指針等。type struct_name struct {
field1 datatype
field2 datatype
}
//定義結(jié)構(gòu)體
type User struct {
Id int
Name string
Birth string
Gender int
}
//結(jié)構(gòu)體變量
var u User
u.Id = 1
u.Name = "alice"
u.Birth = "2020-12-21"
u.Gender = 1
字段名 字段類(lèi)型
| 字段類(lèi)型 | 類(lèi)型 | 零值 |
|---|---|---|
| 布爾類(lèi)型 | 基礎(chǔ) | false |
| 整型 | 基礎(chǔ) | 0 |
| 字符串 | 基礎(chǔ) | "" |
| 數(shù)組類(lèi)型 | 基礎(chǔ) | 默認(rèn)值與元素類(lèi)型相關(guān) |
指針ptr/切片slice/映射map
|
引用 | nil,未分配空間。 |
結(jié)構(gòu)體字段類(lèi)型若存在指針、slice、map,它們的零值都是nil,即還沒(méi)有分配空間,如果需要使用必須先make()。
| 類(lèi)型 | 創(chuàng)建 |
|---|---|
| 指針 | new() |
| 切片 | make() |
| 映射 | make() |
//定義結(jié)構(gòu)體
type User struct {
Id int
array [5]float64
slice []int
ptr *int
dict map[string]string
}
//結(jié)構(gòu)體變量
var u User
fmt.Printf("%+v\n", u) // {Id:0 array:[0 0 0 0 0] slice:[] ptr:<nil> dict:map[]}
fmt.Printf("%t\n", u.slice == nil) //true
fmt.Printf("%t\n", u.ptr == nil) //true
fmt.Printf("%t\n", u.dict == nil) //true
創(chuàng)建結(jié)構(gòu)體變量
//創(chuàng)建結(jié)構(gòu)體變量
var u1 User //直接聲明
fmt.Printf("%+v\n", u1) //{Id:0}
var u2 User = User{}
fmt.Printf("%+v\n", u2) //{Id:0}
采用結(jié)構(gòu)體指針?lè)绞絼?chuàng)建后,結(jié)構(gòu)體指針訪(fǎng)問(wèn)字段的標(biāo)準(zhǔn)方式應(yīng)該是 (*結(jié)構(gòu)體指針).字段名,但Go做了簡(jiǎn)化也支持結(jié)構(gòu)體指針.字段名,因?yàn)檫@種方式更加符合程序員的使用習(xí)慣。Go編譯器底層會(huì)對(duì)簡(jiǎn)化方式做自動(dòng)轉(zhuǎn)換。
var u3 *User = new(User)
fmt.Printf("%+v\n", u3) //&{Id:0}
var u4 *User = &User{}
fmt.Printf("%+v\n", u4) //&{Id:0}
簡(jiǎn)化寫(xiě)法
var u *User = new(User)
fmt.Printf("%+v\n", u) //&{Id:0}
(*u).Id = 100
fmt.Printf("%+v\n", *u)
u.Id = 200
fmt.Println(u)
由于u是一個(gè)指針,因此標(biāo)準(zhǔn)的給字段賦值的方式應(yīng)該采用的是(*u).Id = 100,但也可以直接采用u.Id = 200。由于Go設(shè)計(jì)者為了程序員使用方便,底層會(huì)對(duì)u.Id = 200添加取值運(yùn)算。
創(chuàng)建結(jié)構(gòu)體實(shí)例時(shí)可直接指定字段的值
type User struct {
Id int
Name string
}
func main() {
u := User{1, "alice"}
fmt.Printf("%+v\n", u) // {Id:1 Name:alice}
}
內(nèi)存分配機(jī)制
例如:結(jié)構(gòu)體是值類(lèi)型,默認(rèn)賦值會(huì)拷貝副本,若需采用引用類(lèi)型方式,則需使用結(jié)構(gòu)體變量指針賦值。
u1 := User{1}
u2 := &u1 // var u2 *User = &u1
u2.Id = 100
fmt.Printf("%+v\n", u1) // {Id:100}
fmt.Printf("%+v\n", u2) // &{Id:100}
fmt.Printf("u1=%p, u2=%p\n", &u1, u2) // u1=0xc0000aa058, u2=0xc0000aa058
.點(diǎn)運(yùn)算符優(yōu)先級(jí)要高于*星號(hào)運(yùn)算符,下列為錯(cuò)誤寫(xiě)法:
u1 := User{1}
u2 := &u1 // var u2 *User = &u1
u2.Id = 100
fmt.Printf("%+v\n", *u2.Id) // invalid indirect of u2.Id (type int) (exit status 2)
注意事項(xiàng)
結(jié)構(gòu)體字段在內(nèi)存中是連續(xù)分布的
type Point struct {
x, y int
}
type Rect struct {
leftUp, rightDown Point
}
func main() {
r := Rect{
Point{0, 0},
Point{4, 4},
}
fmt.Printf("%p\n", &r.leftUp.x) // 0xc0000a8080
fmt.Printf("%p\n", &r.leftUp.y) // 0xc0000a8088
fmt.Printf("%p\n", &r.rightDown.x) // 0xc0000a8090
fmt.Printf("%p\n", &r.rightDown.y) // 0xc0000a8098
}
指針本身的地址是連續(xù)但指向的地址不一定是連續(xù)的
type Point struct {
x, y int
}
type Rect struct {
leftUp, rightDown *Point
}
func main() {
r := Rect{
&Point{0, 0},
&Point{4, 4},
}
fmt.Printf("%p\n", &r.leftUp) // 0xc000088220
fmt.Printf("%p\n", &r.rightDown) // 0xc000088228
fmt.Printf("%p\n", r.leftUp) // 0xc0000aa070
fmt.Printf("%p\n", r.rightDown) // 0xc0000aa080
}
結(jié)構(gòu)體是用戶(hù)單獨(dú)定義的類(lèi)型,和其它類(lèi)型進(jìn)行轉(zhuǎn)換時(shí)需具有完全相同的字段(名字、個(gè)數(shù)、類(lèi)型)。
type Point struct {
x, y int
}
type Axis struct {
x, y int
}
func main() {
p1 := Point{0, 0}
p2 := Axis(p1)
fmt.Printf("%+v\n", p1) // {x:0 y:0}
fmt.Printf("%+v\n", p2) // {x:0 y:0}
}
結(jié)構(gòu)體進(jìn)行type重新定義時(shí)相當(dāng)于取別名,Go會(huì)認(rèn)為是新的數(shù)據(jù)類(lèi)型,但相互之間可以強(qiáng)轉(zhuǎn)。
type Point struct {
x, y int
}
type Axis Point
func main() {
var p1 Point
var p2 Axis
//p1 = p2 // cannot use p2 (type Axis) as type Point in assignment (exit status 2)
p1 = Point(p2)
fmt.Printf("%+v %+v\n", p1, p2) // {x:0 y:0} {x:0 y:0}
}
結(jié)構(gòu)體字段上可添加一個(gè)標(biāo)簽tag,標(biāo)簽可通過(guò)反射機(jī)制獲取,比如序列化和反序列化中。
type Response struct {
Code int `json:"code"`
Msg string `json:"msg`
Data interface{} `json:"data"`
}
func main() {
response := Response{200, "success", nil}
buf, err := json.Marshal(response)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("%s\n", buf) // {"code":200,"Msg":"success","data":null}
}
工廠(chǎng)模式
Go的結(jié)構(gòu)體沒(méi)有構(gòu)造函數(shù),可通過(guò)工廠(chǎng)模式來(lái)實(shí)例化結(jié)構(gòu)體。
// 結(jié)構(gòu)體
type user struct {
Id int
Name string
}
// 工廠(chǎng)模式
func User(id int, name string) *user {
return &user{
Id: id,
Name: name,
}
}
func main() {
u := User(1, "alice")
fmt.Printf("%+v\n", u) // &{Id:1 Name:alice}
}