【Go语言】Go语言结构体全面解析

news2024/9/25 9:18:49

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Go语言开发零基础到高阶实战
景天的主页:景天科技苑

在这里插入图片描述

文章目录

  • Go语言中的结构体
    • 一、结构体的基本概念
      • 1.1 结构体的定义
      • 1.2 结构体字段的访问
      • 1.3 初始化
        • 1.3.1 使用字面量初始化
        • 1.3.2 使用关键字初始化
    • 二、结构体的使用
      • 2.1 访问结构体成员
      • 2.2 结构体作为函数参数
      • 2.3 结构体切片
    • 三、结构体的进阶用法
      • 3.1 结构体的嵌套
      • 3.2 结构体方法
      • 3.3 结构体和JSON序列化
      • 3.4 结构体标签(Tag)
      • 3.5 匿名结构体
      • 3.6 匿名字段(字段提升)
      • 3.7 结构体指针
      • 3.8 结构体的零值
    • 四、结构体导出规则
    • 五、结构体的工厂模式
    • 六、实际案例:图书管理系统
      • 6.1 定义图书结构体
      • 6.2 创建图书列表
      • 6.3 实现图书管理系统功能
    • 七、总结

Go语言中的结构体

在Go语言中,结构体(Struct)是一个核心概念,它允许开发者将多个相关或不同类型的数据项组合成一个单一的复合类型。这种特性使得结构体成为Go语言中非常强大和灵活的数据组织方式,特别适用于表示复杂的数据结构和对象。
本文将结合实际案例,详细阐述Go语言中结构体的定义、使用、方法定义以及高级特性,如匿名结构体、嵌套结构体、结构体指针,结构体导出规则等。

一、结构体的基本概念

结构体是Go语言中的一种复合数据类型,可以视为一个字段的集合。这些字段可以是不同的数据类型,包括基本数据类型(如int、float64、string等)、指针、数组、切片、其他结构体等。结构体是用户自定义的类型,它们使得数据处理更加模块化,并增强了代码的可读性和可维护性。

1.1 结构体的定义

结构体的定义使用type关键字和struct关键字。定义结构体时,需要在{}中列出所有的字段及其类型。

type Person struct {
    Name string
    Age  int
    Email string
}

上面的代码定义了一个名为Person的结构体,它有三个字段:Name(字符串类型)、Age(整型)和Email(字符串类型)。

1.2 结构体字段的访问

一旦定义了结构体,就可以创建结构体的实例(即变量),并通过点(.)操作符访问或修改其字段。

func main() {
    var p Person
    p.Name = "Alice"
    p.Age = 30
    p.Email = "alice@example.com"

    fmt.Println(p.Name) // 输出: Alice
}

1.3 初始化

1.3.1 使用字面量初始化

结构体可以使用字面量进行初始化,即直接在声明变量时指定字段的值。

p1 := Person{
    Name: "Alice",
    Age: 30,
    Job: "Engineer",
    Salary: 6000,
}
1.3.2 使用关键字初始化

也可以指定字段名来初始化结构体,这样可以忽略字段的声明顺序。

p2 := Person{
    Name: "Bob",
    Age: 25,
    Job: "Designer",
    Salary: 5000,
}

// 或者只初始化部分字段
p3 := Person{
    Name: "Charlie",
    Age: 35,
}
p3.Job = "Artist"
p3.Salary = 4500

二、结构体的使用

2.1 访问结构体成员

通过结构体变量名和点(.)运算符访问结构体成员。

fmt.Println(p1.Name)  // 输出: Alice
fmt.Println(p2.Age)   // 输出: 25

2.2 结构体作为函数参数

结构体可以作为函数的参数传递,既可以传递值也可以传递指针。传递值会复制整个结构体,而传递指针则只会传递结构体的地址。

func printPerson(p Person) {
    fmt.Println("Name:", p.Name)
    fmt.Println("Age:", p.Age)
}

func printPersonPtr(p *Person) {
    fmt.Println("Name:", p.Name)
    p.Age++  // 修改指针指向的结构体中的字段
}

func main() {
    p := Person{Name: "David", Age: 40}
    printPerson(p)
    printPersonPtr(&p)
    fmt.Println("Age after function call:", p.Age)  // 输出: 41
}

2.3 结构体切片

结构体切片是一个包含多个结构体实例的切片,用于管理结构体的集合。

var people []Person
people = append(people, p1)
people = append(people, p2)

for _, person := range people {
    fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
}

三、结构体的进阶用法

3.1 结构体的嵌套

结构体可以嵌套使用,即一个结构体的字段可以是另一个结构体类型。这有助于构建更复杂的数据结构。

type Address struct {
    City    string
    Country string
}

type Person struct {
    Name    string
    Age     int
    Address Address
}

func main() {
    var p Person
    p.Name = "Bob"
    p.Age = 25
    p.Address.City = "New York"
    p.Address.Country = "USA"

    fmt.Println(p.Address.City) // 输出: New York
}

3.2 结构体方法

结构体方法是在结构体上定义的函数,用于实现对该结构体实例的操作。在Go语言中,通过接收者(receiver)实现这一功能。接收者可以是值接收者或指针接收者。

type Person struct {
    Name string
    Age  int
}

// 使用值接收者
func (p Person) Describe() string {
    return fmt.Sprintf("Name: %s, Age: %d", p.Name, p.Age)
}

// 使用指针接收者
func (p *Person) SetAge(age int) {
    p.Age = age
}

func main() {
    p := Person{Name: "Charlie", Age: 35}
    fmt.Println(p.Describe()) // 输出: Name: Charlie, Age: 35

    p.SetAge(40)
    fmt.Println(p.Describe()) // 输出: Name: Charlie, Age: 40
}

在上面的例子中,Describe方法是一个值接收者方法,它返回Person实例的描述信息。而SetAge方法是一个指针接收者方法,它允许我们修改Person实例的Age字段。

3.3 结构体和JSON序列化

在实际开发中,经常需要将结构体实例转换为JSON格式的字符串,以便于网络传输或数据存储。Go标准库中的encoding/json包提供了这样的功能。

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    p := Person{Name: "David", Age: 45}

    data, err := json.Marshal(p)
    if err != nil {
        fmt.Println("json encode failed:", err)
        return
    }

    fmt.Println(string(data)) // 输出: {"name":"David","age":45}
}

在上面的例子中,通过json:"name"json:"age"这样的标签(Tag),我们指定了结构体字段在JSON字符串中的键名。这些标签是Go语言结构体独有的特性,使得Go能够轻松地与其他语言进行JSON数据的交换。

3.4 结构体标签(Tag)

结构体标签是结构体字段后面的元信息,它由一对反引号`包裹,格式通常为键值对形式,键值之间用冒号:分隔,值被双引号"包围。结构体标签主要用于为结构体字段提供额外的信息,这些信息在运行时可以通过反射机制读取。

除了上述的JSON序列化场景,结构体标签还可以用于数据库ORM映射、XML序列化等场景。

type Product struct {
    ID    int    `db:"id"`       // 数据库字段映射
    Name  string `json:"name"`   // JSON序列化
    Price float64 `xml:"price"`  // XML序列化
}

3.5 匿名结构体

匿名结构体是没有名称的结构体,通常用于一次性使用的情况。

hobby := struct {
    HobbyId   int
    HobbyName string
}{
    HobbyId:   1,
    HobbyName: "Basketball",
}
fmt.Println(hobby)
fmt.Println(hobby.HobbyName)  // 输出: Basketball

3.6 匿名字段(字段提升)

当结构体中嵌入一个匿名结构体时,匿名结构体的字段会被提升,可以直接通过外部结构体访问。
结构体中的匿名字段,没有名字的字段,这个时候属性类型不能重复。
如何打印这个匿名字段,默认使用数据类型当做字段名称。

// Teacher 结构体中的匿名字段,没有名字的字段,这个时候属性类型不能重复
type Teacher struct {
	string
	int
}

// 匿名字段
t1 := Teacher{"dashan", 18}
fmt.Println(t1)
// 如何打印这个匿名字段,默认使用数据类型当做字段名称
fmt.Println(t1.string)  // dashan
fmt.Println(t1.int)   // 18

3.7 结构体指针

结构体指针是指向结构体变量的指针,通过&操作符获取结构体的地址。

package main

import "fmt"

// User2 定一个结构体 type User struct
type User2 struct {
	name string
	age  int
	sex  string
}

func main() {

	// 结构体类型    包.struct名
	user1 := User2{"dajiang", 18, "男"}
	fmt.Println(user1)
	fmt.Printf("%T,%p\n", user1, &user1) // main.User2,0xc00007e4b0

	// 结构体是值类型的,赋值后,重新开辟内存空间
	user2 := user1
	fmt.Println(user2)
	fmt.Printf("%T,%p\n", user2, &user2) // main.User2,0xc00007e540

	user2.name = "tywin"
	fmt.Println(user1)
	fmt.Println(user2)
	fmt.Println("========================")

	// 指针解决值传递的问题
	var user_ptr *User2
	user_ptr = &user1

	// *user_ptr 等价于 user1
	fmt.Println(*user_ptr)
	(*user_ptr).name = "jingtian"
	fmt.Println(user1)
	// 语法糖
	user_ptr.name = "jingtian222222222"
	fmt.Println(user1)

	// 内置函数 new 创建对象。  new 关键字创建的对象,都返回指针,而不是结构体对象。
	// func new(Type) *Type
	// 通过这种方式创建的结构体对象更加灵活,突破了结构体是值类型的限制。
	user3 := new(User2)
	fmt.Println(user3)

	//初始化user3
	(*user3).name = "小红"
	user3.sex = "女"
	user3.age = 18
	fmt.Println(user3)

	updateUser(user3)
	fmt.Println(user3)
}

// 传递User2类型指针
func updateUser(user *User2) {
	//(*user).age = 100
	//语法糖写法
	user.age = 100
}

在这里插入图片描述

3.8 结构体的零值

当结构体变量被声明但未初始化时,它的字段会被设置为对应类型的零值。

var p Person
fmt.Println(p)  // 输出: { 0}

四、结构体导出规则

在其他语言中,有public 公开的,所有地方都可以使用 、priavte私有的,只能自己使用。

结构体:结构体名字,属性名字 。首大写字母,可以导出使用,首字母小写,不能导出使用。

如果结构体名称首字母小写,则结构体不会被导出。这时,即使结构体成员字段名首字母大写,也不会被导出。

如果结构体名称首字母大写,则结构体可被导出,但只会导出大写首字母的成员字段,那些小写首字母的成员字段不会被导出。

如果存在嵌套结构体,即使嵌套在内层的结构体名称首字母小写,外部也能访问到其中首字母大写的成员字段。

我们建个base包,里面有个pojo包,里面有个User.go
在这里插入图片描述

我们创建个main.go。可以看到money不可以被导出

package main

import (
	"fmt"
	"jingtian/yufa/结构体/base/pojo"
)

func main() {
	user := pojo.User{
		Name: "jingtian",
		Age:  18,
		//money : 900

	}

	fmt.Println(user)
}

在这里插入图片描述
打印user,可以打印能导出的字段
在这里插入图片描述
money存在,但不可以被访问和修改
在这里插入图片描述

五、结构体的工厂模式

在Go语言中,由于没有传统的构造函数,我们常常使用工厂模式来创建结构体的实例。工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。

type Person struct {
    Name string
    Age  int
}

// 工厂函数
func NewPerson(name string, age int) *Person {
    return &Person{
        Name: name,
        Age:  age,
    }
}

func main() {
    p := NewPerson("Eve", 28)
    fmt.Println(p.Name) // 输出: Eve
}

在这个例子中,NewPerson函数就是一个工厂函数,它接受参数并返回一个指向Person结构体的指针。

六、实际案例:图书管理系统

下面我们将通过一个实际的图书管理系统案例,来演示Go语言中结构体的应用。

6.1 定义图书结构体

首先,我们定义一个Book结构体来表示图书的信息。

type Book struct {
    ID        string `json:"id"`
    Title     string `json:"title"`
    Author    string `json:"author"`
    Published string `json:"published"`
    ISBN      string `json:"isbn"`
}

6.2 创建图书列表

接下来,我们可以创建一个包含多个Book结构体的切片来表示图书列表。

var books []Book

func initBooks() {
    books = append(books, Book{
        ID:        "001",
        Title:     "Go语言编程之旅",
        Author:    "Alice",
        Published: "2023-01-01",
        ISBN:      "978-7-121-34567-8",
    })
    // ... 可以继续添加其他图书
}

func main() {
    initBooks()
    // 对books进行操作,如显示图书列表等
}

6.3 实现图书管理系统功能

为了演示,我们可以添加一个功能来遍历图书列表并打印每本书的详细信息。

func printBooks() {
    for _, book := range books {
        fmt.Printf("ID: %s, Title: %s, Author: %s, Published: %s, ISBN: %s\n",
            book.ID, book.Title, book.Author, book.Published, book.ISBN)
    }
}

func main() {
    initBooks()
    printBooks()
}

七、总结

结构体是Go语言中非常重要和强大的数据组织方式,通过结构体可以方便地表示复杂的数据结构和对象。本文详细介绍了结构体的定义、使用、方法定义以及高级特性,如匿名结构体、嵌套结构体、结构体指针,结构体导出规则等。通过实际案例的演示,希望能够让读者对Go语言中的结构体有更深入的理解和掌握。结构体不仅提高了代码的可读性和可维护性,还使得数据组织更加清晰和易于管理。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于 Canvas 的可缩放拖动网格示例(Vue3以及TypeScript )

文章目录 1. 基本知识2. Vue33. TypeScript 1. 基本知识 基本知识讲解: Canvas API: 一种用于在网页上绘制图形的 HTML 元素,使用 JavaScript 的 Canvas API 来进行绘制 使用 getContext(2d) 方法获取 2D 绘图上下文,允许开发者绘…

MySQL数据库备份详解

文章目录 引言● 数据库备份的重要性 MySQL数据库备份的基础知识● 备份类型1、完全备份2、增量备份3、差异备份 ● 备份工具与方法1、逻辑备份工具——mysqldump2、物理备份工具——Xtrabackup3、第三方解决方案 MySQL数据库备份的实施步骤1、环境准备2、选择合适的备份工具与…

【Linux基础IO】深入解析Linux基础IO缓冲区机制:提升文件操作效率的关键

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:Linux “ 登神长阶 ” 🤡往期回顾🤡:暂无 🌹🌹期待您的关注 🌹🌹 ❀Linux基础IO 📒1. 什么是缓…

14.第二阶段x86游戏实战2-C++语言开发环境搭建-VisualStudio2017

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 本人写的内容纯属胡编乱造,全都是合成造假,仅仅只是为了娱乐,请不要…

基于jsonpath_ng的JSON数据查改增删

jsonpath_ng支持JSON数据的读写操作。 安装 pip install jsonpath-ng测试数据 from jsonpath_ng import parse import jsonjson_data { "store": {"book": [ { "category": "reference","author": "Nigel Rees&qu…

数据集-目标检测系列-鲨鱼检测数据集 shark >> DataBall

数据集-目标检测系列-鲨鱼检测数据集 shark >> DataBall 数据集-目标检测系列-鲨鱼检测数据集 shark 数据量:6k 想要进一步了解,请联系。 DataBall 助力快速掌握数据集的信息和使用方式,百种数据集,持续增加中。 示例&…

【自动驾驶】基于车辆几何模型的横向控制算法 | Stanley 算法详解与编程实现

写在前面: 🌟 欢迎光临 清流君 的博客小天地,这里是我分享技术与心得的温馨角落。📝 个人主页:清流君_CSDN博客,期待与您一同探索 移动机器人 领域的无限可能。 🔍 本文系 清流君 原创之作&…

【Python】探索 Elpy:Emacs 中的 Python 开发环境

可以短时间不开心,但别长时间不清醒。 对于使用 Emacs 编辑器的 Python 开发者来说,Elpy 是一个强大的集成开发环境(IDE),它通过整合多个 Emacs Lisp 和 Python 包,提供了一套完整的 Python 编程支持。本文…

丹摩智算平台体验:AI开发从未如此简单

目录 初次接触丹摩智算GPU算力资源表格 轻松创建GPU实例镜像选择 实验过程体验实验中的一些细节 使用后的感受一点小建议总结 最近我一直在学习一些与深度学习相关的知识,准备自己动手做一些模型训练的实验。平时在自己电脑上跑模型总感觉有点吃力,特别是…

复制他人 CSDN 文章到自己的博客

文章目录 0.前言步骤 0.前言 在复制别人文章发布时,记得表明转载哦 步骤 在需要复制的csdn 文章页面,打开浏览器开发者工具(F12)Ctrl F 查找"article_content"标签头 右键“Copy”->“Copy element”新建一个 tx…

[大语言模型-论文精读] ACL2024-长尾知识在检索增强型大型语言模型中的作用

ACL2024-长尾知识在检索增强型大型语言模型中的作用 On the Role of Long-tail Knowledge in Retrieval Augmented Large Language Models Authors: Dongyang Li, Junbing Yan, Taolin Zhang, Chengyu Wang, Xiaofeng He, Longtao Huang, Hui Xue, Jun Huang 1.概览 问题解决&…

Windows驱动调试方法

单步调试驱动 驱动的调试不能直接在本机上进行,而是要放在虚拟机(或其它设备)中。这是因为在内核模式下,一个断点的触发将会停下整个系统而不只是单个进程。 在前面的文章里,使用了DbgPrint函数来进行日志的输出&…

学习一下怎么用git

目录 初始化操作 设置名字: 设置邮箱: 查询状态 初始化本地仓库 清空git bush控制台 git的三个区域 文件提交 将会文件提交到暂存区 暂存指定文件 暂存所有改动文件 查看暂存区里面的文件 将文件提交到版本库 git文件状态查看 ​编辑 暂存区的相关指令…

2024专业U盘格式化数据恢复工具推荐榜

对于经常在不固定办公地点工作,且依赖U盘存储重要资料的朋友来说,U盘无疑成为了随身携带的“数字保险箱”。然而,频繁使用U盘也意味着面临数据丢失的风险。面对u盘格式化怎么恢复数据的难题,您或许正寻求有效的解决方案。接下来&a…

Spring源码-从源码层面讲解传播特性

传播特性:service&#xff1a;REQUIRED&#xff0c;dao:REQUIRED 两个都是required使用的是同一个事务&#xff0c;正常情况&#xff0c;在service提交commit <tx:advice id"myAdvice" transaction-manager"transactionManager"><tx:attributes&…

2024年CSP-J认证 CCF信息学奥赛C++ 中小学初级组 第一轮真题-阅读程序题解析

2024 CCF认证第一轮&#xff08;CSP-J&#xff09;真题 二、阅读程序题 (程序输入不超过数组或字符串定义的范围&#xff0c;判断题正确填√错误填X;除特殊说明外&#xff0c;判断题 1.5分&#xff0c;选择题3分&#xff0c;共计40 分) 第一题 01 #include <iostream>…

【Python】探索 PyTorch 在机器学习中的应用

在机器学习的浪潮中&#xff0c;PyTorch 以其灵活性和易用性成为了研究者和开发者的热门选择。本文将探讨 PyTorch 在机器学习领域的应用&#xff0c;并展示如何使用 PyTorch 构建一个基本的机器学习模型。 ⭕️宇宙起点 &#x1f3ac; 机器学习简介&#x1f4a2; PyTorch 与机…

胤娲科技:AI程序员——重塑编程世界的魔法师

当魔法遇上代码 想象一下&#xff0c;一个对编程一无所知的初中生&#xff0c;在熙熙攘攘的展会现场&#xff0c;仅凭几句简单的提示词&#xff0c;就在几分钟内创造出一个功能完备的倒计时网页。 这听起来像是科幻电影中的场景&#xff0c;但如今&#xff0c;在阿里云「通义灵…

心觉:如何重塑高效学习的潜意识(1)两种方法的优缺点

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作180/1000天 你的学习习惯是什么呢 学习的时候是感到轻松吗 很多人感觉现在是知识大爆炸的时代&#xff0c;每天都会产生海量的知…

C++学习笔记----8、掌握类与对象(一)---- 对象中的动态内存分配(2)

2.2、用析构函数释放内存 每当完成动态分配内存时&#xff0c;都应该释放。如果在一个对象中动态分配内存&#xff0c;释放内存的地方就是析构函数。编译器保证当对象被破坏时会调用析构函数。下面就是Spreadsheet类定义中的析构函数&#xff1a; export class Spreadsheet { …