李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
GO
正文
11.Golang结构体(二)
Leefs
2022-07-03 PM
1211℃
0条
[TOC] ### 前言 ![11.Golang结构体(二)01.jpeg](https://lilinchao.com/usr/uploads/2022/07/2364306995.jpeg) ### 一、方法和接收者 #### 1.1 格式定义 Go语言中的`方法(Method)`是一种作用于特定类型变量的函数。这种特定类型变量叫做`接收者(Receiver)`。接收者的概念就类似于其他语言中的`this`或者 `self`。 **方法的定义格式如下:** ```go func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { 函数体 } ``` **说明** + **接收者变量**:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是`self`、`this`之类的命名。 例如,`Person`类型的接收者变量应该命名为 `p`,`Connector`类型的接收者变量应该命名为`c`等。 + **接收者类型**:接收者类型和参数类似,可以是指针类型和非指针类型。 + **方法名、参数列表、返回参数**:具体格式与函数定义相同(将在函数章节进行详细讲述)。 **示例** ```go type student struct { name string age int gender string hobby []string } //构造函数 func newStudent(name string,age int,gender string,hobby []string) *student{ return &student{ name: name, age: age, gender: gender, hobby: hobby, } } //非指针类型接收者 func (s student) methodTest(){ //非指针类型是不能改值 s.age = 20 fmt.Printf("姓名:%s\n",s.name) } //指针类型接收者 func (s *student) methodTestUpdate(){ s.age = 20 fmt.Printf("姓名:%s\n",s.name) } func main() { s := student{ name: "Leefs", age: 18, } s.methodTest() // //不能改值 fmt.Printf("年龄:%d\n",s.age) s2 := &student{ name: "Jeyoo", age: 18, } s2.methodTestUpdate() fmt.Printf("年龄:%d\n",s2.age) } ``` **运行结果** ``` 姓名:Leefs 年龄:18 姓名:Jeyoo 年龄:20 ``` 方法与函数的区别是,函数不属于任何类型,方法属于特定的类型。 #### 1.2 指针类型的接收者 指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的`this`或者`self`。 例如我们为`student`添加一个`setAge`方法,来修改实例变量的年龄。 ```go //使用指针接收者 func (s *student) setAge(newAge int){ s.age = newAge } //在主函数中调用setAge方法 func main(){ hobby := []string{"a","b"} stu := newStudent("zhangsan",18,"男",hobby) fmt.Println(stu.age) //18 stu.setAge(20) fmt.Println(stu.age) //20 } ``` **什么时候应该使用指针类型接收者** 1. 需要修改接收者中的值 2. 接收者是拷贝代价比较大的大对象 3. 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。 #### 1.3 值类型的接收者 当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但**修改操作只是针对副本,无法修改接收者变量本身**。 ```go //使用值接收者 func (s student) setAge2(newAge int){ s.age = newAge } func main(){ hobby := []string{"a","b"} stu := newStudent("zhangsan",18,"男",hobby) fmt.Println(stu.age) //18 stu.setAge2(20) fmt.Println(stu.age) //18 } ``` ### 二、嵌套结构体 一个结构体中可以嵌套包含另一个结构体或结构体指针。 **示例代码** ```go //结构体嵌套 type address struct{ province string city string } type student struct { name string age int addr address } func main() { //初始化结构体 s := student{ name: "Leefs", age: 18, addr: address{ province: "北京", city: "朝阳", }, } fmt.Println(s.name)//Leefs //通过 嵌套结构体名称.参数名称 来获取该参数值 fmt.Println(s.addr.province)//北京 fmt.Println(s.addr.city)//朝阳 fmt.Printf("student=%#v\n", s) //student=main.student05{name:"Leefs", age:18, addr:main.address{province:"北京", city:"朝阳"}} } ``` #### 2.1 嵌套匿名字段 ```go //匿名字段,每个类型只能用一次,不建议写 type student struct { name string string int } func main() { s := student{ name: "Leefs", string: "method", } fmt.Println(s.name)//Leefs fmt.Println(s.string)//method fmt.Println(s.int)//0 } ``` 当访问结构体成员时会先在结构体中查找该字段,找不到再去嵌套的匿名字段中查找。 #### 2.2 嵌套结构体的字段名冲突 嵌套结构体内部可能存在相同的字段名。在这种情况下为了避免歧义需要通过指定具体的内嵌结构体字段名。 ```go //嵌套匿名结构体 type address struct { province string city string } type email struct { province string test string } type other struct { province string tmp string } type student struct { name string age int addr address2 //匿名字段 email other } func main() { s := student{ name: "Leefs", age: 18, addr: address{ province: "北京", city: "朝阳", }, email: email{ province: "1", test: "2", }, other: other{ province: "3", tmp: "4", }, } fmt.Println(s) fmt.Println(s.name) fmt.Println(s.addr.province) fmt.Println(s.addr.city) //可省略类型名 fmt.Println(s.test) //匿名字段冲突时,需要加上类型名,不能省略 // fmt.Println(s.province) //ambiguous selector s.province fmt.Println(s.email.province) //1 } ``` ### 三、结构体的"继承" Go语言中使用结构体也可以实现其他编程语言中面向对象的继承。 ```go //结构体的"继承" type person struct{ name string } func (p *person)dinner(){ fmt.Printf("%s 要吃饭!\n",p.name) } type student07 struct { number int *person //通过嵌套匿名结构体实现继承 } func (s *student07)study(){ fmt.Printf("%s的学号是%d\n",s.name,s.number) } func main() { stu := student07{ number: 100, person:&person{name: "张三",}, //注意嵌套的是结构体指针 } fmt.Println(stu.name) //张三 fmt.Println(stu.number) //100 stu.study() //张三的学号是100 stu.dinner()//张三 要吃饭! } ``` **运行结果** ``` 张三 100 张三的学号是100 张三 要吃饭! ``` ### 四、结构体比较 如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体将可以使用 `==` 或 `!=` 运算符进行比较。可以通过`==`运算符或 DeeplyEqual()函数比较两个结构相同的类型并包含相同的字段值。 因此下面两个比较的表达式是等价的: ```go type student struct { name string age int } func main() { s1 := student{name: "Leefs", age: 10} s2 := student{name: "Leefs", age: 10} //取出结构体中的每个值进行比较 fmt.Println(s1.name == s2.name && s1.age == s2.age) //true //对两个结构体进行比较 (如果两个结构体中的所有值都相等,直接通过 == 号判断为true) fmt.Println(s1 == s2) //true } ``` ### 五、结构体与JSON序列化 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON键值对是用来保存JS对象的一种方式,键/值对组合中的键名写在前面并用双引号`""`包裹,使用冒号`:`分隔,然后紧接着值;多个键值之间使用英文`,`分隔。 **示例代码** ```go //结构体与JSON序列化 type Student struct { Id int `json:"id"` //通过tag实现json序列化该字段时的key Name string `json:"name"` } func main() { stu1 := &Student{ Id: 1, Name: "张三", } fmt.Println("序列化:把编程语言里面的数据转换成JSON格式的字符串") v, err := json.Marshal(stu1) if err != nil { fmt.Println("JSON格式化出错!") fmt.Println(err) return } fmt.Println(v) //[]byte fmt.Println(string(v)) //把[]byte转换成string fmt.Printf("%#v\n", string(v)) //看真实的格式 fmt.Println("反序列化:把JONS格式的字符串转成编程语言中的对象") str := string(v) // str := "{\"Id\":1,\"Name\":\"张三\"}" var stu2 = &Student{} json.Unmarshal([]byte(str), stu2) fmt.Println(*stu2) fmt.Printf("%T\n", stu2) } ``` **运行结果** ``` 序列化:把编程语言里面的数据转换成JSON格式的字符串 [123 34 105 100 34 58 49 44 34 110 97 109 101 34 58 34 229 188 160 228 184 137 34 125] {"id":1,"name":"张三"} "{\"id\":1,\"name\":\"张三\"}" 反序列化:把JONS格式的字符串转成编程语言中的对象 {1 张三} *main.Student ``` ### 六、结构体标签(Tag) `Tag`是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 `Tag`在结构体字段的后方定义,由一对**反引号**包裹起来,具体的格式如下: ```bash `key1:"value1" key2:"value2"` ``` 结构体tag由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔。 **注意事项:** 为结构体编写`Tag`时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。 例如我们为`Student`结构体的每个字段定义json序列化时使用的Tag: ```go //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: "Leffs", } 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":"男"} } ``` *附参考原文链接地址* *https://www.liwenzhou.com/posts/Go/10_struct/#autoid-2-7-0*
标签:
Golang基础
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2214.html
上一篇
10.Golang结构体(一)
下一篇
12.Golang函数
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
JavaWeb
Jquery
Docker
持有对象
SpringBoot
Scala
Beego
前端
LeetCode刷题
Netty
稀疏数组
Spark Streaming
Spark SQL
gorm
数据结构
Redis
随笔
Livy
CentOS
数据结构和算法
Golang
FastDFS
HDFS
Eclipse
Typora
数学
Thymeleaf
Spark
线程池
排序
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭