Go基础编程 - 08 - 结构体

结构体

    • 1. 自定义类型、类型别名
      • 1.1. 自定义类型
      • 1.2. 类型别名
      • 1.3. 类型定义和类型别名的区别
    • 2. 结构体定义
    • 3. 结构体初始化
    • 4. 指针类型结构体
    • 5. 构造函数
    • 6. 方法和接收者
      • 6.1. 方法定义
      • 6.2. 方法调用
      • 6.3. 值方法和指针方法
      • 6.4. 指针方法使用场景
      • 6.5. 任意类型添加方法
    • 7. 结构体成员可见性
    • 8. 结构体匿名字段
    • 9. 嵌套结构体
      • 9.1. 嵌套匿名结构体
      • 9.2. 嵌套结构体的字段名冲突
    • 10. 通过嵌套实现“继承”
    • 11. 结构体与JSON序列化
    • 12. 结构体标签(Tag)
    • 13. 匿名结构体

上一篇:字典及其约束

下一篇:通道


Go语言中没有“类”的概念,也不支持类的“继承”等面向对象的概念,它所做的是通过嵌入字段的方式实现了类型之间的组合。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。

1. 自定义类型、类型别名

1.1. 自定义类型

通过Type关键字定义。
自定义类型是定义一个全新的类型。可以基于基本类型定义,也可以通过struct定义。

// 通过Type关键字的定义,MyInt就是一种新的类型,它具有int的特性。
type MyInt int

1.2. 类型别名

使用 “=” 定义,type TypeAlias = Type
类型别名规定:TypeAlias只是Type的别名,本质上属于同一个类型。

    type intAlias = int// 内置别名类型type byte = uint8type rune = int32type any = interface{}

1.3. 类型定义和类型别名的区别

//类型定义
type NewInt int//类型别名
type MyInt = intfunc main() {var a NewIntvar b MyIntfmt.Printf("type of a:%T\n", a) //type of a:main.NewIntfmt.Printf("type of b:%T\n", b) //type of b:int
}

上面代码结果显示 a 的类型是 main.NewInt,表示 main 包下定义的 NewInt 类型;b 的类型是 int。MyInt 类型只会在代码中存在,编译完成时并不会有 MyInt 类型。

2. 结构体定义

  • 结构体是一种自定义类型,它是由若干字段组成的。
  • 结构体的字段可以是任意类型,甚至可以是结构体本身。

通过 struct 关键字定义。

type 类型名 struct {字段名 字段类型字段名 字段类型…
}类型名:同一个包内不能重复
字段名:同一个结构体内不能重复
字段类型:可以是任意类型,甚至可以是结构体本身
// 定义一个结构体   
type Student struct {Name stringAge  int
}   

3. 结构体初始化

只有当结构体实例化时,才会分配内存。也就是必须实例化后才能使用结构体的字段。

未赋值字段,初始化为字段类型零值。

var stu Student   
fmt.Printf("%#v\n", stu)    // main.Student{Name:"", Age:0} // 通过点符号(.)访问结构体字段
stu.Name = "Tom"   
fmt.Println(stu.Name)

使用键值对初始化

stu := Student{Name: "小花", Age: 18}
fmt.Println(stu)

使用值的列表初始化(简写:初始化时不写键,只写值)
需满足一下条件:

  1. 必须初始化结构体的所有字段。
  2. 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
  3. 该方式不能和键值初始化方式混用。
stu := Student{"小明", 16}
fmt.Println(stu)

4. 指针类型结构体

初始化方式与值类型结构体相同,但使用指针类型进行初始化。

使用 new 关键字实例化

// new
var stu1 = new(Student)
fmt.Printf("%T \n", stu1)

使用 & 对结构体进行取址操作,相当于 new

stu := &Student{}
stu.Name = "小明"
(*stu).Age = 6
fmt.Printf("%#v\n", stu)

Go语法糖:会适时地为我们进行自动地转译,在stu之上,之所以我们可以通过stu.Name = "小明"设置名字,是因为 Go语言把它自动转译为了(*stu).Name = "小明"

5. 构造函数

Go语言没有构造函数,我们可以自己实现。
struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型。

type Student struct {Name stringAge  int
}func NewStudent(name string, age int) *Student {return &Student{Name: name, Age: age}
}func main() {stu := NewStudent("小花", 18)fmt.Printf("%#v\n", stu)
}

6. 方法和接收者

Go语言中通过struct来实现面向对象;可以包含方法,方法是结构体的成员函数。

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的 this 或者 self。

方法的接收者类型必须是某个自定义的数据类型,而且不能是接口类型或接口的指针类型。

函数与方法的区别:函数不属于任何类型,而方法属于特定的类型。

6.1. 方法定义

方法定义格式如下:

func (接收者 变量名) 方法名(参数列表)(返回值列表) {方法体
}

例如:

type Student struct {Name stringAge  int
}func (stu *Student) SayHello(age int) {fmt.Printf("Hello, my name is %s, and %d years old.\n", stu.Name, age)
} 

6.2. 方法调用

Go语言方法表达式有两种:instance.method(args…), .func(instance, args…)

func main() {stu := &Student{Name: "Tom",Age:  18,}stu.SayHello(18)// 隐式隐式sSay := stu.SayHellosSay(20)// 显式传递sr := (*Student).SayHellosr(stu, 22)
}

6.3. 值方法和指针方法

值方法:接收者类型是非指针的自定义数据类型的方法。

指针方法:接收者类型是指针的自定义数据类型的方法。

值方法与指针方法区别:

  1. 接受者类型:

    • 值方法的接收者是该方法所属类型值的一个副本,在方法内对该副本的修改一般不会体现在原值上,除非这个类型本身是某个引用类型(slice、map、chan)。
    • 指针方法的接收者是该方法所属类型值的指针的一个副本,在方法内对该副本指向的值进行修改一定会体现在原值上。
  2. 方法集合:一个自定义数据类型的方法集合中仅包含它的所有值方法;而该类型的指针类型的方法集合囊括了所有值方法和所有指针方法。

package mainimport "fmt"type Student struct {Name stringAge  int
}// 指针方法
func (stu *Student) SetName(name string) {stu.Name = name
}// 值方法
func (stu Student) SetNameCopy(name string) {stu.Name = name
}func main() {stu := &Student{"小红", 20}stu.SetName("小花")fmt.Println(stu.Name) // 小花stu.SetNameCopy("小明")fmt.Println(stu.Name) // 小花
}

6.4. 指针方法使用场景

  1. 需要修改接收者中的值;
  2. 接收者是拷贝代价比较大的对象;
  3. 保证一致性,如果某个方法使用了指针接收者,那么其它方法也应该使用指针方法。

6.5. 任意类型添加方法

Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。

举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。

//MyInt 将int定义为自定义MyInt类型
type MyInt int//SayHello 为MyInt添加一个SayHello的方法
func (m MyInt) SayHello() {fmt.Println("Hello, 我是一个int。")
}func main() {var m MyIntm.SayHello() //Hello, 我是一个int。m = 100fmt.Printf("%#v  %T\n", m, m) //100  main.MyInt
}

7. 结构体成员可见性

Go语言通过首字母的大小写来控制权限。首字母大写包外部可见,首字母小写仅包内部可见。

demo/struct.go

package demotype Student struct {Name  string    // 公共字段Age   int   score int   // 包内字段,在包外部不可初始化
}// 公共方法
func (stu *Student) SetName(name string) {stu.Name = name
}// 包内方法
func (stu *Student) setScore(score int) {stu.score = score
}

main.go

package mianimport ("demo""fmt"
)func main() {stu := &demo.Student{Name: "小花", Age: 18}stu.SetName("小红")stu.setScore(80)    // stu.setScore undefined (type *demo.Student has no field or method setScore)fmt.Println(stu)
}

8. 结构体匿名字段

结构体允许其成员字段在声明时没有字段名而只有类型,那么它就是一个嵌入字段,也可以被称为匿名字段。

匿名字段默认采用类型作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。

type BaseType struct {stringint
}func main() {var b = BaseType{}b.int = 10fmt.Println(b.int)
}

9. 嵌套结构体

一个结构体可以嵌套包含另一个结构体或结构体指针。

type Address struct {Province stringCity     string
}type User struct {Name    stringGender  stringAddress Address
}func main() {user := User{Name:   "pprof",Gender: "女",Address: Address{Province: "北京",City:     "北京",},}fmt.Printf("user=%#v\n", use)
}

9.1. 嵌套匿名结构体

嵌套结构体字段也可以使用匿名。推荐使用匿名方式嵌套结构体,如下所示:

type User struct {Name    stringGender  stringAddress
}func main() {user := User{Name:"pprof", Gender:"女"}user.Address.Province = "北京"fmt.Printf("user=%#v\n", use)
}

9.2. 嵌套结构体的字段名冲突

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

仅匿名内嵌结构体成员可以直接访问。Go直接访问成员匹配规则:

  1. 结构体成员中含有要访问的成员,则匹配结构体成员。
  2. 结构体成员中没有要访问的成员,则在内嵌匿名结构体中继续查找。若同一层级多个内嵌匿名结构体存在同名成员,则产生歧义,报错。
  3. 内嵌结构体中未找到要访问的成员,若内嵌匿名结构体内还存在内嵌匿名结构体,则继续向下查找,依此类推。
  4. 都匹配不到则报错。
package maintype Student struct {Name       stringAge        intCreateTime stringAddressEmail}type Email struct {Account    stringPhone      stringCreateTime string
}type Address struct {Name       stringProvince   stringCity       stringCounty     stringPhone      stringCreateTime string
}func (stu *Student) SetName(name string) {stu.Name = name
}func (addr *Address) SetName(name string) {addr.Name = name
}func (addr *Address) SetProvince(pro string) {addr.Province = pro
}func main() {var stu = &Student{}// 1. 嵌套结构体重名字段,需自定具体路径stu.CreateTime = "2023-12-21"stu.Address.CreateTime = "2023-12-30"// 2. 直接访问匿名结构体的字段名(当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。)stu.Account = "123@mail.com"// 3. 向下查找字段若同一层级出现重名字段,则产生歧义;需指定具体结构体字段。stu.Phone = "123123123" //报错:ambiguous selector stu.Phonestu.Address.Phone = "123123"// 结构体方法处理方式与字段相同stu.SetName("老北京")stu.Address.SetName("小北京")stu.SetProvince("北京市")fmt.Printf("%#v", stu)
}

10. 通过嵌套实现“继承”

type Animal struct {name string
}func (a *Animal) move() {fmt.Printf("%s会动!\n", a.name)
}type Dog struct {Feet    int8*Animal // 通过嵌套匿名结构体实现继承
}func (d *Dog) wang() {fmt.Printf("%s会汪汪汪~\n", d.name)
}func main() {d := &Dog{Feet: 4,Animal: &Animal{ // 注意嵌套的是结构体指针name: "旺财",},}d.wang() //旺财会汪汪汪~d.move() //旺财会动!
}

11. 结构体与JSON序列化

package mainimport ("encoding/json""fmt"
)type Class struct {Title    stringStudents []*Student
}type Student struct {Name  stringAge   intAddress
}type Address struct {Province stringCity     stringCounty   string
}func main() {c := &Class{Title:    "高一(12)班",Students: make([]*Student, 0, 60),}for i := 1; i <= 10; i++ {stu := &Student{Name:    fmt.Sprintf("Stu_%02d", i),Address: Address{Province: "北京"},}c.Students = append(c.Students, stu)}// JSON序列化:结构体 to JSONdata, err := json.Marshal(c)if err != nil {fmt.Println("Json marshal failed.")return}fmt.Printf("json:%s\n\n\n", data)// JSON反序列化:JSON to 结构体str := `{"Title":"101","Students":[{"Name":"小明","Age":16,"Address":{"Province":"北京"}},{"Name":"小花","Age":15,"Address":{"Province":"上海"}},{"Name":"小红","Age":16,"Address":{"Province":"广东"}}]}`c1 := &Class{}err = json.Unmarshal([]byte(str), c1)if err != nil {fmt.Println("json unmarshal failed!")return}fmt.Printf("%#v\n", c1)
}

12. 结构体标签(Tag)

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

结构体Tag在字段的后方定义,使用一对反引号(``)包括起来;由一个或多个键值对组成,键与值使用冒号分隔,值用双引号括起来,键值对之间使用一个空格分隔。

具体格式如下:

`key1:"value1" key2:"value2"`	

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

在结构体字段的定义中,如果字段名与 json 标签不一致,则需要在字段名后面添加一个 json 标签,如:

package mainimport ("encoding/json""fmt"
)type Student struct {Id    int    `json:"id"` //通过指定tag实现json序列化该字段时的keyName  string `json:"name"`Age   int    `json:"-"` //通过指定tag实现json序列化该字段时忽略该字段Sex   string  `json:"name,omitempty"` // omitempty表示字段为空值时不序列化phone string //私有不能被json包访问
}func main() {stu := Student{Id:    1,Name:  "小明",Age:   18,phone: "13333333333",}str, _ := json.Marshal(stu)fmt.Printf("json:%s\n", str) //json:{"id":1,"name":"小明"}str2, _ := json.Marshal(Student{Id:  2,Sex: "女",})fmt.Printf("json:%s\n", str2) //json:{"id":2,"name":"","sex":"女"}
}

13. 匿名结构体

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

package mainimport ("fmt"
)func main() {var user struct{Name string; Age int}user.Name = "pprof.cn"user.Age = 18fmt.Printf("%#v\n", user)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/854725.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在windows 台式机电脑部署GLM4大模型

参考这篇文章在windows笔记本电脑部署GLM4大模型_16g显卡本地部署glm4-CSDN博客 我的环境&#xff08;PC台式机电脑&#xff1a; 处理器 Intel(R) Core(TM) i9-14900K 3.20 GHz 机带 RAM 32.0 GB (31.8 GB 可用)、32G内存、NVIDIA RTX4080&#xff08;16G&#xff09;…

Ubuntu20.04中复现FoundationPose

Ubuntu20.04中复现FoundationPose 文章目录 Ubuntu20.04中复现FoundationPose1.安装cuda和cudnn2.下载相关资源3.环境配置4.运行model-based demo5.运行ycbv demoReference &#x1f680; 非常重要的环境配置 &#x1f680; ubuntu 20.04cuda 11.8.0cudnn v8.9.7python 3.9.19…

如何理解电流镜负载的差分对的增益

我们知道最普通的电阻负载的差分对的差分增益是-gmRD&#xff0c;如果我们不希望输出是双端的&#xff0c;而是希望单端输出&#xff0c;那么使用电阻负载的差分对会导致增益变为原先的一半&#xff0c;因此引入了电流镜负载的差分对&#xff0c;它可以在保证增益与原先相同的情…

Nuxt快速学习开发 - Nuxt3静态资源Assets

Nuxt 使用两个目录来处理样式表、字体或图像等资产。 public/目录内容按原样在服务器根目录中提供。 assets/目录包含您希望构建工具&#xff08;Vite 或 webpack&#xff09;处理的所有资产。 public/目录 public目录用作静态资产的公共服务器&#xff0c;可在您的应用程序定…

【LeetCode 128】 最长连续子序列

判断前一位数在不在字典中是这道题的关键之处&#xff0c;这样就可以避免重复查找&#xff0c;从而达到O(n) 的时间复杂度。如果没有这个判断&#xff0c;那么时间复杂度最坏也得是O(N^2)级别的。 1. 题目 2. 分析 合理利用数据结构。本题中使用了set来保存数组的元素&#x…

氮化铝上的厚膜高功率片式电阻器

EAK厚膜高功率片式电阻器和氮化铝片式端接非常适合大多数需要在小尺寸封装中实现高导热性的应用。AlN 是 BeO 的理想替代品&#xff0c;具有高功耗且对环境或健康无危害。厚膜技术以非常实惠的价格提供稳定的电阻元件。 高稳定性厚膜电阻元件 AlN衬底材料 标准电阻范围为 10Ω…

5216万!国内自动化巨头,拟剥离旗下子业务

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 近日&#xff0c;中控技术发布公告称&#xff0c;为进一步优化资产结构和产业布局&#xff0c;提升公司核心竞争力&#xff0c;公司拟将其全资子…

多模态LLM 跨越语言与视觉的边界

一、引言 在数字时代的浪潮中&#xff0c;我们被由语言和视觉等多种模态构成的信息海洋所包围。人类大脑以其卓越的多模态上下文理解能力&#xff0c;在日常任务中游刃有余。然而&#xff0c;在人工智能领域&#xff0c;如何将这种能力赋予机器&#xff0c;尤其是如何在语言模…

计算子网掩码

例题 如果子网掩码是255.255.192.0&#xff0c; 那么下面主机&#xff08;&#xff09;必须通过路由器才能与主机129.23.144.16通信&#xff08; 1分 &#xff09;A.129.23.148.127B. 129.23.191.21C. 129.23.127.222D. 129.23.130.33计算 要确定哪些主机必须通过路由器才能与…

MybatisPlus:高效便捷的Java持久层框架

一、MybatisPlus简介 MybatisPlus&#xff08;简称MP&#xff09;是一个流行的Java持久层框架&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生&#xff0c;旨在简化数据库操作和提高开发效率。MybatisPlus为开发者提供了一套方便的API和…

运维开发详解:从入门到精通

运维开发&#xff08;DevOps&#xff09;是一种整合开发与运维的实践&#xff0c;旨在通过自动化和持续交付提高软件开发和部署的效率。本文将详细介绍运维开发的概念、核心工具和最佳实践&#xff0c;帮助您从入门到精通。 一、运维开发的概念 1. 什么是运维开发&#xff1f…

About Apple Pay

本文翻译整理自&#xff1a;About Apple Pay &#xff08;更新时间&#xff1a;2017-03-16&#xff09; https://developer.apple.com/library/archive/ApplePay_Guide/index.html#//apple_ref/doc/uid/TP40014764 文章目录 一、关于 Apple Pay1、使用 Apple Pay2、测试 Apple …

k8s核心组件

Master组件&#xff1a; kube-apiserver&#xff1a;用于暴露Kubernetes API&#xff0c;任何资源请求或调用操作都是通过kube-apiserver提供的接口进行。它是Kubernetes集群架构的大脑&#xff0c;负责接收所有请求&#xff0c;并根据用户的具体请求通知其他组件工作。etcd&am…

SpringBoot3整合SpringDoc实现在线接口文档

写在前面 在现目前项目开发中&#xff0c;一般都是前后端分离项目。前端小姐姐负责开发前端&#xff0c;苦逼的我们负责后端开发 事实是一个人全干&#xff0c;在这过程中编写接口文档就显得尤为重要了。然而作为一个程序员&#xff0c;最怕的莫过于自己写文档和别人不写文档…

找工作小项目:day16-重构核心库、使用智能指针(3)

day16-重构核心库、使用智能指针&#xff08;3&#xff09; 最后将使用这个库的方式进行展示。 1、客户端 在while ((o getopt(argc, argv, optstring)) ! -1)所有的操作都是获取参数的操作&#xff0c;threads 、msgs 和wait 分别指线程数、消息长度以及等待时间。 创建一…

【数据结构(邓俊辉)学习笔记】二叉搜索树02——查找、插入和删除

文章目录 1.概述2. 查找2.1 查找&#xff1a;算法2.2 查找&#xff1a;理解2.3 查找&#xff1a;实现2.4 查找&#xff1a;语义 3. 插入3.1 插入&#xff1a;算法3.2 插入&#xff1a;实现 4. 删除4.1 删除&#xff1a;框架4.2 删除&#xff1a;单分支4.3 删除&#xff1a;双分…

Pyqt QCustomPlot 简介、安装与实用代码示例(一)

目录 简介安装实用代码示例带有填充的简单衰减正弦函数及其红色的指数包络线具有数据点的 sinc 函数、相应的误差条和 2--sigma 置信带几种散点样式的演示展示 QCustomPlot 在设计绘图方面的多功能性 结语 所有文章除特别声明外&#xff0c;均采用 CC BY-NC-SA 4.0 许可协议。转…

RERCS系统开发实战案例-Part05 FPM Application的Feeder Class搜索组件的实施

1、通过事务码 SE24对Feeder Class实施 1&#xff09;接口页签的简单说明&#xff1a; ① IF_FPM_GUIBB&#xff1a;通用UI构建块&#xff0c;整个UIBB模块的基础接口&#xff1b; ② IF_FPM_GUIBB_SEARCH&#xff1a;通用搜索UI构建块&#xff0c;搜索组件UIBB的基础接口&…

期末复习GGG-----查找子串

郭的 char *search( char *s, char *t ){int i0;while(s[i]){int j0;if(s[i]t[0]){while(s[ij]t[j]&&t[j]){j;}if(t[j]\0)return si;}i;}return NULL; } AI的 #include <stdio.h> #include <string.h> #define MAXS 30char *search(char *s, char *t);in…

React项目-Cesium地图初始化

一、环境描述 React集成Cesium地图需要注意软件兼容性问题&#xff0c;可以从官网或者百度文章查询React和Cesium地图的版本兼容性&#xff0c; 1、软件版本 &#xff08;1&#xff09;create-react-app创建项目&#xff1b; &#xff08;2&#xff09;React版本&#xff1a;1…