golang中的结构体

1.简介

go中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或者部分属性时,这时候再用单一的基本数据类型就无法满足需求了,go提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。也就是我们可以通过struct来自定义自己的类型。比如 猫封装一下,属性:颜色、名字、年龄。方法:跑、跳等,把这个东西封装一起可以封装成结构体。

2.type关键词自定义类型和类型别名

2.1自定义类型

在go语言中有一些基本的数据类型,比如string、int、bool等数据类型,可以用type关键字自定义类型。

语法: type comstomInt int

将comstomInt 定义为int类型,通过type关键字的定义,comstomInt就是一种新的类型,comstomInt和int不是同一类型,它具有int的特性。

复制代码
    type comstomInt int  
    var a comstomInt = 3
	var b int = 3
	fmt.Printf("a:%v %T\n", a, a) //a:3 main.comstomInt
	fmt.Printf("b:%v %T\n", b, b) //b:3 int

2.2类型别名

类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一类型。就像外号和真实姓名都是指的同一个人。

语法: type intAlias=int

我们之前见过的rune和byte就是类型别名:

type byte = uint8

type rune = int32

2.3自定义类型和类型别名的区别

类型别名与自定类型表面上看只有一个等号的差异,我们通过下面的这段代码来理解他们之间的却别。

Go 复制代码
type comstomInt int
type intAlias = int

func main() {
	var a intAlias = 3
	fmt.Printf("a:%v %T\n", a, a) //a:3 int
	var b comstomInt = 3
	fmt.Printf("b:%v %T\n", b, b) //b:3 main.comstomInt
}

结果显示a的类型时int类型。b的类型时main.comstomInt,表示main包下定义的comstomInt类型。

3.结构体的定义

使用type和struct关键字来定义结构体,语法如下:

type 类型名 struct{

属性1 类型

属性2 类型

...

}

解释:

  • 类型名:表示自定义结构体的名称,在一个包内不能重复。(首字母大写可以外部访问)
  • 属性:表示结构体字段名,结构体中的字段必须唯一。(首字母大写可以外部访问)
  • 类型:表示结构体属性的具体类型。

举个例子,我们定义一个学生Student的结构体:

Go 复制代码
type Student struct {
	Name   string
	Number int
	Sex    int
	Age    int
}

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

Go 复制代码
type Student struct {
	Name   string
	Number,Sex,Age int
}

4.结构体的实例化

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

4.1第一种方法

结构体本身也是一种数据类型,我们可以像声明内置类型一样使用var关键字声明结构提类型。

var 结构体实例 结构体类型

Go 复制代码
type Student struct {
	Name   string //姓名
	Number int    //学号
}

func main() {
	var s1 Student
	s1.Name = "韩梅梅"
	s1.Number = 20250306123
	fmt.Printf("s1=%#v\n", s1)//s1=main.Student{Name:"韩梅梅", Number:20250306123}
}

4.2第二种方法

可以通过new关键字对结构体进行实例化,得到的是具体的地址。

Go 复制代码
type Student struct {
	Name   string //姓名
	Number int    //学号
}

func main() {
	s1 := new(Student)
	s1.Name = "韩梅梅"
	s1.Number = 20250306123
	fmt.Printf("s1=%#v\n", s1) //s1=&main.Student{Name:"韩梅梅", Number:20250306123}
	fmt.Println(s1.Name)       //韩梅梅
    fmt.Println((*s1).Name)    //韩梅梅
}

从结果可以看出s1是一个结构体指针。

注意:在go中支持对结构体指针直接使用点(.)来访问结构体成员。s1. Name和(*s1).Name输出是一样的,go设计者为了程序员使用方便,底层会对结构体进行处理。

4.3第三种方法

使用&对结构体进行取地址操作相当于对该结构体类型进行了new的实例化操作。

Go 复制代码
type Student struct {
	Name   string //姓名
	Number int    //学号
}

func main() {
	s1 := &Student{}
	s1.Name = "韩梅梅"
	s1.Number = 20250306123
	fmt.Printf("s1=%#v\n", s1) //s1=&main.Student{Name:"韩梅梅", Number:20250306123}
	fmt.Println(s1.Name)       //韩梅梅
	fmt.Println((*s1).Name)    //韩梅梅
}

4.4第四种方法

键值对初始化:

Go 复制代码
type Student struct {
	Name   string //姓名
	Number int    //学号
}

func main() {
	s1 := Student{
		Name:   "蕾蕾",
		Number: 20250306123,
	}
	fmt.Printf("s1=%#v\n", s1) //s1=main.Student{Name:"蕾蕾", Number:20250306123}
	fmt.Println(s1.Name)       //蕾蕾
}

4.5第五种方法

结构体指针进行键值对初始化:

Go 复制代码
type Student struct {
	Name   string //姓名
	Number int    //学号
}

func main() {
	s1 := &Student{
		Name: "蕾蕾",
	}
	fmt.Printf("s1=%#v\n", s1) //s1=&main.Student{Name:"蕾蕾", Number:0}
	fmt.Println(s1.Name)       //蕾蕾
	fmt.Println((*s1).Name)    //蕾蕾
}

当某些字段没有初始值的时候,这个字段可以不写,没有初始化的字段返回零值

4.6第六种方法

使用列表初始化,也就是初始化的时候不写键,直接写值。

注意:

  1. 必须初始化结构体的所有字段。
  2. 初始化的填充顺序必须与字段在结构体中的声明顺序一致。
  3. 该初始化不能和键值初始化方式混用。
Go 复制代码
type Student struct {
	Name   string //姓名
	Number int    //学号

}

func main() {
	s1 := &Student{
		"蕾蕾",
		1234567890,
	}
	fmt.Printf("s1=%#v\n", s1) //s1=&main.Student{Name:"蕾蕾", Number:1234567890}
	fmt.Println(s1.Name)       //蕾蕾
	fmt.Println((*s1).Name)    //蕾蕾
}

5.结构体的方法和接收者

5.1结构体是值类型

Go 复制代码
package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func main() {
	p1 := Person{
		"小王子",
		18,
	}
	p2 := p1
	p2.Name = "kings"
	fmt.Printf("%#v\n", p1) //main.Person{string:"小王子", int:18}
	fmt.Printf("%#v\n", p2) //main.Person{Name:"kings", Age:18}
}

在go语言中,没有类的概念但是可以给类型(结构体,自定义类型)定义方法。所谓方法 就是定义了接收者的函数。接收者的概念就类似于其他语言中的this或者 self。

方法的定义格式如下:

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

其中

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

给结构体Person定义一个方法打印Person的信息 :

Go 复制代码
package main

import "fmt"

type Person struct {
	name string
	age  int8
}

func (p Person) printInfo() {
	fmt.Printf("姓名:%v 年龄:%v", p.name, p.age)
}
func main() {
	p1 := Person{
		name: "李雷",
		age:  15,
	}
	p1.printInfo() //姓名:李雷 年龄:15
}

5.2值类型的接收者

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

5.3指针类型的接收者

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

Go 复制代码
package main

import "fmt"

type Person struct {
	name string
	age  int
}

// 值类型接受者
func (p Person) printInfo() {
	fmt.Printf("姓名:%v年龄:%v\n", p.name, p.age)
}

// 指针类型接收者
func (p *Person) setInfo(name string, age int) {
	p.name = name
	p.age = age
}
func main() {
	p1 := Person{
		name: "小王子",
		age:  25,
	}
	p1.printInfo() //姓名:小王子年龄:25
	p1.setInfo("张三", 20)
	p1.printInfo() //姓名:张三年龄:20
}

6.给任意类型添加方法

在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们 的自定义类型添加方法。

Go 复制代码
package main

import "fmt"

type myInt int

func (m myInt) SayHello() {
	fmt.Println("Hello,我是一个int。")
}
func main() {
	var m1 myInt
	m1.SayHello() //Hello,我是一个int。
	m1 = 100
	fmt.Printf("%#v %T\n", m1, m1) //100 main.MyInt
}

注意事项:非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。

7.结构体的匿名字段

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

Go 复制代码
import "fmt"

type Person struct {
	string
	int
}

func main() {
	p1 := Person{
		"小王子",
		18,
	}
	fmt.Printf("%#v\n", p1)        //main.Person{string:"小王子", int:18}
	fmt.Println(p1.string, p1.int) //小王子 18
}

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

8.嵌套结构体

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

Go 复制代码
package main

import "fmt"

type Address struct {
	City  string
	Phone string
}
type User struct {
	Name       string
	Sex        string
	AddressKey Address
}
func main() {
	user := User{
		Name: "张三",
		Sex:  "男",
		AddressKey: Address{
			City:  "杭州",
			Phone: "132*****321",
		},
	}
	fmt.Printf("%#v\n", user)//main.User{Name:"张三", Sex:"男", AddressKey:main.Address{City:"杭州", Phone:"132*****321"}}
}

9.嵌套匿名结构体

Go 复制代码
package main

import "fmt"

type Address struct {
	City  string
	Phone string
}
type User struct {
	Name    string
	Sex     string
	Address //匿名结构体
}

func main() {
	user := User{
		Name: "张三",
		Sex:  "男",
		Address: Address{
			City:  "杭州",
			Phone: "132*****321",
		},
	}
	user.Address.City = "北京"  //通过匿名结构体.字段名访问
	user.City = "上海"          //直接访问匿名结构体的字段名
	fmt.Printf("%#v\n", user) //main.User{Name:"张三", Sex:"男", AddressKey:main.Address{City:"杭州", Phone:"132*****321"}}
}

注意:当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。

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

Go 复制代码
package main

import (
	"fmt"
	"time"
)

type Address struct {
	City       string
	Phone      string
	CreateTime string
}
type Email struct {
	Acount     string
	CreateTime string
}
type User struct {
	Name    string
	Sex     string
	Address //匿名结构体
	Email   //匿名结构体
}

func main() {
	user := User{
		Name: "张三",
		Sex:  "男",
		Address: Address{
			City:  "杭州",
			Phone: "132*****321",
		},
	}
	user.Email.CreateTime = time.Now().Format("2006-01-02 03:04:05")//指定Email结构体中的CreateTime
	user.Address.CreateTime = time.Now().Format("2006-01-02 03:04:05")//指定Address结构体中的VreateTime
	fmt.Printf("%#v\n", user) //main.User{Name:"张三", Sex:"男", Address:main.Address{City:"杭州", Phone:"132*****321", CreateTime:"2025-03-16 05:49:43"}, Email:main.Email{Acount:"", CreateTime:"2025-03-16 05:49:43"}}
}

注意:嵌套结构体内可能存在相同的字段名称,这时候为了避免歧义需要自定具体的嵌套结构体字段。

11.结构体的继承

go语言中使用结构体也可以实现其他编程语言中的继承关系,利用匿名结构体嵌套实现继承。

Go 复制代码
package main

import "fmt"

type Animal struct {
	Name string
}

func (receiver Animal) printName() {
	fmt.Println("名字:", receiver.Name)
}

type Dog struct {
	Color string
	Age   int
	*Animal
}

func main() {
	dog := &Dog{}
	dog.Animal = &Animal{
		Name: "大黄",
	}
	dog.printName() //名字:大黄
}

12.json数据

JSON是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。

json的基本格式:

Go 复制代码
 {
                    "avgSeatView": "4.8%",
                    "avgShowView": "7.5",
                    "boxRate": "58.6%",
                    "boxSplitUnit": {
                        "num": ".",
                        "unit": "万"
                    },
                    "movieInfo": {
                        "movieId": 1294273,
                        "movieName": "哪吒之魔童闹海",
                        "releaseInfo": "上映47天"
                    },
                    "showCount": 142769,
                    "showCountRate": "36.1%",
                    "splitBoxRate": "59.0%",
                    "splitBoxSplitUnit": {
                        "num": ".",
                        "unit": "万"
                    },
                    "sumBoxDesc": "148.58亿",
                    "sumSplitBoxDesc": "134.35亿"
                }

13.结构体与JSON序列化

go json序列化是指把结构体数据转化成json格式的字符串,go json的反序列化是指把json数据转化成go中的结构体对象。

go中的序列化和反序列化主要通过"encoding/json"包中的json.Marshal()和json.Unmarshal()方法实现。

13.1结构体转JSON字符串

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Persion struct {
	ID    int
	Sex   string
	Name  string
	Hobby []string
}

func main() {
	p := Persion{
		ID:    10001,
		Sex:   "男",
		Name:  "赵六",
		Hobby: []string{"篮球", "足球", "羽毛球"},
	}
	fmt.Printf("%#v\n", p) //main.Persion{ID:10001, Sex:"男", Name:"赵六", Hobby:[]string{"篮球", "足球", "羽毛球"}}
	by, err := json.Marshal(p)
	if err != nil {
		fmt.Println("转译错误!!")
	} else {
		fmt.Println(string(by)) //{"ID":10001,"Sex":"男","Name":"赵六","Hobby":["篮球","足球","羽毛球"]}
	}
}

13.2json字符串转换成结构体对象

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Persion struct {
	ID    int
	Sex   string
	Name  string
	Hobby []string
}

func main() {
	str := `{"ID":10001,"Sex":"男","Name":"赵六","Hobby":["篮球","足球","羽毛球"]}`
	var p Persion
	err := json.Unmarshal([]byte(str), &p)
	if err != nil {
		fmt.Println("序列化错误!")
	} else {
		fmt.Printf("%#v\n", p) //main.Persion{ID:10001, Sex:"男", Name:"赵六", Hobby:[]string{"篮球", "足球", "羽毛球"}}
	}
}

14.结构体标签tag

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

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

结构体tag由一个或多个键值组成。键与值使用冒号分隔,值用双引号括起来。同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔。

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

14.结构体转json字符串

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Persion struct {
	ID    int      `json:"id"` //通过指定tag实现json序列化该字段时key(id)
	Sex   string   `json:"sex"`
	Name  string   `json:"name"`
	Hobby []string `json:"hobby"`
}

func main() {
	p := Persion{
		ID:    10001,
		Sex:   "男",
		Name:  "赵六",
		Hobby: []string{"篮球", "足球", "羽毛球"},
	}
	by, err := json.Marshal(p)
	if err != nil {
		fmt.Println("转译错误!!")
	} else {
		fmt.Println(string(by)) // {"id":10001,"sex":"男","name":"赵六","hobby":["篮球","足球","羽毛球"]}

	}
}

 

14.2json字符串转化成结构体

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Persion struct {
	ID    int      `json:"id"` //通过指定tag实现json序列化该字段时key(id)
	Sex   string   `json:"sex"`
	Name  string   `json:"name"`
	Hobby []string `json:"hobby"`
}

func main() {
	str := `{"id":10001,"sex":"男","name":"赵六","hobby":["篮球","足球","羽毛球"]}`
	var p Persion
	err := json.Unmarshal([]byte(str), &p)
	if err != nil {
		fmt.Println("序列化错误!")
	} else {
		fmt.Printf("%#v\n", p) //main.Persion{ID:10001, Sex:"男", Name:"赵六", Hobby:[]string{"篮球", "足球", "羽毛球"}}
	}
}

15.嵌套结构体和json序列化反序列化

15.1结构体转化成json字符串

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	ID    int      `json:"id"` //通过指定tag实现json序列化该字段时key(id)
	Sex   string   `json:"sex"`
	Name  string   `json:"name"`
	Hobby []string `json:"hobby"`
}
type Class struct {
	No      string    `json:"no"`
	Student []Student `json:"student"`
}

func main() {
	var cl Class
	cl.No = "六年级一班"
	stu := make([]Student, 0)
	for i := 0; i < 10; i++ {
		s := Student{
			ID:    1000 + i,
			Sex:   "男",
			Name:  fmt.Sprintf("王五%v", i),
			Hobby: []string{"java", "php", "golang"},
		}
		stu = append(stu, s)
	}
	cl.Student = stu
	by, err := json.Marshal(cl)
	if err != nil {
		fmt.Println("转译错误!")
	} else {
		fmt.Println(string(by))
	}
}

结果:

Go 复制代码
{"no":"六年级一班","student":[{"id":1000,"sex":"男","name":"王五0","hobby":["java","php","golang"]},{"id":1001,"sex":"男","name":"王五1","hobby":["java","php","golang"]},{"id":1002,"sex":"男","name":"王五2","hobby":["java","php","golang"]},{"id":1003,"sex":"男","name":"王五3","hobby":["java","php","golang"]},{"id":1004,"sex":"男","name":"王五4","hobby":["java","php","golang"]},{"id":1005,"sex":"男","name":"王五5","hobby":["java","php","golang"]},{"id":1006,"sex":"男","name":"王五6","hobby":["java","php","golang"]},{"id":1007,"sex":"男","name":"王五7","hobby":["java","php","golang"]},{"id":1008,"sex":"男","name":"王五8","hobby":["java","php","golang"]},{"id":1009,"sex":"男","name":"王五9","hobby":["java","php","golang"]}]}

15.2json字符串转化成结构体

Go 复制代码
package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	ID    int      `json:"id"` //通过指定tag实现json序列化该字段时key(id)
	Sex   string   `json:"sex"`
	Name  string   `json:"name"`
	Hobby []string `json:"hobby"`
}
type Class struct {
	No      string    `json:"no"`
	Student []Student `json:"student"`
}

func main() {
	str := `{"no":"六年级一班","student":[{"id":1000,"sex":"男","name":"王五0","hobby":["java","php","golang"]},{"id":1001,"sex":"男","name":"王五1","hobby":["java","php","golang"]},{"id":1002,"sex":"男","name":"王五2","hobby":["java","php","golang"]},{"id":1003,"sex":"男","name":"王五3","hobby":["java","php","golang"]},{"id":1004,"sex":"男","name":"王五4","hobby":["java","php","golang"]},{"id":1005,"sex":"男","name":"王五5","hobby":["java","php","golang"]},{"id":1006,"sex":"男","name":"王五6","hobby":["java","php","golang"]},{"id":1007,"sex":"男","name":"王五7","hobby":["java","php","golang"]},{"id":1008,"sex":"男","name":"王五8","hobby":["java","php","golang"]},{"id":1009,"sex":"男","name":"王五9","hobby":["java","php","golang"]}]}
`
	var cl Class
	err := json.Unmarshal([]byte(str), &cl)
	if err != nil {
		fmt.Println("序列化错误!")
	} else {
		fmt.Printf("%#v", cl)
	}
}

结果:

Go 复制代码
main.Class{No:"六年级一班", Student:[]main.Student{main.Student{ID:1000, Sex:"男", Name:"王五0", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1001, Sex:"男", Name:"王五1", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1002, Sex:"男", Name:"王五2", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1003, Sex:"男", Name:"王五3", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1004, Sex:"男", Name:"王五4", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1005, Sex:"男", Name:"王五5", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1006, Sex:"男", Name:"王五6", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1007, Sex:"男", Name:"王五7", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1008, Sex:"男", Name:"王五8", Hobby:[]string{"java", "php", "golang"}}, main.Student{ID:1009, Sex:"男", Name:"王五9", Hobby:[]string{"java", "php", "golang"}}}}
相关推荐
Pandaconda20 分钟前
【后端开发面试题】每日 3 题(十二)
数据库·后端·面试·负载均衡·高并发·后端开发·acid
木易:_/29 分钟前
【004】deepseek本地化部署后,python的调用方式_#py
开发语言·python
谭知曦31 分钟前
Scheme语言的压力测试
开发语言·后端·golang
uhakadotcom34 分钟前
实时计算Flink版:解锁数据处理新世界
后端·面试·github
uhakadotcom36 分钟前
Hologres实时数仓引擎:简化数据处理与分析
后端·面试·github
娱乐界祖师爷1 小时前
Qt中的 #include “xxxx.moc“ 说明
开发语言·qt
杜子腾dd1 小时前
16.使用读写包操作Excel文件:XlsxWriter 包
大数据·开发语言·python·excel·pandas
带娃的IT创业者1 小时前
Flask应用调试模式下外网访问的技巧
后端·python·flask
阮清漪1 小时前
Bash语言的智能家居
开发语言·后端·golang
十六ᵛᵃᵉ1 小时前
day04_Java高级
java·开发语言