Go语言结构体,这一篇就够了

news/2025/2/12 22:07:50/

Go语言结构体,这一篇就够了

  • 1.结构体的概念
  • 2.结构体的定义
  • 3.结构体的实例化
  • 4.结构体初始化
  • 5.构造函数
  • 6.方法和接收者
    • 方法
    • 接收者
  • 7.嵌套结构体
  • 8.结构体的“继承”
  • 9.结构体与JSON序列化
  • 10.结构体标签(Tag)

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。

1.结构体的概念

Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。 也就是我们可以通过struct来定义自己的类型了。

Go语言中通过struct来实现面向对象🩳


2.结构体的定义

使用type和struct关键字来定义结构体

举个例子,我们定义一个Person(人)结构体,代码如下:

type person struct {name stringcity stringage  int8
}

同样类型的字段也可以写在一行:

type person struct {name, city stringage        int8
}

3.结构体的实例化

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

package mainimport "fmt"type person struct {name stringcity stringage  int8
}func main() {var p personp.name = "大河"p.age = 18p.city = "长春"fmt.Println(p) // {大河 长春 18}fmt.Println(p.name)  // 大河
}

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

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

4.结构体初始化

使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值

package mainimport "fmt"type person struct {name stringcity stringage  int8
}func main() {p := person{name: "大河",city: "北京",}fmt.Println(p)  // {大河 北京 0}
}

初始化结构体的时候可以简写,也就是初始化的时候不写键,直接写值:

package mainimport "fmt"type person struct {name stringcity stringage  int8
}func main() {p := person{"张三","北京",18,}fmt.Println(p) // {张三 北京 18}
}

使用这种格式初始化时,需要注意:

  • 必须初始化结构体的所有字段。
  • 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
  • 该方式不能和键值初始化方式混用。

5.构造函数

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

package mainimport "fmt"type person struct {name stringcity stringage  int8
}/*
构造函数
*/
func newPerson(name, city string, age int8) *person {return &person{name: name,city: city,age:  age,}
}func main() {p := newPerson("大河", "北京", 22)fmt.Println(p)  // &{大河 北京 22}
}

6.方法和接收者

方法

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

方法的定义格式如下:

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

方法使用实例:

package mainimport "fmt"type Person struct {name stringcity stringage  int8
}/*
构造函数
*/
func newPerson(name, city string, age int8) *Person {return &Person{name: name,city: city,age:  age,}
}func (p Person) say() {fmt.Println("我要说话了!")
}func main() {p := newPerson("大河", "北京", 22)p.say()
}

方法与函数的区别是,函数不属于任何类型,方法属于特定的类型🧣

接收者

指针类型的接收者

指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的this或者self。 例如我们为Person添加一个SetAge方法,来修改实例变量的年龄。

package mainimport "fmt"type Person struct {name stringcity stringage  int8
}/*
构造函数
*/
func newPerson(name, city string, age int8) *Person {return &Person{name: name,city: city,age:  age,}
}func (p *Person) setAge(newAge int8) {p.age = newAge
}func main() {p := newPerson("大河", "北京", 22)p.setAge(33)fmt.Println(p)  // &{大河 北京 33}
}

值类型的接收者

当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。

package mainimport "fmt"type Person struct {name stringcity stringage  int8
}/*
构造函数
*/
func newPerson(name, city string, age int8) *Person {return &Person{name: name,city: city,age:  age,}
}func (p Person) setAge(newAge int8) {p.age = newAge
}func main() {p := newPerson("大河", "北京", 22)p.setAge(33)fmt.Println(p) // &{大河 北京 22}
}

什么时候应该使用指针类型接收者?

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

7.嵌套结构体

一个结构体中可以嵌套包含另一个结构体或结构体指针,就像下面的示例代码那样。

package mainimport "fmt"type Address struct {Province stringCity     string
}// User 用户结构体
type User struct {Name    stringGender  stringAddress Address
}func main() {user := User{Name:   "小王子",Gender: "男",Address: Address{Province: "山东",City:     "威海",},}fmt.Println(user)  // {小王子 男 {山东 威海}}
}

嵌套结构体内部可能存在相同的字段名。在这种情况下为了避免歧义需要通过指定具体的内嵌结构体字段名

package mainimport "fmt"// Address 地址结构体
type Address struct {Province   stringCity       stringCreateTime string
}// Email 邮箱结构体
type Email struct {Account    stringCreateTime string
}// User 用户结构体
type User struct {Name   stringGender stringAddressEmail
}func main() {var user Useruser.Name = "dahe"user.Gender = "1"user.Address.CreateTime = "2022"user.Email.CreateTime = "2023"fmt.Println(user) // {dahe 1 {  2022} { 2023}}
}

结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)🧥


8.结构体的“继承”

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

例如:

package mainimport "fmt"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.move()  // 乐乐会动!d.wang()  // 乐乐会汪汪汪~
}

9.结构体与JSON序列化

JSON是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON键值对是用来保存JS对象的一种方式,键/值对组合中的键名写在前面并用双引号""包裹,使用冒号:分隔,然后紧接着值;多个键值之间使用英文,分隔

序列化和反序列化程序实例:

package mainimport ("encoding/json""fmt"
)// Student 学生
type Student struct {ID     intGender stringName   string
}// Class 班级
type Class struct {Title    stringStudents []*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(c)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)
}

10.结构体标签(Tag)

Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:

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

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

package mainimport ("encoding/json""fmt"
)// Student 学生
type Student struct {ID     int    `json:"uid"` // 通过指定tag实现json序列化该字段时的keyGender string // json序列化是默认使用字段名作为keyname   string // 私有不能被json包访问
}func main() {s := Student{ID:     1,Gender: "男",name:   "沙河娜扎",}data, err := json.Marshal(s)if err != nil {fmt.Println("json marshal failed!")return}fmt.Printf("json str:%s\n", data)// json str:{"uid":1,"Gender":"男"}
}

版权声明:本文教程基于李文周的Go语言博客 - 结构体篇


http://www.ppmy.cn/news/30702.html

相关文章

JavaScript 高级4 :正则表达式

JavaScript 高级4 &#xff1a;正则表达式 Date: January 19, 2023 Text: 正则表达式、正则表达式特殊字符、正则表达式中的替换 目标&#xff1a; 能够说出正则表达式的作用 能够写出简单的正则表达式 能够使用正则表达式对表单进行验证 能够使用正则表达式替换内容 正则…

[项目] Boost搜索引擎

目录 1.项目相关背景 2.项目宏观原理 3.技术栈和项目环境 4.正排索引&&倒排索引 5.去标签与数据清洗 6.构建索引模块Index 6.1正排索引 6.2 建立倒排 jiebacpp使用 建立分词 7.搜索引擎模块Searcher Jsoncpp -- 通过jsoncpp进行序列化和反序列化 处理Cont…

udk开发-稀里糊涂

一、EDK2简介 1.EDK2工作流 ​ 二、EDK2 Packages 1.Packages介绍 ​ EDK2 Packages是一个容器&#xff0c;其中包含一组模块及模块的相关定义。每个Package是一个EDK2单元。 整个Project的源代码可以被分割成不同的Pkg。这样的设计不仅可以降低耦合性&#xff0c;还有利于分…

new set数组对象去重失败

我们知道Set是JS的一个种新的数据结构&#xff0c;和数组类似&#xff0c;和数组不同的是它可以去重&#xff0c;比如存入两个1或两个"123"&#xff0c;只有1条数据会存入成功&#xff0c;但有个特殊情况&#xff0c;如果添加到set的值是引用类型&#xff0c;比如数组…

Validator校验之ValidatorUtils

注意&#xff1a;hibernate-validator 与 持久层框架 hibernate 没有什么关系&#xff0c;hibernate-validator 是 hibernate 组织下的一个开源项目 。 hibernate-validator 是 JSR 380&#xff08;Bean Validation 2.0&#xff09;、JSR 303&#xff08;Bean Validation 1.0&…

tcsh常用配置

查看当前的shell类型 在 Linux 的世界中&#xff0c;有着许多 shell 程序。常见的有&#xff1a; Bourne shell (sh) C shell (csh) TC shell (tcsh) Korn shell (ksh) Bourne Again shell (bash) 其中&#xff0c;最常用的就是bash和tcsh&#xff0c;本次文章介绍tcsh的…

60% 程序员大呼:我要远程办公!

近几年数字化的普及&#xff0c;白领们从挤地铁、打卡、开会、写日报转变成“早上9点视频会议”&#xff0c;企业的办公场所也从写字楼、会议室、工位变成了手机、电脑中的线上会议室&#xff0c;远程办公已经成为一种流行的办公形式。《财富》杂志发现&#xff0c;75%的员工表…

C++vector 简单实现

一。概述 vector是我们经常用的一个容器&#xff0c;其本质是一个线性数组。通过对动态内存的管理&#xff0c;增删改查数据&#xff0c;达到方便使用的目的。 作为一个线性表&#xff0c;控制元素个数&#xff0c;容量&#xff0c;开始位置的指针分别是&#xff1a; start …