目录


一、结构体详解

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:"男"}}}

}