李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
GO
正文
11.Golang结构体(二)
Leefs
2022-07-03 PM
695℃
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
标签云
前端
Scala
Thymeleaf
Filter
Flume
链表
Spark Streaming
Docker
工具
ajax
字符串
MyBatis
Git
ClickHouse
MyBatis-Plus
Spark
Yarn
nginx
线程池
机器学习
Spark RDD
人工智能
Redis
BurpSuite
Netty
Java阻塞队列
Golang
HDFS
Elasticsearch
查找
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞