Go结构体的基本使用详解

目录
  • 定义
  • 实例化
  • 匿名结构体
  • 空结构体
  • 构造函数
  • 方法与接收者
  • 匿名字段
  • 实现面向对象的“继承”特性
  • 标签tag
  • 结构体与JSON系列化

本文主要介绍Go的结构体类型的基本使用,快速上车

定义

结构体,是一种自定义的数据类型,由多个数据类型组合而成。用于描述一类事物相关属性

定义方式

type 类型名 struct {
    字段名 字段类型
    …
}

//示例:
type Animal struct {
  Name string
  Age  int
}

实例化

结构体和结构体指针,两者的实例化有所区别(注意:结构体指针必须手动初始化,分配内存地址)

提供多种写法,灵活使用:

//结构体实例化
//写法1
//var a Animal
//a.Name = "aaa"
//a.Age = 18
//写法2
a := Animal{
  Name: "dog",
  Age:  18,
}
fmt.Println(fmt.Sprintf("%T - %v", a, a)) //main.Animal - {dog 18}

//结构体指针实例化
//写法1
var b *Animal
b = new(Animal)
//写法2
//b := new(Animal)
//写法3
//b := &Animal{}
b.Name = "cat"                            //在底层是(*b).Name = "cat",这是Go语言帮我们实现的语法糖
fmt.Println(fmt.Sprintf("%T - %v", b, b)) //*main.Animal - &{cat 0}

匿名结构体

适用于临时数据存储的场景

var v struct {
  Name string
  Age  int
}
fmt.Println(v)

空结构体

不占用内存空间

var v struct{}
fmt.Println(unsafe.Sizeof(v)) //0

v1 := struct{}{}
fmt.Println(unsafe.Sizeof(v1)) //0

构造函数

Go没有自带的构造函数,采用自实现

  • 定义方式1:结构体不复杂,可以返回结构体类型,值拷贝性能开销小
func NewPerson(name string, age int8) Person {
  return Person{
    name: name,
    age:  age,
  }
}
  • 定义方式2:结构体复杂,得返回结构体指针类型,避免值拷贝产生的性能开销
func NewPerson(name string, age int8) *Person {
  return &Person{
    name: name,
    age:  age,
  }
}

方法与接收者

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

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

标准格式

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

接收者类型(两种):

  • 非指针类型:发生值拷贝产生副本,方法内修改字段,只在方法内生效;
  • 指针类型:不产生副本,方法内修改字段,同步生效;
func NewPerson(name string, age int8) *Person {
  return &Person{
    name: name,
    age:  age,
  }
}

func (p *Person) Dream() {
  p.name = "aaa"
  fmt.Printf("%s的梦想是学好Go语言\n", p.name)  //aaa的梦想是学好Go语言
}

func main() {
  p1 := NewPerson("小王子", 25)
  p1.Dream()
  fmt.Println(p1) //&{aaa 25}
}

什么时候使用指针类型的接收者

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

注意点

1.接收者类型,可以是任何类型,不仅仅只针对结构体类型。但要注意下,类型和方法定义得在同一个包下面

type MyInt int

func (i MyInt) SayInt() {
  fmt.Println("my type is MyInt")
}

func main() {
  var i1 MyInt
  i2 := MyInt(10)
  i1.SayInt()
  i2.SayInt()
}

输出结果:
my type is MyInt
my type is MyInt

匿名字段

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

但需要注意字段名冲突问题,所以不建议使用省略写法操作匿名字段

type User struct {
  Name    string
  Gender  string
  Address //匿名字段
}

type Address struct {
  Province   string
  City       string
  CreateTime string
}

func main() {
  var u1 User
  u1.Name = "张三"
  u1.Gender = "男"
  u1.Address.City = "北京" //匿名字段默认使用类型名作为字段名
  u1.CreateTime = "2019" //匿名字段可以省略,但注意多个匿名字段下有相同字段名,会编译失败,所以建议不采用省略写法
  fmt.Println(u1)
}

实现面向对象的“继承”特性

Go不是面向对象编程的语言,但可以通过嵌套结构体的方式,来实现面向对象的“继承”特性

type Animal struct {
  Name string
  Age  int
}

func (a Animal) Say() {
  fmt.Println(fmt.Sprintf("1-my name is %s and age is %d", a.Name, a.Age))
}

type Cat struct {
  Animal //嵌套结构体实现继承
}

func main() {
  c1 := Cat{}
  c1.Name = "加菲猫"
  c1.Age = 5
  c1.Say()

  //输出结果:
  //1-my name is 加菲猫 and age is 5
}

子类还可以重写父类的Say方法,并且还能拥有自己的Run方法

func (c Cat) Say() {
  fmt.Println(fmt.Sprintf("2-my name is %s and age is %d", c.Name, c.Age))
}

func (c Cat) Run() {
  fmt.Println(fmt.Sprintf("my name is %s,还是跑步高手", c.Name))
}

func main() {
  c1 := Cat{}
  c1.Name = "加菲猫"
  c1.Age = 5
  c1.Say()
  c1.Run()

  //输出结果:
  //2-my name is 加菲猫 and age is 5
  //my name is 加菲猫,还是跑步高手
}

标签tag

通过反射机制,识别结构体的标签,容错能力较差,需要注意使用

标准格式

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

使用注意事项

  • 外层使用 反引号 包起来,里边value需要使用 双引号 包起来;
  • KV之间使用冒号,多个KV之间使用空格(注意:冒号前后不要加其他符号)

结构体与JSON系列化

给结构体添加json标签,然后做json系列化操作:

  • 首字母大写字段(公开):会转换成json标签指定的字段名,若未指定,则使用自身字段名;
  • 首字小写字段(私有):不会输出,因为这类字段仅在定义当前结构体的包中可访问;

简单示例

type CardInfo struct {
  Title  string `json:"title"`
  Desc   string
  height int `json:"height"`
}

func main() {
  c1 := CardInfo{
    Title:  "成长之星",
    Desc:   "balabala",
    height: 100,
  }
  data, _ := json.Marshal(c1)
  fmt.Println(string(data)) //{"title":"成长之星","Desc":"balabala"}

  str := "{"title":"title111", "desc":"desc222", "height":20}"
  c2 := CardInfo{}
  _ = json.Unmarshal([]byte(str), &c2)
  fmt.Println(c2) //{title111 desc222 0}
}

到此这篇关于Go结构体的基本使用详解的文章就介绍到这了,更多相关Go结构体使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Golang自定义结构体转map的操作

    在Golang中,如何将一个结构体转成map? 本文介绍两种方法.第一种是是使用json包解析解码编码.第二种是使用反射,使用反射的效率比较高,代码在这里.如果觉得代码有用,可以给我的代码仓库一个star. 假设有下面的一个结构体 func newUser() User { name := "user" MyGithub := GithubPage{ URL: "https://github.com/liangyaopei", Star: 1, } NoDive :

  • Golang空结构体struct{}用途,你知道吗

    golang 空结构体 struct{} 可以用来节省内存 a := struct{}{} println(unsafe.Sizeof(a)) // Output: 0 理由如下: 如果使用的是map,而且map又很长,通常会节省不少资源 空struct{}也在向别人表明,这里并不需要一个值 本例说明在map里节省资源的用途: set := make(map[string]struct{}) for _, value := range []string{"apple", "o

  • Go语言中的匿名结构体用法实例

    本文实例讲述了Go语言中的匿名结构体用法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package main      import (     "fmt" )      func main() {     var user struct{Name string; Gender int}     user.Name = "dotcoo"     user.Gender = 1     fmt.Printf("%#v\n",

  • golang 结构体初始化时赋值格式介绍

    golang在给结构体赋值初始值时,用:分割k,v值 x := ItemLog{ Id: GetUuid(), ItemId: u.Id, UsrId: "123", Name: u.Name, Price: u.Price, Desc: u.Desc, Status: u.Status, DevArea: u.DevArea, } 补充:golang 结构体作为map的元素时,不能够直接赋值给结构体的某个字段 引入: 错误 Reports assignments directly t

  • 详解Go 结构体格式化输出

    在软件系统中定位问题时日志不可或缺,但是当一个系统功能繁多,需要打印的日志也多如牛毛,此时为了提高我们浏览日志的效率,便于阅读的输出格式必不可少. 打印结构体是打印日志时最长见的操作,但是当结构体内容较多都在一行时,不易于阅读.在 Go 中结构体可以方便的转为 JSON,因此我们可以借助 JSON 完成对 struct 的格式化输出. 打印在一行,使用 %+v 显示结构体字段名: package main import ( "fmt" ) // Student 学生信息 type St

  • Go语言结构体定义和使用方法

    本文实例讲述了Go语言结构体定义和使用方法.分享给大家供大家参考.具体分析如下: 一个结构体(struct)就是一个字段的集合. (而 type 定义跟其字面意思相符.) 复制代码 代码如下: package main import "fmt" type Vertex struct {     X int     Y int } func main() {     fmt.Println(Vertex{1, 2}) } 结构体字段使用点号来访问. 复制代码 代码如下: package

  • Go 结构体、数组、字典和 json 字符串的相互转换方法

    Go 语言中 encoding/json 包可以很方便的将结构体.数组.字典转换为 json 字符串. 引用 import "encoding/json" 解析语法 // v 传入结构体.数组等实例变量 // []byte 字节数组 // error 可能会有的错误 func Marshal(v interface{}) ([]byte, error) 反解析 // []byte 字节数组 // v 传入结构体.数组等实例变量的指针地址 // error 可能会有的错误 func Un

  • golang 实现两个结构体复制字段

    实际工作中可能会有这样的场景: 两个结构体(可能类型一样), 字段名和类型都一样, 想复制一个结构体的全部或者其中某几个字段的值到另一个(即merge操作), 自然想到可以用反射实现 package main import "fmt" import "reflect" // 用b的所有字段覆盖a的 // 如果fields不为空, 表示用b的特定字段覆盖a的 // a应该为结构体指针 func CopyFields(a interface{}, b interface

  • C++ 结构体初始化与赋值详解

    目录 1.结构体初始化 2.结构体赋值 参考文献 1.结构体初始化 结构体是常用的自定义构造类型,是一种很常见的数据打包方法.结构体对象的初始化有多种方式,分为顺序初始化.指定初始化.构造函数初始化.假如有如下结构体. struct A { int b; int c; }; (1)顺序初始化因为书写起来较为简约,是我们最常用的初始化方式,但相对于指定初始化,无法变更数据成员初始化顺序,灵活性较差,而且表现形式不直观,不能一眼看出 struct 各个数据成员的值. A a = {1, 2}; (2

  • go语言结构体指针操作示例详解

    目录 指针 go指针操作 不能操作不合法指向 new函数 指针做函数的参数 数组指针 结构体指针变量 结构体成员普通变量 结构体成员指针变量 结构体比较和赋值 结构体作为函数参数 指针 指针是代表某个内存地址的值.内存地址储存另一个变量的值. 指针(地址),一旦定义了不可改变,指针指向的值可以改变 go指针操作 1.默认值nil,没有NULL常量 2.操作符“&”取变量地址,“*“通过指针(地址)访问目标对象(指向值) 3.不支持指针运算,不支持“->”(箭头)运算符,直接用“.”访问目标成

  • Go语言学习之结构体和方法使用详解

    目录 1. 结构体别名定义 2. 工厂模式 3. Tag 原信息 4. 匿名字段 5. 方法 1. 结构体别名定义 变量别名定义 package main import "fmt" type integer int func main() { //类型别名定义 var i integer = 1000 fmt.Printf("值: %d, 类型: %T\n", i, i) var j int = 100 j = int(i) //j和i不属于同一类型,需要转换 fm

  • C语言程序中结构体的内存对齐详解

    目录 一.为什么存在内存对齐 二.结构体的内存对齐四规则 三.举例 一.为什么存在内存对齐 1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的:某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常. 2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐. 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问:而对齐的内存访问仅需要一次访问. 总的来说结构体的内存对齐是拿空间来换取时间的做法. 二.结构体的内存对齐四规则 默认情况:默认的对

  • Go结构体的基本使用详解

    目录 定义 实例化 匿名结构体 空结构体 构造函数 方法与接收者 匿名字段 实现面向对象的“继承”特性 标签tag 结构体与JSON系列化 本文主要介绍Go的结构体类型的基本使用,快速上车 定义 结构体,是一种自定义的数据类型,由多个数据类型组合而成.用于描述一类事物相关属性. 定义方式: type 类型名 struct { 字段名 字段类型 - } //示例: type Animal struct { Name string Age int } 实例化 结构体和结构体指针,两者的实例化有所区别

  • Golang打印复杂结构体两种方法详解

    目录 fmt结构体占位符 打印复杂结构体 方案一 方案二 fmt结构体占位符 在Golang中有原生的 fmt 格式化工具去打印结构体,可以通过占位符%v.%+v.%#v去实现,这3种的区别如下所示: type User struct { Name string Age int } func main() { user := User{ Name: "张三", Age: 95, } fmt.Printf("%v\n", user) fmt.Printf("

  • C语言 结构体(Struct)详解及示例代码

    前面的教程中我们讲解了数组(Array),它是一组具有相同类型的数据的集合.但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组为字符,成绩为小数,因为数据类型不同,显然不能用一个数组来存放. 在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据.结构体的定义形式为: struct 结构体名{     结构体所包含的变量或数组 }; 结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可

  • 结构体对齐的规则详解及C++代码验证

    目录 基本概念 结构体对齐的规则 程序验证 基本概念 CPU一次能读取多少个字节的数据主要是看数据总线是多少位的,16位CPU一次能读取2个字节,32位CPU一次能读取4个字节,64位CPU一次能读取8个字节.并且不能跨内存区间访问,这句话的意思可以理解为,如果CPU是32位的话,那么可以将整个内存区间每4个字节分为一块(BLOCK),每次读取一个BLOCK的数据. 那么对于下面这个结构体: struct st { char c; int i; }; 如果不进行对齐操作,char 的地址范围0x

  • C语言热门考点结构体与内存对齐详解

    目录 一.引例 1.结构体的第一个成员永远放在结构体起始位置偏移量为0的位置 2.从第二个成员开始,总是放在偏移量为一个对齐数的整数处,对齐数=编译器默认的对齐数和变量自身大小的较小值 3.结构体的总大小必须是各个成员的对齐数中最大的那个对齐数的整数倍 二.小试牛刀 三.嵌套结构体的特殊情况 四.关于为什么存在内存对齐 1.平台原因(移植原因): 2.性能原因: 总结 一.引例 到底什么是结构体内存对齐,我们用一段代码来介绍一下 struct S1 { char c1;//1字节 int a;/

  • C++中声明类的class与声明结构体的struct关键字详解

    class class 关键字声明类类型或定义类类型的对象. 语法 [template-spec] class [ms-decl-spec] [tag [: base-list ]] { member-list } [declarators]; [ class ] tag declarators; 参数 template-spec 可选模板说明. ms-decl-spec 可选存储类说明有关更多信息 tag 给定于类的类型名称.在类范围内的标记成为了保留字.标志是可选项.如果省略,定义匿名类.

随机推荐