目录
一、结构体详解
1. 结构体定义
Golang 中没有“类”的概念,Golang 中的结构体和其他语言中的类有点相似。和其他面向对 象语言中的类相比,Golang 中的结构体具有更高的扩展性和灵活性。
Golang 中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全 部或部分属性时,这时候再用单一的基本数据类型就无法满足需求了,Golang 提供了一种 自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称 struct。 也就是我们可以通过 struct 来定义自己的类型了。
package main
import "fmt"
//type关键词 自定义类型 回顾
type myInt int
type myFunc func(int, int) int
func main() {
var a myInt = 8
fmt.Printf("%v %T\n", a, a) // 8 main.myInt
}
使用 type 和 struct 关键字来定义结构体,具体代码格式如下:
type 类型名 struct {
字段名 字段类型
字段名 字段类型 …
}
其中:
• 类型名:表示自定义结构体的名称,在同一个包内不能重复。
• 字段名:表示结构体字段名。结构体中的字段名必须唯一。
• 字段类型:表示结构体字段的具体类型。
package main
// type 定义结构体 结构体首字母可以大写也可以小写,大写表示这个结构体是公有的,在其他的包里面 可以使用。小写表示这个结构体是私有的,只有这个包里面才能使用。
type Person struct {
name string
age int
sex string
}
func main() {
......
}
2. 实例化结构体的7种方法
package main
import "fmt"
type Person struct {
name string
age int
sex string
}
func main() {
// 实例化结构体 第一种方法
var p1 Person
p1.name = "张三"
p1.age = 20
p1.sex = "男"
fmt.Printf("%v %T\n", p1, p1) // {张三 20 男} main.Person
// 打印结构体的值 最好加上 # 这样能显示全部信息
fmt.Printf("%#v %T\n", p1, p1) // main.Person{name:"张三", age:20, sex:"男"} main.Person
//第二种方法
var p2 = new(Person) // 此时的p2 是个指针
p2.name = "李四"
p2.age = 21
p2.sex = "男"
fmt.Printf("%#v %T\n", p2, p2) // &main.Person{name:"李四", age:21, sex:"男"} *main.Person
// 第三种方法 实例化
p3 := &Person{} // 一样是 指针
p3.name = "王五"
p3.age = 22
p3.sex = "女"
fmt.Printf("%#v %T\n", p3, p3) // &main.Person{name:"王五", age:22, sex:"女"} *main.Person
// 第四种方法 键值对赋值
p4 := &Person{
name: "郑六",
age: 30,
sex: "男", // 注意每个结尾都有 , 号
}
fmt.Printf("%#v %T\n", p4, p4) // &main.Person{name:"郑六", age:30, sex:"男"} *main.Person
// 第五种方法
p5 := Person{
name: "赵麻子",
age: 11,
sex: "nv",
}
fmt.Printf("%#v %T\n", p5, p5) // main.Person{name:"赵麻子", age:11, sex:"nv"} main.Person
// 第六种方法 初始化
p6 := &Person{
"赵四",
20,
"男",
}
fmt.Printf("%#v %T\n", p6, p6) // &main.Person{name:"赵四", age:20, sex:"男"} *main.Person
// 第七种方法 初始化
p7 := Person{
"赵四",
20,
"男",
}
fmt.Printf("%#v %T\n", p7, p7) // main.Person{name:"赵四", age:20, sex:"男"} main.Person
}
二、结构体方法
1. 结构体的方法定义
在 go 语言中,没有类的概念但是可以给类型(结构体,自定义类型)定义方法。所谓方法 就是定义了接收者的函数。接收者的概念就类似于其他语言中的 this 或者 self。
方法的定义格式如下:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
package main
import "fmt"
type Person struct {
name string
age int
sex string
height int
}
//func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { 函数体 }
func (p Person) getUserInfo() {
fmt.Printf("姓名:%v 年龄:%v\n", p.name, p.age)
}
func main() {
p1 := Person{
name: "张三",
age: 20,
sex: "李四",
height: 181,
}
p1.getUserInfo() // 姓名:张三 年龄:20
}
2. 结构体内自定义方法的引用
注意:想改变结构体内的值,必须先变成指针。
package main
import "fmt"
type Person struct {
name string
age int
sex string
height int
}
//func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { 函数体 }
func (p Person) getUserInfo() {
fmt.Printf("姓名:%v 年龄:%v\n", p.name, p.age)
}
// 这种是改不掉里面的值的 ,要使用指针才行
func (p Person) setUserInfo1(name string, age int) {
p.name = name
p.age = age
}
//接受者类型是指针的时候 就可以改变了
func (p *Person) setUserInfo2(name string, age int) {
p.name = name
p.age = age
}
func main() {
p1 := Person{
name: "张三",
age: 20,
sex: "李四",
height: 181,
}
p1.getUserInfo() // 姓名:张三 年龄:20
p1.setUserInfo1("李四", 33)
p1.getUserInfo() // 姓名:张三 年龄:20
p1.setUserInfo2("王五", 22)
p1.getUserInfo() // 姓名:王五 年龄:22
}
3. 任意类型添加方法
在 Go 语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的 int 类型使用 type 关键字可以定义新的自定义类型,然后为我们 的自定义类型添加方法。
package main
import "fmt"
// 给任意类型添加方法
type myInt int
func (m myInt) sayHello() {
fmt.Println("我是自定义myInt类型的sayHello方法")
}
func main() {
var a myInt
a.sayHello() // 我是自定义myInt类型的sayHello方法
}
三、嵌套、继承
1. 匿名结构体
注意:匿名结构体中不允许出现多个重复的类型
package main
import "fmt"
// 匿名结构体 注意:匿名结构体中不允许出现多个重复的类型
type Person struct {
string
int
}
func main() {
// 匿名结构体赋值
p := Person{
"张三",
20,
}
fmt.Println(p) // {张三 20}
}
2. 结构体中可以定义任意类型的字段
package main
import "fmt"
// 结构体中可以定义任意类型的字段
type userInfo struct {
Name string
Age int
Hobby []string // 不可以[...]string
Address map[string]string
}
func main() {
// 结构体多值
u := userInfo{
Name: "张三",
Age: 20,
}
u.Hobby = make([]string, 3, 3) // 切片和map 必须先make
u.Hobby = []string{"吃饭", "睡觉", "打豆豆"}
u.Address = make(map[string]string)
u.Address = map[string]string{"email": "15@qq.com", "phone": "15098801234"}
fmt.Printf("%#v \n", u) // main.userInfo{Name:"张三", Age:20, Hobby:[]string{"吃饭", "睡觉", "打豆豆"}, Address:map[string]string{"email":"15@qq.com", "phone":"15098801234"}}
}
3. 结构体嵌套结构体
package main
import "fmt"
// 结构体中嵌套结构体
type user struct {
Name string
Age int
From From // 也可以直接写成 From
}
type From struct {
City string
Country string
}
func main() {
// 结构体嵌套结构体
ur := user{
Name: "李四",
Age: 20,
From: From{
City: "北京",
Country: "中国",
},
}
fmt.Printf("%#v\n", ur) // main.user{Name:"李四", Age:20, From:main.From{City:"北京", Country:"中国"}}
ur.Name = "王五"
ur.From.City = "德克萨斯州"
ur.From.Country = "美国"
fmt.Printf("%#v\n", ur) // main.user{Name:"王五", Age:20, From:main.From{City:"德克萨斯州", Country:"美国"}}
}
4. 结构体嵌套匿名结构体
package main
import "fmt"
// 结构体嵌套匿名结构体
type user2 struct {
Name string
Age int
From
}
type From struct {
City string
Country string
}
func main() {
// 结构体嵌套匿名结构体
ur2 := user2{
Name: "王麻子",
Age: 34,
}
ur2.From.Country = "中国"
ur2.City = "山东" // 当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
fmt.Printf("%#v\n", ur2) // main.user2{Name:"王麻子", Age:34, From:main.From{City:"山东", Country:"中国"}}
}
5. 结构体嵌套多个匿名结构体
package main
import "fmt"
// 结构体嵌套多个匿名结构体
type user3 struct {
Name string
From
Info
}
type Info struct {
Email string
Phone string
City string
}
type From struct {
City string
Country string
}
func main() {
// 结构体嵌套多个匿名结构体
ur3 := user3{
Name: "赵四",
}
ur3.Email = "1159@qq.com"
ur3.Phone = "15144557544"
//ur3.City = "台湾" // 当多个匿名结构体中有相同字段时就不可以这样写了,正确写法如下
ur3.Info.City = "台湾"
ur3.From.City = "山东"
ur3.Country = "中国"
fmt.Printf("%#v\n", ur3) // main.user3{Name:"赵四", From:main.From{City:"山东", Country:"中国"}, Info:main.Info{Email:"1159@qq.com", Phone:"15144557544", City:"台湾"}}
}
6. 结构体继承
package main
import "fmt"
// 结构体继承
type Animal struct {
Name string
}
func (a *Animal) run() {
fmt.Printf("%v 在奔跑\n", a.Name)
}
type dog struct {
Age int
Animal // 当这里变成 *Animal 时 赋值就要加个 &
}
func (d *dog) info() {
fmt.Printf("这条狗名字叫:%v 年龄:%v\n", d.Name, d.Age)
}
type dog2 struct {
Age int
*Animal
}
func (d *dog2) info() {
fmt.Printf("这条狗名字叫:%v 年龄:%v\n", d.Name, d.Age)
}
func main() {
// 结构体继承
var d dog
d.Name = "小白"
d.Age = 3
d.run() // 小白 在奔跑
d.info() // 这条狗名字叫:小白 年龄:3
d2 := &dog2{
Age: 2,
Animal: &Animal{
Name: "大白",
},
}
d2.run() // 大白 在奔跑
d2.info() // 这条狗名字叫:大白 年龄:2
}
四、结构体和JSON相互转换
1. 结构体转化成json
注意:如果结构体里面有私有属性也就是小写定义的字段,则不会被json使用
package main
import (
"encoding/json"
"fmt"
)
// 结构体转字符串 如果里面有私有属性也就是小写定义的字段,则不会被json使用
type userInfo struct {
Name string
Age int
Sex string
}
func main() {
// 结构体转字符串
var u = &userInfo{
Name: "张三",
Age: 20,
Sex: "男",
}
uslice, _ := json.Marshal(u) // 传入结构体返回切片和错误信息
jsonStr := string(uslice) // 转换成字符串
fmt.Println(jsonStr) // {"Name":"张三","Age":20,"Sex":"男"}
}
2. json转化成结构体
// json字符串转换成结构体
str := `{"Name":"张三","Age":20,"Sex":"男"}`
var u2 userInfo
err := json.Unmarshal([]byte(str), &u2) // 传入byte类型的字符串和结构体,返回错误信息
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%#v\n", u2) // main.userInfo{Name:"张三", Age:20, Sex:"男"}
}
3. 结构体标签 tag
package main
import (
"encoding/json"
"fmt"
)
// 结构体标签 tag
type user struct {
Id int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
// 生成后的json按照标签来书写
var u3 = &user{
Id: 12,
Name: "李四",
Age: 30,
}
u3slice, _ := json.Marshal(u3)
jsonStr2 := string(u3slice)
fmt.Println(jsonStr2) // {"id":12,"name":"李四","age":30}
}
4. 嵌套结构体和json的序列化反序列化
package main
import (
"encoding/json"
"fmt"
)
// 嵌套结构体与json序列化反序列化
type Student struct {
Id int
Name string
Age int
Sex string
}
type Class struct {
Title string
Students []Student
}
func main() {
// 嵌套结构体json相互序列化
c := &Class{
Title: "3班",
Students: make([]Student, 0, 10),
}
for i := 0; i < 5; i++ {
s := Student{
Id: i,
Name: fmt.Sprintf("王%v", i),
Age: 15 + i,
Sex: "男",
}
c.Students = append(c.Students, s)
}
fmt.Printf("%#v\n", c) // &main.Class{Title:"3班", Students:[]main.Student{main.Student{Id:0, Name:"王0", Age:15, Sex:"男"}, main.Student{Id:1, Name:"王1", Age:16, Sex:"男"}, main.Student{Id:2,:"王2", Age:17, Sex:"男"}, main.Student{Id:3, Name:"王3", Age:18, Sex:"男"}, main.Student{Id:4, Name:"王4", Age:19, Sex:"男"}}}
data, _ := json.Marshal(c)
jsonStr3 := string(data)
fmt.Println(jsonStr3) // {"Title":"3班","Students":[{"Id":0,"Name":"王0","Age":15,"Sex":"男"},{"Id":1,"Name":"王1","Age":16,"Sex":"男"},{"Id":2,"Name":"王2","Age":17,"Sex":"男"},{"Id":3,"Name"Age":18,"Sex":"男"},{"Id":4,"Name":"王4","Age":19,"Sex":"男"}]}
// 反序列化
str2 := `{"Title":"3班","Students":[{"Id":0,"Name":"王0","Age":15,"Sex":"男"},{"Id":1,"Name":"王1","Age":16,"Sex":"男"},{"Id":2,"Name":"王2","Age":17,"Sex":"男"},{"Id":3,"Name":"王3","Age":18,"Sex":"男"},{"Id":4,"Name":"王4","Age":19,"Sex":"男"}]}`
c1 := &Class{}
fmt.Printf("%#v\n", c1)
err2 := json.Unmarshal([]byte(str2), c1)
if err2 != nil {
fmt.Println(err2)
}
fmt.Printf("%#v\n", c1) // &main.Class{Title:"3班", Students:[]main.Student{main.Student{Id:0, Name:"王0", Age:15, Sex:"男"}, main.Student{Id:1, Name:"王1", Age:16, Sex:"男"}, main.Student{Id:2,:"王2", Age:17, Sex:"男"}, main.Student{Id:3, Name:"王3", Age:18, Sex:"男"}, main.Student{Id:4, Name:"王4", Age:19, Sex:"男"}}}
}