2024年Go最全(六)Go------自定义类型type,结构体struct,全网首发

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

其中FmtProfile 是方法名,而(person Profile) :表示将 FmtProfile 方法与 Profile 的实例绑定。

我们把 Profile 称为方法的接收者,而 person 表示实例本身

,在方法内可以使用 person.属性名 的方法来访问实例属性。

package main

import “fmt”

// 定义一个名为Profile 的结构体

type Profile struct {

name string

age int

gender string

mother *Profile // 指针

father *Profile // 指针

}

// 定义一个与 Profile 的绑定的方法

func (person Profile) FmtProfile() {

fmt.Printf(“名字:%s\n”, person.name)

fmt.Printf(“年龄:%d\n”, person.age)

fmt.Printf(“性别:%s\n”, person.gender)

}

func main() {

// 实例化

myself := Profile{name: “小明”, age: 24, gender: “male”}

// 调用函数

myself.FmtProfile()

}

//输出如下

名字:小明

年龄:24

性别:male

//Person 结构体

type Person struct {

name string

age int8

}

//NewPerson 构造函数

func NewPerson(name string, age int8) *Person {

return &Person{

name: name,

age: age,

}

}

//Dream Person做梦的方法

func (p Person) Dream() {

fmt.Printf(“%s的梦想是学好Go语言!\n”, p.name)

}

func main() {

p1 := NewPerson(“测试”, 25)

p1.Dream()

}

2. 方法的参数传递方式(重点)


当你想要在方法内改变实例的属性的时候,必须使用指针做为方法的接收者

package main

import “fmt”

// 声明一个 Profile 的结构体

type Profile struct {

name string

age int

gender string

mother *Profile // 指针

father *Profile // 指针

}

// 重点在于这个星号: *, 说明已经有一个struct对象实例化了

//是 再次基础上做修改

func (person *Profile) increase_age() {

person.age += 1

}

func main() {

myself := Profile{name: “小明”, age: 24, gender: “male”}

fmt.Printf(“当前年龄:%d\n”, myself.age)

myself.increase_age()

fmt.Printf(“当前年龄:%d”, myself.age)

}

//结果

当前年龄:24

当前年龄:25

可以看到在方法内部对 age 的修改已经生效。你可以尝试去掉 *,使用值做为方法接收者,看看age是否会发生改变(答案是:不会改变

至此,我们知道了两种定义方法的方式:

  • 做为方法接收者

  • 指针做为方法接收者

那我们如何进行选择呢?

  • 直接使用指针做为方法的接收者。
  1. 你需要在方法内部改变结构体内容的时候

  2. 出于性能的问题,当结构体过大的时候(相当于,组合)

  • 有些情况下,以值或指针做为接收者都可以,但是考虑到代码一致性,建议都使用指针做为接收者

指针类型的接收者

// SetAge 设置p的年龄

// 使用指针接收者

func (p *Person) SetAge(newAge int8) {

p.age = newAge

}

调用该方法:

func main() {

p1 := NewPerson(“测试”, 25)

fmt.Println(p1.age) // 25

p1.SetAge(30)

fmt.Println(p1.age) // 30

}

4. 结构体实现 “继承”


Go 语言本身并不支持继承

可以使用组合的方法,实现类似继承的效果。

组合:比如一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一起,最后才有了我们用的电脑。

在 Go 语言中,把一个结构体嵌入到另一个结构体的方法,称之为组合。

现在这里有一个表示公司(company)的结构体,还有一个表示公司职员(staff)的结构体。

type company struct {

companyName string

companyAddr string

}

type staff struct {

name string

age int

gender string

position string

}

若要将公司信息与公司职员关联起来,一般都会想到将 company 结构体的内容照抄到 staff 里。

借鉴继承的思想,我们可以将公司的属性都“继承”过来。

但是在 Go 中没有类的概念,只有组合

可以将 company 这个 结构体嵌入 staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 company 的所有属性了。

type staff struct {

name string

age int

gender string

position string

company // 匿名字段

// cpmpay compay // 嵌套

}

验证一下。

package main

import “fmt”

type company struct {

companyName string

companyAddr string

}

type staff struct {

name string

age int

gender string

position string

company

}

func main() {

myCom := company{

companyName: “Tencent”,

companyAddr: “北京市”,

}

staffInfo := staff{

name: “小明”,

age: 28,

gender: “男”,

position: “云计算工程师”,

company: myCom,

}

/*

user1 := User{

Name: “pprof”,

Gender: “女”,

Address: Address{

Province: “陕西”,

City: “西安”,

},

}

*/

fmt.Printf(“%s 在 %s 工作\n”, staffInfo.name, staffInfo.companyName)

fmt.Printf(“%s 在 %s 工作\n”, staffInfo.name, staffInfo.company.companyName)

}

结果,可见staffInfo.companyName 和 staffInfo.company.companyName 的效果是一样的。

小明 在 Tencent 工作

小明 在 Tencent 工作

嵌套类型

嵌套结构体内部可能存在相同的字段名。这个时候为了避免歧义需要指定具体的内嵌结构体的字段

//Address 地址结构体

type Address struct {

Province string

City string

}

//User 用户结构体

type User struct {

Name string

Gender string

Address //匿名结构体

}

func main() {

var user2 User

user2.Name = “pprof”

user2.Gender = “女”

user2.Address.Province = “陕西” //通过匿名结构体.字段名访问

user2.City = “西安” //直接访问匿名结构体的字段名

fmt.Printf(“user2=%#v\n”, user2) //user2=main.User{Name:“pprof”, Gender:“女”, Address:main.Address{Province:“黑龙江”, City:“哈尔滨”}}

}

继承

使用结构体也可以实现其他编程语言中面向对象的继承

//Animal 动物

type Animal struct {

name string

}

func (a *Animal) move() {

fmt.Printf(“%s会动!\n”, a.name)

}

//Dog 狗

type Dog struct {

Feet int8

*Animal //通过嵌套匿名结构体实现继承

}

func (d *Dog) wang() {

fmt.Printf(“%s会汪汪汪~\n”, d.name)

}

func main() {

d1 := &Dog{

Feet: 4,

Animal: &Animal{ //注意嵌套的是结构体指针

name: “乐乐”,

},

}

d1.wang() //乐乐会汪汪汪~

d1.move() //乐乐会动!

}

5. 内部方法与外部方法(大小写)


函数名的首字母大小写非常重要,它被来实现控制对方法的访问权限。

  • 当方法的首字母为大写时,这个方法对于所有包都是Public,其他包可以随意调用

  • 当方法的首字母为小写时,这个方法是Private,其他包是无法访问

6. 三种实例化方法


第一种:正常实例化

func main() {

xm := Profile{

name: “小明”,

age: 18,

gender: “male”,

}

}

第二种:使用 new

func main() {

xm := new(Profile)

// 等价于: var xm *Profile = new(Profile)

fmt.Println(xm)

// output: &{ 0 }

xm.name = “iswbm” // 或者 (*xm).name = “iswbm”

xm.age = 18 // 或者 (*xm).age = 18

xm.gender = “male” // 或者 (*xm).gender = “male”

fmt.Println(xm)

//output: &{iswbm 18 male}

}

第三种:使用 &

func main() {

var xm *Profile = &Profile{}

fmt.Println(xm)

// output: &{ 0 }

xm.name = “iswbm” // 或者 (*xm).name = “iswbm”

xm.age = 18 // 或者 (*xm).age = 18

xm.gender = “male” // 或者 (*xm).gender = “male”

fmt.Println(xm)

//output: &{iswbm 18 male}

}

7. 选择器的冷知识


从一个结构体实例对象中获取字段的值,通常都是使用 . 这个操作符,该操作符叫做 选择器

当你对象是结构体对象的指针时,你想要获取字段属性时,按照常规理解应该这么做

type Profile struct {

Name string

}

func main() {

//这里直接将p1定义为指针类型

p1 := &Profile{“iswbm”}

fmt.Println((*p1).Name) // output: iswbm

}

有一个更简洁的做法,可以直接省去 * 取值的操作,选择器 . 会直接解引用,示例如下

type Profile struct {

Name string

}

func main() {

p1 := &Profile{“iswbm”}

fmt.Println(p1.Name) // output: iswbm

}

8. 结构体与JSON序列化


JSON是一种轻量级的数据交换格式

JSON键值对是用来保存JS对象的一种方式,键/值对组合中的键名写在前面并用双引号""包裹,使用冒号:分隔,然后紧接着值;多个键值之间使用英文,分隔。

//Student 学生

type Student struct {

ID int

Gender string

Name string

}

//Class 班级

type Class struct {

Title string

Students []*Student

}

func main() {

c := &Class{

Title: “101”,

Students: make([]*Student, 0, 200),

}

for i := 0; i < 10; i++ {

stu := &Student{

Name: fmt.Sprintf(“stu%02d”, i),

Gender: “男”,

ID: i,

}

c.Students = append(c.Students, stu)

}

//JSON序列化:结构体–>JSON格式的字符串

data, err := json.Marshal©

if err != nil {

fmt.Println(“json marshal failed”)

return

}

fmt.Printf(“json:%s\n”, data)

//JSON反序列化:JSON格式的字符串–>结构体

str := {"Title":"101","Students":[{"ID":0,"Gender":"男","Name":"stu00"},{"ID":1,"Gender":"男","Name":"stu01"},{"ID":2,"Gender":"男","Name":"stu02"},{"ID":3,"Gender":"男","Name":"stu03"},{"ID":4,"Gender":"男","Name":"stu04"},{"ID":5,"Gender":"男","Name":"stu05"},{"ID":6,"Gender":"男","Name":"stu06"},{"ID":7,"Gender":"男","Name":"stu07"},{"ID":8,"Gender":"男","Name":"stu08"},{"ID":9,"Gender":"男","Name":"stu09"}]}

c1 := &Class{}

err = json.Unmarshal([]byte(str), c1)

if err != nil {

fmt.Println(“json unmarshal failed!”)

return

}

fmt.Printf(“%#v\n”, c1)

}

9. 结构体标签(Tag)


Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。

Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:

key1:"value1" key2:"value2"

结构体标签由一个或多个键值对组成。键与值使用冒号分隔,双引号括起来。键值对之间使用一个空格分隔

注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。

例如我们为Student结构体的每个字段定义json序列化时使用的Tag:

//Student 学生

type Student struct {

ID int json:"id" //通过指定tag实现json序列化该字段时的key

Gender string //json序列化是默认使用字段名作为key

name string //私有不能被json包访问

}

func main() {

s1 := Student{

ID: 1,

Gender: “女”,

name: “pprof”,

}

data, err := json.Marshal(s1)

if err != nil {

fmt.Println(“json marshal failed!”)

return

}

fmt.Printf(“json str:%s\n”, data) //json str:{“id”:1,“Gender”:“女”}

}

10. map类型的结构体


package main

import “fmt”

type student struct {

id int

name string

age int

}

func main() {

ce := make(map[int]student)

ce[1] = student{1, “xiaolizi”, 22}

ce[2] = student{2, “wang”, 23}

fmt.Println(ce)

//如何删除

delete(ce, 2)

fmt.Println(ce)

}

实现map有序输出


(面试经常问到)

package main

import (

“fmt”

“sort”

)

func main() {

map1 := make(map[int]string, 5)

map1[1] = “www.topgoer.com”

map1[2] = “rpc.topgoer.com”

map1[0] = “xiaohuang”

map1[5] = “qcqly”

map1[3] = “xiaohong”

sli := []int{}

for k, _ := range map1 {

sli = append(sli, k)

}

sort.Ints(sli)

for i := 0; i < len(map1); i++ {

fmt.Println(map1[sli[i]])

}

}

思考


package main

import “fmt”

type student struct {

id int

name string

age int

}

func demo(ce []student) {

//切片是引用传递,是可以改变值的

ce[1].age = 999

// ce = append(ce, student{3, “xiaowang”, 56})

// return ce

}

func main() {

var ce []student //定义一个切片类型的结构体

ce = []student{

student{1, “xiaoming”, 22},

student{2, “xiaozhang”, 33},

}

fmt.Println(ce)

demo(ce)

fmt.Println(ce)

}

匿名结构体

========================================================================

在定义一些临时数据结构等场景下还可以使用匿名结构体

package main

import (

“fmt”

)

func main() {

var user struct{Name string; Age int}

user.Name = “qcq.cn”

user.Age = 18

fmt.Printf(“%#v\n”, user)

}

1. 创建指针类型结构体


我们还可以通过使用new关键字对结构体进行实例化,得到的是结构体的地址。 格式如下:

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

struct {

id int

name string

age int

}

func demo(ce []student) {

//切片是引用传递,是可以改变值的

ce[1].age = 999

// ce = append(ce, student{3, “xiaowang”, 56})

// return ce

}

func main() {

var ce []student //定义一个切片类型的结构体

ce = []student{

student{1, “xiaoming”, 22},

student{2, “xiaozhang”, 33},

}

fmt.Println(ce)

demo(ce)

fmt.Println(ce)

}

匿名结构体

========================================================================

在定义一些临时数据结构等场景下还可以使用匿名结构体

package main

import (

“fmt”

)

func main() {

var user struct{Name string; Age int}

user.Name = “qcq.cn”

user.Age = 18

fmt.Printf(“%#v\n”, user)

}

1. 创建指针类型结构体


我们还可以通过使用new关键字对结构体进行实例化,得到的是结构体的地址。 格式如下:

[外链图片转存中…(img-LjIWDKSw-1715628116422)]
[外链图片转存中…(img-vsVjcz3o-1715628116422)]
[外链图片转存中…(img-KTrc4GVx-1715628116423)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值