Go语言结构体
1.struct的定义
- 1.Go中面向对象是通过
struct来实现的,struct是用户自定义类型
type User struct{
Username string
Sex string
Age int
AvatarUrl string
}
2.struct的初始化
// 方式一:
var user User
user.Username = "joke"
user.Sex = "M"
user.Age = 21
user.AvatarUrl = "https://www.person/1/profile/avat.png"
// 方式二:
user := User {
Username: "lucy",
Age: 17,
Sex: "M",
// AvatarUrl: "https://www.person/15/profile/avat.png",
}
3.定义的同时初始化
- 第一种赋值方式(结构体被调用时,所有成员会进行复制,
不推荐使用)
var user User = User {
Username: "tony",
Age: 29,
Sex: "F",
AvatarUrl: "https://www.person/27/profile/avat.png",
}
fmt.Println(user) // {tony F 29 https://www.person/27/profile/avat.png}
- 第二种赋值方式
var user *User = &User{}
(*user).Username = "haili"
// 编译器会自动将其转化为(*user).Age
user.Age = 35
fmt.Printf("%#v", *user) // main.User{Username:"haili", Sex:"", Age:35, AvatarUrl:""}
var user *User = &User {
Username: "tony",
Age: 29,
Sex: "F",
AvatarUrl: "https://www.person/27/profile/avat.png",
}
fmt.Println(*user) // {tony F 29 https://www.person/27/profile/avat.png}
fmt.Println(user.Age) // 29
- 第三种赋值方式(new返回的也是一个内存地址)
// new(User) 相当于 &User{}
var user *User = new(User)
user.Age = 32
user.Username = "tom"
user.Sex = "F"
user.AvatarUrl = "https://www.person/4/profile/avat.png"
fmt.Printf("%#v", *user) // main.User{Username:"tom", Sex:"F", Age:32, AvatarUrl:"https://www.person/4/profile/avat.png"}
4.行为(方法)的定义
- 第一种:值拷贝:实例对应的方法被调用时,实例成员会被进行复制(
不推荐使用)
package main
import (
"fmt"
"unsafe"
)
type User struct{
Username string
Sex string
Age int
AvatarUrl string
}
func (u User) UserInfo() string {
// 查看传递进来的结构体的Username的地址
fmt.Printf("Address is %x\n", unsafe.Pointer(&u.Username)) // c00001a100
// 相当于(*u).Username,编译器帮我们做了这些工作
return fmt.Sprintf("Name:%s-Age:%d-Sex:%s", u.Username, u.Age, u.Sex)
}
func main() {
// user := &User{"lucy", "M", 17, "https://www.person/15/profile/avat.png"} // 这样也是支持的
user := User{"lucy", "M", 17, "https://www.person/15/profile/avat.png"}
fmt.Printf("Address is %x\n", unsafe.Pointer(&user.Username)) // c00001a0c0
fmt.Println(user.UserInfo()) // Name:lucy-Age:17-Sex:M
}
package main
import (
"fmt"
"unsafe"
)
type Stu struct {
Name string
Age int
}
func (stu Stu) Set(name string, age int) {
// go会为此处的stu新开辟一块内存地址,和调用处的stu不是一个东西
fmt.Printf("Address is %x\n", unsafe.Pointer(&stu.Age)) // c000004510
stu.Name = name
stu.Age = age
}
func main() {
stu := Stu{
Name: "lucy",
Age: 22,
}
fmt.Println(stu) // {lucy 22}
fmt.Printf("Address is %x\n", unsafe.Pointer(&stu.Age)) // c0000044b0
stu.Set("jack", 25)
fmt.Println(stu) // {lucy 22}
}
- 第二种:引用拷贝:避免内存拷贝(推荐使用)
package main
import (
"fmt"
"unsafe"
)
type User struct{
Username string
Sex string
Age int
AvatarUrl string
}
func (u *User) UserInfo() string {
// 查看传递进来的结构体的Username的地址
fmt.Printf("Address is %x\n", unsafe.Pointer(&u.Username)) // c00001a0c0
// 相当于(*u).Username,编译器帮我们做了这些工作
return fmt.Sprintf("Name:%s-Age:%d-Sex:%s", u.Username, u.Age, u.Sex)
}
func main() {
// user := &User{"lucy", "M", 17, "https://www.person/15/profile/avat.png"} // 这样也是支持的
user := &User{"lucy", "M", 17, "https://www.person/15/profile/avat.png"}
fmt.Printf("Address is %x\n", unsafe.Pointer(&user.Username)) // c00001a0c0
fmt.Println(user.UserInfo()) // Name:lucy-Age:17-Sex:M
}
package main
import (
"fmt"
"unsafe"
)
type Stu struct {
Name string
Age int
}
func (stu *Stu) Set(name string, age int) {
fmt.Printf("Address is %x\n", unsafe.Pointer(&stu.Age)) // c0000044b0
stu.Name = name
stu.Age = age
}
func main() {
stu := &Stu{
Name: "lucy",
Age: 22,
}
// stu := Stu{ 这样也可以,主要看方法
// Name: "lucy",
// Age: 22,
}
fmt.Println(*stu) // {lucy 22}
fmt.Printf("Address is %x\n", unsafe.Pointer(&stu.Age)) // c0000044b0
stu.Set("jack", 25) // 修改成功
fmt.Println(*stu) // {jack 25}
}
5.struct的内存布局
- 结构体的内存布局:占用一段连续的内存空间
package main
import "fmt"
type Test struct{
A int32 // 4字节
B int32
C int32
D int32
}
func main() {
var t Test
fmt.Printf("%p\n",&t) // 0xc0000100d0
fmt.Printf("%p\n", &t.A) // 0xc0000100d0
fmt.Printf("%p\n", &t.B) // 0xc0000100d4
fmt.Printf("%p\n", &t.C) // 0xc0000100d8
fmt.Printf("%p\n", &t.D) // 0xc0000100dc
}
6.构造函数
package main
import "fmt"
type User struct{
Username string
Sex string
Age int
AvatarUrl string
}
func NewUser(username, sex, AvatarUrl string, age int) *User {
user := &User{
Username: username,
Sex: sex,
Age: age,
AvatarUrl: AvatarUrl,
}
return user
}
func main() {
// 使用构造函数生成一个
user := NewUser("jack", "M", "https://www.person/14/profile/avat.png", 23)
fmt.Println(*user) // {jack M 23 https://www.person/14/profile/avat.png}
}
7.struct嵌套
- 1.值类型嵌套
type Book struct {
Name string
Price float64
author Author
}
type Author struct {
Name string
Age int
}
func main() {
book := &Book{
Name: "天龙八部",
Price: 39.8,
author: struct {
Name string
Age int
}{Name: "金庸", Age: 86},
/* 也可以
author: Author{
Name: "金庸",
Age: 86,
},
*/
}
fmt.Println(*book) // {天龙八部 39.8 {金庸 86}}
fmt.Println((*book).author.Name) // 金庸
}
- 2.指针类型嵌套
type Book struct {
Name string
Price float64
author *Author
}
type Author struct {
Name string
Age int
}
func main() {
book := &Book{
Name: "天龙八部",
Price: 39.8,
author: *Author{
Name: "金庸",
Age: 86,
},
}
fmt.Println(*book) // {天龙八部 39.8 0xc0000044a0}
fmt.Println((*book).author.Name) // 金庸
fmt.Println((*book.author).Name) // 金庸
}
8.匿名字段
- 1.匿名字段的定义和初始化
type User1 struct{
Username string
Sex string
int
string
}
func main() {
var user &User1
user.Username = "lucy"
user.Sex = "M"
// 通过类型访问
user.int = 22
user.string = "https://www.person/5/profile/avat.png"
fmt.Println(*user) // {lucy M 22 https://www.person/5/profile/avat.png}
}
- 2.匿名嵌套
type Book struct {
Name string
Price float64
*Author
}
type Author struct {
Name string
Age int
}
func main() {
book := &Book{
Name: "天龙八部",
Price: 39.8,
Author: &Author{
Name: "金庸",
Age: 86,
},
}
/*
book := &Book{}
book.Name = "天龙八部"
book.Price = 39.8
book.Author = &Author{
Name: "金庸",
Age: 86,
}
// 也可以使用下面方式初始化Author对象
// book.Author = new(Author)
// book.Age = 86 // 在book中未发现Age属性,则会去引用对象Author中寻找
// book.Author.Name = "金庸" // Name名称冲突,所以需要指定Author对象
*/
fmt.Println(*book) // {天龙八部 39.8 0xc0000044a0}
fmt.Println((*book.Author).Name) // 金庸
fmt.Println((*book).Author.Name) // 金庸
}
- 3.匿名字段冲突解决
![]()
user := &User{}
user.Name = "lucy"
user.Age = "22"
user.Address = new(Address)
user.CreateTime = "2022-09-21" // User下没有,然后去Address中寻找,最终赋值给Address对象中的CreateTime字段

user := &User{}
user.Name = "lucy"
user.Age = 22
user.Address = new(Address)
user.CreateTime = "2022-09-21" // User有,直接初始化User对象中的该字段
// 如果想对Address中的CreateTime赋值,则必须指明对象为Address
user.Address.CreateTime = "2022-09-20"

user := &User{}
user.Name = "lucy"
user.Age = 22
user.Address = new(Address)
user.Email = new(Email)
user.CreateTime = "2022-09-21" // 直接报错,User对象中没有CreateTime,Address和Email中都存在该字段,无法确定要对哪个对象中的CreateTime进行赋值。此时需要明确指定赋值对象
// 如果想对Address中的CreateTime赋值,则必须指明对象为Address
user.Address.CreateTime = "2022-09-20"
// 如果想对Email中的CreateTime赋值,则必须指明对象为Email
user.Email.CreateTime = "2022-09-19"

user := &User{}
user.Name = "lucy"
user.Age = 22
user.Address = new(Address)
user.Email = new(Email)
// User有,直接初始化User对象中的该字段
user.CreateTime = "2022-09-21"
// 如果想对Address中的CreateTime赋值,则必须指明对象为Address
user.Address.CreateTime = "2022-09-20"
// 如果想对Email中的CreateTime赋值,则必须指明对象为Email
user.Email.CreateTime = "2022-09-19"
9.继承
- go语言中的继承是通过
结构体sturct中的匿名字段嵌套实现的 - 嵌套多个struct时(类似于python中的多继承),如果嵌套对象都实现了同名方法,则需要显示指明调用哪个对象的方法(没有python中的MRO)
package main
import "fmt"
type Animal struct {
Color string
Name string
}
func (animal *Animal) Eat(foods string) {
fmt.Printf("%s的%s在吃%s\n", animal.Color, animal.Name, foods)
}
func (animal *Animal) Sing(song string) {
fmt.Printf("%s的%s在唱%s\n", animal.Color, animal.Name, song)
}
func (animal *Animal) Playing(toy string) {
fmt.Printf("%s的%s在玩%s\n", animal.Color, animal.Name, toy)
}
type Dog struct {
*Animal
}
func (dog *Dog) Playing(toy string) {
// dog本身的方法
fmt.Printf("%s的%s在自己家玩%s\n", dog.Color, dog.Name, toy)
}
type Cat struct {
*Animal
}
func main() {
dog:=&Dog{}
dog.Animal = new(Animal)
// 以下属性和方法都是从Animal继承而来
dog.Color = "棕色"
dog.Name = "小狗tony"
dog.Eat("骨头") // 棕色的小狗tony在吃骨头
dog.Playing("飞盘") // 棕色的小狗tony在自己家玩飞盘
dog.Animal.Playing("飞盘") // 棕色的小狗tony在玩飞盘
dog.Sing("生日快乐") // 棕色的小狗tony在唱生日快乐
}
10.struct私有属性
- 字段可见性:结构体内字段明如果以
小写开头,则表示私有,外部包不可见
1.定义一个包文件夹
structure,内部文件struct.go内容如下
// structure/struct.go
package structure
type User struct{
Username string
Sex string
age int
}
2.
structure同级创建一个main.go文件,内容如下
package main
import (
// 包重命名 类似与python导包中的as import pandas as pd
userStruct "dcpuffer/dcpuffer/structure"
"fmt"
)
func main() {
user := &userStruct.User{}
user.Username = "lucy"
user.Sex = "lucy"
user.age = 22 // 私有属性age不允许外部访问,此处会报错
fmt.Println(user)
}
11.tag应用
tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来
![]()
package main
import (
"encoding/json"
"fmt"
)
// 如果结构体中字段为小写,则json后为空,因为对于json包,小写字段为私有属性,无法访问
type User struct {
UserName string `json:"username"` // 重点强调 :两侧不能有空格
Sex string `json:"sex"`
Score float64
}
func main() {
user := &User{
UserName: "user01",
Sex: "F",
Score: 87.6,
}
fmt.Printf("%#v\n", *user) // main.User{UserName:"user01", Sex:"F", Score:87.6}
data, err := json.Marshal(user)
if err == nil {
fmt.Println(string(data)) // {"username":"user01","sex":"F","Score":87.6}
} else {
fmt.Println(err)
}
}
12.结构体于json序列化
- 序列化:结构体转成json数据格式
package main
import (
"encoding/json"
"fmt"
)
type Class struct {
Name string
Count int
Stu []*Stu
}
type Stu struct {
Name string
Age int
Sex int
}
func main() {
c := &Class{
Name: "一班",
Count: 0,
Stu: nil,
}
for i := 0; i < 10; i++ {
// 生成10个学生信息
stu := &Stu{
//Name: "stu" + string(i),
Name: fmt.Sprintf("stu%d", i),
Age: 20,
Sex: 0,
}
c.Stu = append(c.Stu, stu)
}
c.Count = 10
fmt.Println(*c) // {一班 10 [0xc000064440 0xc000064460 ....]}
data, err := json.Marshal(c)
if err != nil {
fmt.Println("json serialize failed")
} else {
fmt.Println(string(data)) // json串 {"Name":"一班","Count":10,"Stu":[{"Name":"stu0","Age":20,"Sex":0}, ...]}
}
}
- 反序列化:json转成结构体格式数据
var rawJson = `{"Name":"一班","Count":10,"Stu":[{"Name":"stu0","Age":20,"Sex":0}, ...]}`
c1 := &Class{}
err = json.Unmarshal([]byte(rawJson), c1)
if err != nil {
fmt.Println("json unserialize failed")
} else {
fmt.Println(*c1) // {一班 10 [0xc000064440 0xc000064460 ....]}
fmt.Println((*c1).Stu[0].Name) // stu0
}
13.练习
- 实现一个简单的学生管理系统,每个学生有分数、年级、性别、名字等字段,用户可以在控制台添加学生,修改学生信息,打印学生列表的功能
stumanger\struct.go
package main
import "fmt"
// 定义学生对象结构体
type Student struct {
Username string
Score float64
Grade string
Sex int
}
// 定义一个结构体类型的map,存储学生信息,键为学生姓名
var allStudents = make(map[string]*Student, 10)
// 为结构体添加AddOrUpdate方法
func (st *Student) AddOrUpdate() {
stu, ok := allStudents[st.Username]
if ok {
// 更新
stu.Score = st.Score
stu.Sex = st.Sex
stu.Grade = st.Grade
fmt.Printf("学生[%s]信息修改成功!\n", st.Username)
} else {
// 新增
allStudents[st.Username] = st
fmt.Printf("学生[%s]信息添加成功!\n", st.Username)
}
return
}
stumanger\amin.go
package main
import "fmt"
func showMenu() {
fmt.Println("-----------------学生信息管理系统-----------------")
fmt.Println("| 1.新增修改学生信息 |")
fmt.Println("| 2.删除学生信息 |")
fmt.Println("| 3.查看学生信息 |")
fmt.Println("| 4.查看学生列表 |")
fmt.Println("------------------------------------------------")
fmt.Print(">>>")
}
func aUStudent() {
var (
username string
sex int
grade string
score float64
)
fmt.Print("请输入姓名:")
fmt.Scanf("%s\r", &username)
fmt.Print("请输入性别(0代表女,1代表男):")
fmt.Scanf("%d\r", &sex)
fmt.Print("请输入年级:")
fmt.Scanf("%s\r", &grade)
fmt.Print("请输入分数:")
fmt.Scanf("%f\r", &score)
stu := &Student{username, score, grade, sex}
stu.AddOrUpdate()
}
func getStudent(name string) {
stu, ok := allStudents[name]
if ok {
fmt.Println("****************************")
fmt.Println("* 姓名 性别 年级 分数 *")
fmt.Printf("* %s %d %s %.2f *\n", stu.Username, stu.Sex, stu.Grade,stu.Score)
fmt.Println("****************************")
} else {
fmt.Printf("学生[%s]信息不存在\n", name)
}
}
func listStudents() []Student {
var students []Student
for _, stus := range allStudents{
students = append(students, *stus)
}
return students
}
func deleteStudent(name string) {
_, ok := allStudents[name]
if ok {
delete(allStudents, name)
fmt.Printf("学生[%s]信息删除成功", name)
} else {
fmt.Printf("学生[%s]信息不存在\n", name)
}
}
func main() {
for {
showMenu()
var sel int
var username string
fmt.Scanf("%d\n", &sel)
switch sel {
case 1:
aUStudent()
case 2:
fmt.Print("请输入学生姓名:")
fmt.Scanf("%s\r", &username)
deleteStudent(username)
case 3:
fmt.Print("请输入学生姓名:")
fmt.Scanf("%s\r", &username)
getStudent(username)
case 4:
fmt.Println(listStudents())
}
fmt.Println(">>>")
}
}




浙公网安备 33010602011771号