1.簡介
- go也支持面向對象編程(OOP),但是和傳統的面向對象編程有區別,并不是純粹的面向對象語言。所以說go支持面向對象編程特性是比較準確的。
- go沒有類(class),go語言的結構體(struct)和其它編程語言的類(class)有同等的地位,你可以理解go是基于struct來實現OOP特性的。
- go面向對象編程非常簡潔,去掉了傳統OOP語言的繼承、方法重載、構造函數和析構函數、隱藏的this指針等。
- go仍然有面向對象編程的繼承、封裝和多態的特性,只是實現的方式和其他OOP語言不一樣,比如繼承:go沒有extends關鍵字,繼承是通過匿名字段來實現。
- go面向對象(OOP)很優雅,OOP本身就是語言類型系統(type system)的一部分,通過接口(interface)關聯,耦合性低,也非常靈活。也就是說在go中面向接口編程是非常重要的特性。
2.type關鍵詞自定義類型和類型別名
2.1自定義類型
在go語言中有一些基本的數據類型,比如string、int、bool等數據類型,可以用type關鍵字自定義類型。
語法: type comstomInt int
? 將comstomInt 定義為int類型,通過type關鍵字的定義,comstomInt就是一種新的類型,comstomInt和int不是同一類型,它具有int的特性。
type comstomInt int var a comstomInt = 3var b int = 3fmt.Printf("a:%v %T\n", a, a) //a:3 main.comstomIntfmt.Printf("b:%v %T\n", b, b) //b:3 int
2.2類型別名
類型別名規定:TypeAlias只是Type的別名,本質上TypeAlias與Type是同一類型。就像外號和真實姓名都是指的同一個人。
語法: type intAlias=int
我們之前見過的rune和byte就是類型別名:
type byte = uint8
type rune = int32
2.3自定義類型和類型別名的區別
類型別名與自定類型表面上看只有一個等號的差異,我們通過下面的這段代碼來理解他們之間的卻別。
type comstomInt int
type intAlias = intfunc main() {var a intAlias = 3fmt.Printf("a:%v %T\n", a, a) //a:3 intvar b comstomInt = 3fmt.Printf("b:%v %T\n", b, b) //b:3 main.comstomInt
}
結果顯示a的類型時int類型。b的類型時main.comstomInt,表示main包下定義的comstomInt類型。
3.結構體的定義
使用type和struct關鍵字來定義結構體,語法如下:
type 類型名 struct{
屬性1 類型
屬性2 類型
...
}
解釋:
- 類型名:表示自定義結構體的名稱,在一個包內不能重復。(首字母大寫可以外部訪問)
- 屬性:表示結構體字段名,結構體中的字段必須唯一。(首字母大寫可以外部訪問)
- 類型:表示結構體屬性的具體類型。
?舉個例子,我們定義一個學生Student的結構體:
type Student struct {Name stringNumber intSex intAge int
}
?同樣類型的字段也可以寫在一行。
type Student struct {Name stringNumber,Sex,Age int
}
4.結構體的實例化?
簡述:只有當結構體實例化時,才會真正地分配內存。也就是必須示例化以后才能使用結構體的字段。
- 結構體是自定義的數據類型,代表一類事物。
- 結構體變量(實例)是具體的,實際的,代表一個具體變量。
- 同一個結構體變量(實例)之間是相對獨立的,變量不會相互影響。
4.1第一種方法
結構體本身也是一種數據類型,我們可以像聲明內置類型一樣使用var關鍵字聲明結構提類型。
var? 結構體實例? 結構體類型
type Student struct {Name string //姓名Number int //學號
}func main() {var s1 Students1.Name = "韓梅梅"s1.Number = 20250306123fmt.Printf("s1=%#v\n", s1)//s1=main.Student{Name:"韓梅梅", Number:20250306123}
}
4.2第二種方法
?可以通過new關鍵字對結構體進行實例化,得到的是具體的地址。
type Student struct {Name string //姓名Number int //學號
}func main() {s1 := new(Student)s1.Name = "韓梅梅"s1.Number = 20250306123fmt.Printf("s1=%#v\n", s1) //s1=&main.Student{Name:"韓梅梅", Number:20250306123}fmt.Println(s1.Name) //韓梅梅fmt.Println((*s1).Name) //韓梅梅
}
從結果可以看出s1是一個結構體指針。
注意:在go中支持對結構體指針直接使用點(.)來訪問結構體成員。s1.?Name和(*s1).Name輸出是一樣的,go設計者為了程序員使用方便,底層會對結構體進行處理。
4.3第三種方法
使用&對結構體進行取地址操作相當于對該結構體類型進行了new的實例化操作。
type Student struct {Name string //姓名Number int //學號
}func main() {s1 := &Student{}s1.Name = "韓梅梅"s1.Number = 20250306123fmt.Printf("s1=%#v\n", s1) //s1=&main.Student{Name:"韓梅梅", Number:20250306123}fmt.Println(s1.Name) //韓梅梅fmt.Println((*s1).Name) //韓梅梅
}
4.4第四種方法
?鍵值對初始化:
type Student struct {Name string //姓名Number int //學號
}func main() {s1 := Student{Name: "蕾蕾",Number: 20250306123,}fmt.Printf("s1=%#v\n", s1) //s1=main.Student{Name:"蕾蕾", Number:20250306123}fmt.Println(s1.Name) //蕾蕾
}
4.5第五種方法
?結構體指針進行鍵值對初始化:
type Student struct {Name string //姓名Number int //學號
}func main() {s1 := &Student{Name: "蕾蕾",}fmt.Printf("s1=%#v\n", s1) //s1=&main.Student{Name:"蕾蕾", Number:0}fmt.Println(s1.Name) //蕾蕾fmt.Println((*s1).Name) //蕾蕾
}
注意:在創建一個結構體變量后,如果沒有給字段賦值,都對應一個零值(默認值)。
布爾類型是false,數值是0,字符串是“”。
數組類型的默認值和它的元素類型相關,比如score[3]int 則為[0,0,0]。
指針,slice和map的零值都是nil,即還沒有分配空間。如果需要使用這樣的字段需要先make,才能使用。
4.6第六種方法
使用列表初始化,也就是初始化的時候不寫鍵,直接寫值。
注意:
- 必須初始化結構體的所有字段。
- 初始化的填充順序必須與字段在結構體中的聲明順序一致。
- 該初始化不能和鍵值初始化方式混用。
type Student struct {Name string //姓名Number int //學號}func main() {s1 := &Student{"蕾蕾",1234567890,}fmt.Printf("s1=%#v\n", s1) //s1=&main.Student{Name:"蕾蕾", Number:1234567890}fmt.Println(s1.Name) //蕾蕾fmt.Println((*s1).Name) //蕾蕾
}
5.結構體的方法和接收者
5.1結構體是值類型
package mainimport "fmt"type Person struct {Name stringAge int
}func main() {p1 := Person{"小王子",18,}p2 := p1p2.Name = "kings"fmt.Printf("%#v\n", p1) //main.Person{string:"小王子", int:18}fmt.Printf("%#v\n", p2) //main.Person{Name:"kings", Age:18}
}
?在go語言中,沒有類的概念但是可以給類型(結構體,自定義類型)定義方法。所謂方法 就是定義了接收者的函數。接收者的概念就類似于其他語言中的this或者 self。
方法的定義格式如下:
func (接收者變量 接收者類型) 方法名(參數列表) (返回參數) { 函數體 }
其中
- 接收者變量:接收者中的參數變量名在命名時,官方建議使用接收者類型名的第一個小 寫字母,而不是self、this之類的命名。例如,Person類型的接收者變量應該命名為 p, Connector 類型的接收者變量應該命名為c等。
- 接收者類型:接收者類型和參數類似,可以是指針類型和非指針類型。
- 方法名、參數列表、返回參數:具體格式與函數定義相同。
給結構體Person定義一個方法打印Person的信息?:
package mainimport "fmt"type Person struct {name stringage int8
}func (p Person) printInfo() {fmt.Printf("姓名:%v 年齡:%v", p.name, p.age)
}
func main() {p1 := Person{name: "李雷",age: 15,}p1.printInfo() //姓名:李雷 年齡:15
}
對上面類型說明:
- ?func (p Person) printInfo() {} 表示Person結構體有一方法,方法名為printInfo。
- (p Person)體現printInfo方法是和Person類型綁定的。
- printInfo方法只能通過Person類型的變量調用,而不能直接調用,也不能使用其他類型變量來調用。
- ?func (p Person) printInfo() {}? p表示那么Person變量調用,這個p就是它的副本,這點和函數傳參非常相似。
5.2值類型的接收者
當方法作用于值類型接收者時,Go語言會在代碼運行時將接收者的值復制一份。在值類型 接收者的方法中可以獲取接收者的成員值,但修改操作只是針對副本,無法修改接收者變量 本身。
5.3指針類型的接收者
指針類型的接收者由一個結構體的指針組成,由于指針的特性,調用方法時修改接收者指針 的任意成員變量,在方法結束后,修改都是有效的。這種方式就十分接近于其他語言中面向 對象中的this或者self。?
package mainimport "fmt"type Person struct {name stringage int
}// 值類型接受者
func (p Person) printInfo() {fmt.Printf("姓名:%v年齡:%v\n", p.name, p.age)
}// 指針類型接收者
func (p *Person) setInfo(name string, age int) {p.name = namep.age = age
}
func main() {p1 := Person{name: "小王子",age: 25,}p1.printInfo() //姓名:小王子年齡:25p1.setInfo("張三", 20)p1.printInfo() //姓名:張三年齡:20
}
6.給任意類型添加方法
?在Go語言中,接收者的類型可以是任何類型,不僅僅是結構體,任何類型都可以擁有方法。 舉個例子,我們基于內置的int類型使用type關鍵字可以定義新的自定義類型,然后為我們 的自定義類型添加方法。
package mainimport "fmt"type myInt intfunc (m myInt) SayHello() {fmt.Println("Hello,我是一個int。")
}
func main() {var m1 myIntm1.SayHello() //Hello,我是一個int。m1 = 100fmt.Printf("%#v %T\n", m1, m1) //100 main.MyInt
}
注意事項:非本地類型不能定義方法,也就是說我們不能給別的包的類型定義方法。
7.結構體的匿名字段
?結構體允許其成員字段在聲明時沒有字段名而只有類型,這種沒有名字的字段就稱為匿名字 段。
import "fmt"type Person struct {stringint
}func main() {p1 := Person{"小王子",18,}fmt.Printf("%#v\n", p1) //main.Person{string:"小王子", int:18}fmt.Println(p1.string, p1.int) //小王子 18
}
匿名字段默認采用類型名作為字段名,結構體要求字段名稱必須唯一,因此一個結構體中同 種類型的匿名字段只能有一個。?
8.嵌套結構體
一個結構體中可以嵌套包含另一個結構體或者結構體指針。
package mainimport "fmt"type Address struct {City stringPhone string
}
type User struct {Name stringSex stringAddressKey Address
}
func main() {user := User{Name: "張三",Sex: "男",AddressKey: Address{City: "杭州",Phone: "132*****321",},}fmt.Printf("%#v\n", user)//main.User{Name:"張三", Sex:"男", AddressKey:main.Address{City:"杭州", Phone:"132*****321"}}
}
9.嵌套匿名結構體
package mainimport "fmt"type Address struct {City stringPhone string
}
type User struct {Name stringSex stringAddress //匿名結構體
}func main() {user := User{Name: "張三",Sex: "男",Address: Address{City: "杭州",Phone: "132*****321",},}user.Address.City = "北京" //通過匿名結構體.字段名訪問user.City = "上海" //直接訪問匿名結構體的字段名fmt.Printf("%#v\n", user) //main.User{Name:"張三", Sex:"男", AddressKey:main.Address{City:"杭州", Phone:"132*****321"}}
}
?注意:當訪問結構體成員時會先在結構體中查找該字段,找不到再去匿名結構體中查找。
10.嵌套匿名結構體的字段名沖突
package mainimport ("fmt""time"
)type Address struct {City stringPhone stringCreateTime string
}
type Email struct {Acount stringCreateTime string
}
type User struct {Name stringSex stringAddress //匿名結構體Email //匿名結構體
}func main() {user := User{Name: "張三",Sex: "男",Address: Address{City: "杭州",Phone: "132*****321",},}user.Email.CreateTime = time.Now().Format("2006-01-02 03:04:05")//指定Email結構體中的CreateTimeuser.Address.CreateTime = time.Now().Format("2006-01-02 03:04:05")//指定Address結構體中的VreateTimefmt.Printf("%#v\n", user) //main.User{Name:"張三", Sex:"男", Address:main.Address{City:"杭州", Phone:"132*****321", CreateTime:"2025-03-16 05:49:43"}, Email:main.Email{Acount:"", CreateTime:"2025-03-16 05:49:43"}}
}
注意:嵌套結構體內可能存在相同的字段名稱,這時候為了避免歧義需要自定具體的嵌套結構體字段。?
11.結構體的繼承
go語言中使用結構體也可以實現其他編程語言中的繼承關系,利用匿名結構體嵌套實現繼承。
package mainimport "fmt"type Animal struct {Name string
}func (receiver Animal) printName() {fmt.Println("名字:", receiver.Name)
}type Dog struct {Color stringAge int*Animal
}func main() {dog := &Dog{}dog.Animal = &Animal{Name: "大黃",}dog.printName() //名字:大黃
}
12.json數據
JSON是一種輕量級的數據交換格式,易于閱讀和編寫,同時也易于機器解析和生成。
json的基本格式:
{"avgSeatView": "4.8%","avgShowView": "7.5","boxRate": "58.6%","boxSplitUnit": {"num": ".","unit": "萬"},"movieInfo": {"movieId": 1294273,"movieName": "哪吒之魔童鬧海","releaseInfo": "上映47天"},"showCount": 142769,"showCountRate": "36.1%","splitBoxRate": "59.0%","splitBoxSplitUnit": {"num": ".","unit": "萬"},"sumBoxDesc": "148.58億","sumSplitBoxDesc": "134.35億"}
13.結構體與JSON序列化
go json序列化是指把結構體數據轉化成json格式的字符串,go json的反序列化是指把json數據轉化成go中的結構體對象。
go中的序列化和反序列化主要通過“encoding/json”包中的json.Marshal()和json.Unmarshal()方法實現。
13.1結構體轉JSON字符串
package mainimport ("encoding/json""fmt"
)type Persion struct {ID intSex stringName stringHobby []string
}func main() {p := Persion{ID: 10001,Sex: "男",Name: "趙六",Hobby: []string{"籃球", "足球", "羽毛球"},}fmt.Printf("%#v\n", p) //main.Persion{ID:10001, Sex:"男", Name:"趙六", Hobby:[]string{"籃球", "足球", "羽毛球"}}by, err := json.Marshal(p)if err != nil {fmt.Println("轉譯錯誤!!")} else {fmt.Println(string(by)) //{"ID":10001,"Sex":"男","Name":"趙六","Hobby":["籃球","足球","羽毛球"]}}
}
13.2json字符串轉換成結構體對象
package mainimport ("encoding/json""fmt"
)type Persion struct {ID intSex stringName stringHobby []string
}func main() {str := `{"ID":10001,"Sex":"男","Name":"趙六","Hobby":["籃球","足球","羽毛球"]}`var p Persionerr := json.Unmarshal([]byte(str), &p)if err != nil {fmt.Println("序列化錯誤!")} else {fmt.Printf("%#v\n", p) //main.Persion{ID:10001, Sex:"男", Name:"趙六", Hobby:[]string{"籃球", "足球", "羽毛球"}}}
}
14.結構體標簽tag
Tag是結構體的元素信息,可以在運行的時候通過反射的機制讀取出來。tag在結構體字段的后方定義,由一對反引號包裹起來,具體格式如下:
`key1:"value1"? key2:"value2"`
結構體tag由一個或多個鍵值組成。鍵與值使用冒號分隔,值用雙引號括起來。同一個結構體字段可以設置多個鍵值對tag,不同的鍵值對之間使用空格分隔。
注意事項:為結構體編寫tag時,必須嚴格遵守鍵值對的規則。結構體標簽的解析代碼的容錯能力很差,一旦格式寫錯,編譯和運行時都不會提示任何錯誤,通過反射也無法正確取值,列如不要在key和value之間添加空格。
14.結構體轉json字符串
package mainimport ("encoding/json""fmt"
)type Persion struct {ID int `json:"id"` //通過指定tag實現json序列化該字段時key(id)Sex string `json:"sex"`Name string `json:"name"`Hobby []string `json:"hobby"`
}func main() {p := Persion{ID: 10001,Sex: "男",Name: "趙六",Hobby: []string{"籃球", "足球", "羽毛球"},}by, err := json.Marshal(p)if err != nil {fmt.Println("轉譯錯誤!!")} else {fmt.Println(string(by)) // {"id":10001,"sex":"男","name":"趙六","hobby":["籃球","足球","羽毛球"]}}
}
14.2json字符串轉化成結構體
package mainimport ("encoding/json""fmt"
)type Persion struct {ID int `json:"id"` //通過指定tag實現json序列化該字段時key(id)Sex string `json:"sex"`Name string `json:"name"`Hobby []string `json:"hobby"`
}func main() {str := `{"id":10001,"sex":"男","name":"趙六","hobby":["籃球","足球","羽毛球"]}`var p Persionerr := json.Unmarshal([]byte(str), &p)if err != nil {fmt.Println("序列化錯誤!")} else {fmt.Printf("%#v\n", p) //main.Persion{ID:10001, Sex:"男", Name:"趙六", Hobby:[]string{"籃球", "足球", "羽毛球"}}}
}
15.嵌套結構體和json序列化反序列化
15.1結構體轉化成json字符串
package mainimport ("encoding/json""fmt"
)type Student struct {ID int `json:"id"` //通過指定tag實現json序列化該字段時key(id)Sex string `json:"sex"`Name string `json:"name"`Hobby []string `json:"hobby"`
}
type Class struct {No string `json:"no"`Student []Student `json:"student"`
}func main() {var cl Classcl.No = "六年級一班"stu := make([]Student, 0)for i := 0; i < 10; i++ {s := Student{ID: 1000 + i,Sex: "男",Name: fmt.Sprintf("王五%v", i),Hobby: []string{"java", "php", "golang"},}stu = append(stu, s)}cl.Student = stuby, err := json.Marshal(cl)if err != nil {fmt.Println("轉譯錯誤!")} else {fmt.Println(string(by))}
}
結果:
{"no":"六年級一班","student":[{"id":1000,"sex":"男","name":"王五0","hobby":["java","php","golang"]},{"id":1001,"sex":"男","name":"王五1","hobby":["java","php","golang"]},{"id":1002,"sex":"男","name":"王五2","hobby":["java","php","golang"]},{"id":1003,"sex":"男","name":"王五3","hobby":["java","php","golang"]},{"id":1004,"sex":"男","name":"王五4","hobby":["java","php","golang"]},{"id":1005,"sex":"男","name":"王五5","hobby":["java","php","golang"]},{"id":1006,"sex":"男","name":"王五6","hobby":["java","php","golang"]},{"id":1007,"sex":"男","name":"王五7","hobby":["java","php","golang"]},{"id":1008,"sex":"男","name":"王五8","hobby":["java","php","golang"]},{"id":1009,"sex":"男","name":"王五9","hobby":["java","php","golang"]}]}
15.2json字符串轉化成結構體
package mainimport ("encoding/json""fmt"
)type Student struct {ID int `json:"id"` //通過指定tag實現json序列化該字段時key(id)Sex string `json:"sex"`Name string `json:"name"`Hobby []string `json:"hobby"`
}
type Class struct {No string `json:"no"`Student []Student `json:"student"`
}func main() {str := `{"no":"六年級一班","student":[{"id":1000,"sex":"男","name":"王五0","hobby":["java","php","golang"]},{"id":1001,"sex":"男","name":"王五1","hobby":["java","php","golang"]},{"id":1002,"sex":"男","name":"王五2","hobby":["java","php","golang"]},{"id":1003,"sex":"男","name":"王五3","hobby":["java","php","golang"]},{"id":1004,"sex":"男","name":"王五4","hobby":["java","php","golang"]},{"id":1005,"sex":"男","name":"王五5","hobby":["java","php","golang"]},{"id":1006,"sex":"男","name":"王五6","hobby":["java","php","golang"]},{"id":1007,"sex":"男","name":"王五7","hobby":["java","php","golang"]},{"id":1008,"sex":"男","name":"王五8","hobby":["java","php","golang"]},{"id":1009,"sex":"男","name":"王五9","hobby":["java","php","golang"]}]}
`var cl Classerr := json.Unmarshal([]byte(str), &cl)if err != nil {fmt.Println("序列化錯誤!")} else {fmt.Printf("%#v", cl)}
}
結果:
main.Class{No:"六年級一班", Student:[]main.Student{main.Student{ID:1000, Sex:"男", Name:"王五0", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1001, Sex:"男", Name:"王五1", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1002, Sex:"男", Name:"王五2", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1003, Sex:"男", Name:"王五3", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1004, Sex:"男", Name:"王五4", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1005, Sex:"男", Name:"王五5", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1006, Sex:"男", Name:"王五6", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1007, Sex:"男", Name:"王五7", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1008, Sex:"男", Name:"王五8", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1009, Sex:"男", Name:"王五9", Hobby:[]string{"java", "php", "golang"}}}}