golang中级进阶(二):结构体

目录

一、结构体详解

1. 结构体定义

2. 实例化结构体的7种方法

二、结构体方法

1. 结构体的方法定义

2. 结构体内自定义方法的引用

3. 任意类型添加方法

三、嵌套、继承

1. 匿名结构体

2. 结构体中可以定义任意类型的字段

3. 结构体嵌套结构体

4. 结构体嵌套匿名结构体

5. 结构体嵌套多个匿名结构体

6. 结构体继承

四、结构体和JSON相互转换

1. 结构体转化成json

2. json转化成结构体

3. 结构体标签 tag

4. 嵌套结构体和json的序列化反序列化


一、结构体详解

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

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值