Go语言之结构体

在实际开发中,我们可以将一组类型不同的、但是用来描述同一件事物的变量放到结构体中。例如,在校学生有姓名、年龄、身高、成绩等属性,学了结构体后,我们就不需要再定义多个变量了,将它们都放到结构体中即可。

在Go语言中,结构体承担着面向对象语言中类的作用。Go语言中,结构体本身仅用来定义属性。还可以通过接收器函数来定义方法,使用内嵌结构体来定义继承。这样使用结构体相关操作Go语言就可以实现OOP面向对象编程了。

1、声明结构体

Go语言通过type和struct关键字声明结构体,格式如下:

type 类型名 struct {   // 标识结构体的类型名,在同一个包内不能重复字段1 字段1类型    // 字段名必须唯一字段2 字段2类型…
}

Go语言结构体(Struct)从本质上讲是一种自定义的数据类型,只不过这种数据类型比较复杂,是由 int、char、float 等基本类型组成的。你可以认为结构体是一种聚合类型。


type Student struct {sid    intname   stringage    int8course []string //  选秀课程
}

Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性。结构体成员,也可称之为成员变量,字段,属性。属性要满足唯一性。
同类型的变量也可以写在一行,用逗号隔开

type Book struct {title,author stringprice int
}

2、结构体的实例化

结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存,因此必须在定义结构体并实例化后才能使用结构体的字段。实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。
实例化方式包括如下几种。

2.1、声明结构体变量再赋值

结构体本身是一种类型,可以像整型、字符串等类型一样,以 var 的方式声明结构体即可完成实例化。

package mainimport "fmt"type Student struct {sid    intname   stringage    int8course []string //  选秀课程
}func main() {// 声明一个结构体对象 ,值类型,默认开辟空间,字段赋予零值var s Studentfmt.Println("s:", s)// 要访问结构体成员,需要使用点号 . 操作符fmt.Println(s.name)// 更改成员变量的值s.name = "yuan"fmt.Println(s.name)// s.course[0] = "chinese"   // 结果,如何调整}

1、结构体属于值类型,即var声明后会像整形字符串一样创建内存空间。
2、创建结构体对象如果没有给字段赋值,则默认零值(字符串默认 “",数值默认0,布尔默认false,切片和map默认nil对象)

结构体的内存存储:

package mainimport "fmt"type Student struct {sid    intname   stringage    int8course []string //  选秀课程
}func main() {// 声明一个结构体对象 ,值类型,默认开辟空间,字段赋予零值var s Studentfmt.Println("s:", s)s.sid = 1001s.name = "yuan"s.age = 23s.course = []string{"chinese", "math", "english"}fmt.Printf("%p\n", &s)fmt.Printf("%p\n", &(s.sid))fmt.Printf("%p\n", &(s.name))fmt.Printf("%p\n", &(s.age))fmt.Printf("%p\n", &(s.course)) // 切片24个字节
}

在这里插入图片描述

之前我们学习过值类型和引用类型,知道值类型是变量对应的地址直接存储值,而引用类型是变量对应地址存储的是地址。因为结构体因为是值类型,所以p的地址与存储的第一个值的地址是相同的,而后面每一个成员变量的地址是连续的。

2.2、实例化之 结构体

// (1) 方式1
s1 := Student{}
s1.sid = 1001
s1.name = "yuan"
// (2) 方式2:键值对赋值
s2 := Student{sid: 1002, name: "rain", course: []string{"chinese", "math", "english"}}
fmt.Println(s2)
// (3) 方式3:多值赋值
s3 := Student{1003, "alvin", 22, []string{"chinese", "math", "english"}}
fmt.Println(s3)

1、结构体可以使用“键值对”(Key value pair)初始化字段,每个“键”(Key)对应结构体中的一个字段,键的“值”(Value)对应字段需要初始化的值。键值对的填充是可选的,不需要初始化的字段可以不填入初始化列表中,走默认值。
2、多值初始化方式必须初始化结构体的所有字段且每一个初始值的填充顺序必须与字段在结构体中的声明顺序一致。
3、键值对与值列表的初始化形式不能混用。

2.3、实例化之&结构体

package mainimport "fmt"type Student struct {sid    intname   stringage    int8course []string //  选秀课程
}func CourseInit(stu Student) {stu.course = []string{"chinese", "math", "english"}fmt.Println(stu)
}func CourseInit2(stu *Student) {(*stu).course = []string{"chinese", "math", "english"}
}func main() {// 案例1s1 := Student{sid: 1001, name: "alvin", age: 32}s2 := s1 // 值拷贝fmt.Println(s2)s1.age = 100fmt.Println(s2.name)// 如果希望s3的值跟随s2保持一致怎么实现s3 := &s1 // var s4 *Student = &s2s1.age = 100fmt.Println((*s3).age)fmt.Println(s3.age)// 案例2var s4 = Student{sid: 1001, name: "alvin", age: 32}CourseInit(s4)fmt.Println("s报的课程:", s4.course)// 怎么能初始化成功呢?var s5 = &Student{sid: 1001, name: "alvin", age: 32}CourseInit2(s5)fmt.Println("s报的课程:", (*s5).course) // *s.course的写法是错误的fmt.Println("s报的课程:", s5.course)}

在Go语言中,结构体指针的变量可以继续使用.,这是因为Go语言为了方便开发者访问结构体指针的成员变量可以像访问结构体的成员变量一样简单,使用了语法糖(Syntactic sugar)技术,将 instance.Name 形式转换为 (*instance).Name。

2.4、实例化之 new(结构体)

Go语言中,还可以使用 new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。使用 new 的格式如下:其中:

instance := new(T)

其中:
T 为类型,可以是结构体、整型、字符串等。
instance:T 类型被实例化后保存到 instance 变量中,instance的类型为 *T,属于指针。

s := new(Student)              // &Student{}
fmt.Println(reflect.TypeOf(s)) // *Student
fmt.Println(s)                 // *Student
s.name = "yuan"
fmt.Println((*s).name)
fmt.Println(s.name)

4、模拟构造函数

Go语言没有构造函数,但是我们可以使用结构体初始化的过程来模拟实现构造函数。

package mainimport "fmt"type Student struct {sid    intname   stringage    int8course []string //  选秀课程
}func NewStudent(sid int, name string, age int8, course []string) *Student {return &Student{sid:    sid,name:   name,age:    age,course: course,}
}func main() {s := NewStudent(1001, "yuan", 32, nil)fmt.Println(s)}

5、方法接收器

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。
方法的定义格式如下:

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

其中,
接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
方法名、参数列表、返回参数:具体格式与函数定义相同。

package mainimport "fmt"type Player struct {Name        stringHealthPoint intLevel       intNowPosition []intProp        []string
}func NewPlayer(name string, hp int, level int, np []int, prop []string) *Player {return &Player{name,hp,level,np,prop,}
}func (p Player) attack() {fmt.Printf("%s发起攻击!\n", p.Name)
}
func (p *Player) attacked() {fmt.Printf("%s被攻击!\n", p.Name)p.HealthPoint -= 10fmt.Println(p.HealthPoint)
}func (p *Player) buyProp(prop string) {p.Prop = append(p.Prop, prop)fmt.Printf("%s购买道具!\n", p.Name)
}func main() {player := NewPlayer("yuan", 100, 100, nil, nil)player.attack()player.attacked()fmt.Println(player.HealthPoint)player.buyProp("魔法石")fmt.Println(player.Prop)
}

1、官方定义:Methods are not mixed with the data definition (the structs): they are orthogonal to types; representation(data) and behavior (methods) are independent
2、方法与函数的区别是,函数不属于任何类型,方法属于特定的类型。

6、匿名字段

结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。

package mainimport "fmt"type Person struct {stringint
}func main() {p1 := Person{"yuan",18,}fmt.Printf("%#v\n", p1)        //main.Person{string:"yuan", int:18}fmt.Println(p1.string, p1.int) //北京 18
}

结构体也可以作为匿名字段使用

package mainimport "fmt"type Addr struct {country  stringprovince stringcity     string
}type Person struct {name stringage  intAddr
}func main() {p1 := Person{"yuan",18,Addr{"中国", "广东省", "深圳"},}fmt.Printf("%#v\n", p1)      //main.Person{string:"北京", int:18}fmt.Println(p1.name, p1.age) // yuan 18fmt.Println(p1.Addr)fmt.Println(p1.Addr.country) // 中国fmt.Println(p1.city)         // 深圳
}

当结构体中有和匿名字段相同的字段时,采用外层优先访问原则

7、结构体的继承

package mainimport "fmt"//Animal 动物
type Animal struct {name string
}func (a *Animal) eat() {fmt.Printf("%s is eating!\n", a.name)
}
func (a *Animal) sleep() {fmt.Printf("%s is sleeping!\n", a.name)
}// Dog 类型
type Dog struct {Kind    string*Animal //通过嵌套匿名结构体实现继承
}func (d *Dog) bark() {fmt.Printf("%s is barking ~\n", d.name)
}// Cat 类型
type Cat struct {*Animal
}func (c *Cat) climbTree() {fmt.Printf("%s is climb tree ~\n", c.name)
}func main() {d1 := &Dog{Kind: "金毛",Animal: &Animal{ //注意嵌套的是结构体指针name: "旺财",},}d1.eat()d1.bark()c1 := &Cat{Animal: &Animal{name: "喵喵",},}c1.sleep()c1.climbTree()}

8、序列化

序列化: 通过某种方式把数据结构或对象写入到磁盘文件中或通过网络传到其他节点的过程。
反序列化:把磁盘中对象或者把网络节点中传输的数据恢复为python的数据对象的过程。

8.1、json初识

序列化最重要的就是json序列化。
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

在这里插入图片描述

8.2、结构体的json操作

package mainimport ("encoding/json""fmt"
)type Addr struct {Province stringCity     string
}
type Stu struct {Name string `json:"name"` // 结构体的标签Age  int    `json:"-"`    // 表示不参与序列化Addr Addr
}func main() {var stuMap = map[string]interface{}{"name": "yuan", "age": 32, "addr": "beijing"}var stuStruct = Stu{Name: "yuan", Age: 18, Addr: Addr{Province: "Hebei", City: "langFang"}}// 序列化jsonStuMap, _ := json.Marshal(stuMap)jsonStuStruct, _ := json.Marshal(stuStruct)fmt.Println(string(jsonStuMap))fmt.Println(string(jsonStuStruct))// 反序列化// var x  = make(map[int]string)var StuMap map[string]interface{}err := json.Unmarshal(jsonStuMap, &StuMap)if err != nil {return}fmt.Println("StuMap", StuMap, StuMap["name"])var StuStruct Stuerr := json.Unmarshal(jsonStuStruct, &StuStruct)if err != nil {return}fmt.Println(StuStruct)fmt.Println(StuStruct.Name)fmt.Println(StuStruct.Addr.City)}

章节作业

将客户关系管理系统改为结构体版本

package mainimport ("bufio""encoding/json""fmt""io/ioutil""os""strings"
)type Customer struct {Cid    intName   stringGender stringAge    int8Email  string
}func NewCustomer(cid int, name string, gender string, age int8, email string) Customer {return Customer{Cid:    cid,Name:   name,Gender: gender,Age:    age,Email:  email,}
}type CustomerService struct {customers   []CustomercustomersId int
}func NewCustomerService(customers []Customer, customersId int) CustomerService {return CustomerService{customers, customersId}
}func (cs *CustomerService) findById(id int) int {index := -1for i := 0; i < len(cs.customers); i++ {if cs.customers[i].Cid == id {index = i}}return index
}func (cs *CustomerService) nextChoice() (b bool) {// 引导用户选择继续还是返回fmt.Print("返回上一层【回车】,继续该操作【C/c】,退出【Q/q】:")var Choice stringfmt.Scanln(&Choice)if strings.ToUpper(Choice) == "C" {b = true} else if strings.ToUpper(Choice) == "Q" {os.Exit(0)}return
}func (cs *CustomerService) addCustomer() {for true {// 引导用户输入学号和姓名fmt.Printf("\033[1;35;40m%s\033[0m\n", "---------------------------添加客户开始-----------------------------")// 引导用户输入var name stringfmt.Print("请输入客户姓名:")fmt.Scan(&name)var gender stringfmt.Print("请输入客户性别:")fmt.Scan(&gender)var age int8fmt.Print("请输入客户年龄:")fmt.Scan(&age)var email stringfmt.Print("请输入客户邮箱:")fmt.Scan(&email)// 创建客户的map对象cs.customersId++ // 客户编号不需要输入,系统自增即可newCustomer := NewCustomer(cs.customersId, name, gender, age, email)// 添加客户map对象添加到客户切片中cs.customers = append(cs.customers, newCustomer)fmt.Printf("\033[1;35;40m%s\033[0m\n", "---------------------------添加客户完成-----------------------------")goOn := cs.nextChoice()if !goOn {break}}
}func (cs *CustomerService) listCustomer() {for true {fmt.Printf("\033[1;32;40m%s\033[0m\n", "----------------------------------客户列表开始----------------------------")for _, customer := range cs.customers {fmt.Printf("\u001B[1;39;45m编号:%-8d 姓名:%-8s 性别:%-8s 年龄:%-8d 邮箱:%-10s \u001B[0m\n",customer.Cid, customer.Name, customer.Gender, customer.Age, customer.Email)}fmt.Printf("\033[1;32;40m%s\033[0m\n", "----------------------------------客户列表完成----------------------------")goOn := cs.nextChoice()if !goOn {break}}
}
func (cs *CustomerService) updateCustomer() {fmt.Printf("\033[1;36;40m%s\033[0m\n", "---------------------------客户修改开始----------------------------")for true {var updateCid intfmt.Print("请输入更新客户编号(-1退出):")fmt.Scan(&updateCid)if updateCid == -1 {return}updateIndex := cs.findById(updateCid)if updateIndex == -1 {fmt.Println("删除失败,输入的编号ID不存在")continue}// 引导用户输入var name stringfmt.Printf("请输入客户姓名(%s):", cs.customers[updateIndex].Name)fmt.Scanln(&name)var gender stringfmt.Printf("请输入客户性别(%s):", cs.customers[updateIndex].Gender)fmt.Scanln(&gender)var age int8fmt.Printf("请输入客户年龄(%d):", cs.customers[updateIndex].Age)fmt.Scanln(&age)var email stringfmt.Printf("请输入客户邮箱(%s):", cs.customers[updateIndex].Email)fmt.Scanln(&email)if age != 0 {cs.customers[updateIndex].Age = age}if name != "" {cs.customers[updateIndex].Name = name}if gender != "" {cs.customers[updateIndex].Gender = gender}if email != "" {cs.customers[updateIndex].Email = email}fmt.Printf("\033[1;36;40m%s\033[0m\n", "---------------------------客户修改完成----------------------------")goOn := cs.nextChoice()if !goOn {break}}
}func (cs *CustomerService) deleteCustomer() {for true {fmt.Printf("\033[1;31;40m%s\033[0m\n", "---------------------------删除客户开始----------------------------")var delCid intfmt.Print("请输入删除客户编号:")fmt.Scan(&delCid)delIndex := cs.findById(delCid)if delIndex == -1 {fmt.Println("删除失败,输入的编号ID不存在")continue}cs.customers = append(cs.customers[:delIndex], cs.customers[delIndex+1:]...)fmt.Printf("\033[1;31;40m%s\033[0m\n", "---------------------------删除客户完成----------------------")goOn := cs.nextChoice()if !goOn {break}}
}func (cs *CustomerService) keepCustomers() {customersJsonBytes, _ := json.Marshal(cs.customers)err := ioutil.WriteFile("customers.json", customersJsonBytes, 0666)if err != nil {return}fmt.Printf("\033[1;31;40m%s\033[0m\n", "---------------------------保存完成----------------------")}func main() {var customers []CustomercustomersJsonBytes, err := ioutil.ReadFile("customers.json")if err != nil {return}json.Unmarshal(customersJsonBytes, &customers)cs := NewCustomerService(customers, customers[len(customers)-1].Cid)for true {fmt.Printf("\033[1;30;42m%s\033[0m\n", `
----------------客户信息管理系统--------------1、添加客户2、查看客户3、更新客户4、删除客户5、保存6、退出
-------------------------------------------
`)var choice intfmt.Printf("\033[1;38;40m%s\033[0m", "请输入选择【1-5】:")stdin := bufio.NewReader(os.Stdin)fmt.Fscan(stdin, &choice)switch choice {case 1:cs.addCustomer()case 2:cs.listCustomer()case 3:cs.updateCustomer()case 4:cs.deleteCustomer()case 5:cs.keepCustomers()case 6:os.Exit(0)default:fmt.Println("按要求输入数字,请重新输入")}}}

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

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

相关文章

uni-app:scroll-view滚动盒子,实现横(纵)向滚动条

参照&#xff1a;scroll-view | uni-app官网 (dcloud.net.cn) 样式&#xff1a; 代码&#xff1a; <template><view class"box"><scroll-view scroll-x"true" class"scroll"><view class"box1"> <view c…

MySQL八股学习记录6-日志from小林coding

MySQL八股学习记录6-日志from小林coding MySQL日志分类undo logBuffer Poolredo logbinlogredo log 和undo log有什么区别主从复制是如何实现update语句执行过程为什么需要两阶段提交 MySQL日志分类 undo log:InnoDB存储引擎层生成的日志,实现事务中的原子性,主要用于事务回滚…

Linux进程控制(二)---进程等待

目录 什么是进程等待 为什么要进行进程等待&#xff1f; wait() waitpid() status的使用★ options★ 问题&#xff1a;既然进程具有独立性&#xff0c;进程退出码不也是子进程数据吗&#xff0c;父进程凭什么拿到呢&#xff1f;wait/waitpid究竟做了什么呢&#xff1f; …

Spring Cloud Hystrix简单实用

文章目录 一、简介二、快速开始1、pom依赖2、启动类注解3、服务降级配置HystrixCommand4、配置熔断策略5、测试 三、原理分析四、实际使用 一、简介 Hystrix&#xff0c;英文意思是豪猪&#xff0c;全身是刺&#xff0c;刺是一种保护机制。Hystrix也是Netflflix公司的一款组件。…

Kotlin获取Fragment中的组件

左边和右边分别是两个不同的Fragment&#xff0c;左边的Fragment中右一个Button组件&#xff0c;目标是想要获取这个组件的id&#xff0c;以便进行将右边的Fragment更改成另一个Fragmeent的操作。 left_fragment.xml <?xml version"1.0" encoding"utf-8&qu…

玩玩两个简单的python的web框架 flask、fastapi

IDEA连接远程解释器&#xff0c;本地代码编辑无法代码提示 一、Flask入门使用 官网 其它参考 注意 1.这里使用linux 192.168.72.126上远程解释器,需要/usr/bin/pip3 install flask&#xff0c;host参数不要使用localhost/127.0.0.1,即只监听本地的访问&#xff0c;会导致wind…

手机定屏死机问题操作指南

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、定屏死机问题抓取 Log 要求二、 复现定屏死机问题后做什么三、检查adb是否可连的方法四、连接adb 抓取以下Log五、如果adb不可连&#xff0c;执行下…

IDEA实用设置及插件

一、IDEA实用设置 二、IDEA实用插件 1. aiXcoder是一个基于最先进的深度学习技术的强大的代码完成器和代码搜索引擎。它有可能向您推荐一整行代码&#xff0c;这将帮助您更快地进行编码。AiXcoder还提供了一个代码搜索引擎&#xff0c;帮助您在GitHub上搜索API用例。 2. 阿里…

【iOS】—— 面向对象,Runtime,ARC等问题总结

对于暑假学习大多数是对之前学习的一个复习&#xff0c;在这里只做对之前学习欠缺知识的补充以及这些知识点涉及的一些问题&#xff0c;从问题入手学习。 文章目录 面向对象1.一个NSObject对象占多少内存&#xff1f;2.对象的isa指针指向哪里&#xff1f;3.OC的类信息存放在哪…

HarmonyOS/OpenHarmony应用开发-Stage模型UIAbility组件使用(五)

UIAbility组件间交互&#xff08;设备内&#xff09; UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时&#xff0c;会涉及到启动特定的UIAbility&#xff0c;该UIAbility可以是应用内的其他UIAbility&#xff0c;也可以是其他应用的UIAbility&#xff08;例如启动…

Dcat-admin使用 Alpine 双向数据绑定

介绍 Alpine.js 这东西真的轻量级&#xff0c;和vue相似&#xff0c;和 livewire 同一个作者&#xff0c;推荐大家使用&#xff0c;可以平替jquery 效果 实现 在 bootstrap.php 引入js Admin::headerJs([https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-y/alpinejs/3.9.0/…

unity 2019 内置渲染管线 光照与Lighting面板 参数详解

文章目录 前言一 Unity的光照 与 烘焙光照1 unity完整的光照组成2 光的亮度与颜色3 全局光照直接光间接光5 间接光≠光照贴图 二 色彩空间与自动烘焙1 unity的色彩空间2 自动烘焙光照 三 烘焙1 什么是烘焙&#xff0c;烘焙的是什么2 如何进行烘焙3 烘焙的优点和缺点4 查看光照贴…