Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(4)

简介: Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】

Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(3)https://developer.aliyun.com/article/1534260

5.7.2、指针类型接收者

       指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。

type person struct{
    name string
    age int8
    city string
}
 
// 构造器
func newPerson(name,city string,age int8) *person{
    return &person{
        name : name,
        city : city,
        age : age,
    }
}
 
// person 的方法
func (p person) eat(){
    fmt.Println("人会吃饭")
}
 
// 指针类型的接收者
func (p *person) setName(name string){
    p.name = name
}
 
func main(){
    p := newPerson("张三","北京",22)
    p.eat() // 人会吃饭
    p.setName("李四")
    fmt.Printf("%#v\n", p) // &main.person{name:"李四", age:22, city:"北京"}
}

注意:结构体是值类型的,并不是引用类型!所以当方法的接收者为指针类型时,才能真正操作结构体实例,否则就相当于拷贝了一份,在方法里自娱自乐。

5.7.3、指针类型接收者

这就是自娱自乐的场景,如果希望使用方法修改结构体实例的属性,那么一定不能使用这种方法。

type person struct{
    name string
    age int8
    city string
}
 
// 构造器
func newPerson(name,city string,age int8) *person{
    return &person{
        name : name,
        city : city,
        age : age,
    }
}
 
// person 的方法
func (p person) eat(){
    fmt.Println("人会吃饭")
}
 
// 非指针类型的接收者
func (p person) setName(name string){
    p.name = name
}
 
func main(){
    p := newPerson("张三","北京",22)
    p.eat() // 人会吃饭
    p.setName("李四")
    fmt.Printf("%#v\n", p) // &main.person{name:"张三", age:22, city:"北京"}
}
 

5.8、嵌套结构体

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

type Person struct{
    name string
    age int8
    addr Address
}
 
type Address struct{
    province string
    city string
}
 
func main(){
    p := Person{
        name :"张三",
        age : 22,
        addr : Address{
            province: "山西省",
            city : "晋中",
        },
    }
    fmt.Printf("%#v\n", p) // main.Person{name:"张三", age:22, addr:main.Address{province:"山西省", city:"晋中"}}
}

5.9、结构体的继承

type Animal struct{
    category string
}
 
func (a *Animal) move(){
    fmt.Printf("%s会移动",a.category)
}
 
type Dog struct{
    feet int8
    *Animal
}
 
func main(){
    dog := Dog{
        feet : 4,
        Animal: &Animal{
            category : "狗",
        },
    }
    dog.move() // 狗会移动
}

5.10、结构体字段的可见性

结构体中字段大写开头表示可公开访问,小写表示私有。

5.11、结构体 JSON 序列化

       我们可以使用 "encoding/json" 包下的 json.Marshal() 函数将结构体转换为 JSON 字符串。该函数接受一个参数,即要转换的结构体对象。如果转换成功,它将返回一个包含JSON数据的字节切片和一个错误值。

注意结构体的属性必须都为公开的(属性首字母大写),否则无法序列化为 json!

package main
 
import (
    "encoding/json"
    "fmt"
)
 
//Student 学生
type Student struct {
  ID     int
  Gender string
  Name   string
}
 
func main() {
    student1 := Student{
        ID : 1,
        Gender: "男",
        Name : "刘海柱",
    }
  //JSON序列化:结构体-->JSON格式的字符串
  data, err := json.Marshal(student1)
  if err != nil {
    fmt.Println("json marshal failed")
    return
  }
  fmt.Println(string(data)) // {"ID":1,"Gender":"男","Name":"刘海柱"}
  
  //JSON反序列化:JSON格式的字符串-->结构体
  str := `{"ID":1,"Gender":"男","Name":"刘海柱"}`
  student2 := &Student{}
  err = json.Unmarshal([]byte(str), student2)
  if err != nil {
    fmt.Println("json unmarshal failed!")
    return
  }
  fmt.Printf("%#v\n",student2) // &main.Student{ID:1, Gender:"男", Name:"刘海柱"}
}

5.12、结构体标签 Tag

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

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

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

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

package main
 
import (
    "encoding/json"
    "fmt"
)
 
//Student 学生
type Student struct {
  ID     int `json:"id"`
  Gender string
  Name   string // 私有属性不能被 json 访问
}
 
func main() {
    student1 := Student{
        ID : 1,
        Gender: "男",
        Name : "刘海柱",
    }
  //JSON序列化:结构体-->JSON格式的字符串
  data, err := json.Marshal(student1)
  if err != nil {
    fmt.Println("json marshal failed")
    return
  }
  fmt.Println(string(data)) // {"id":1,"Gender":"男","Name":"刘海柱"}
}

       上面,我们给 ID 属性添加了一个标签 'json:"id"',这样当使用 json 序列化的时候就可以使用我们指定的字段名 "id" 了。

相关文章
|
9天前
|
JSON 测试技术 Go
零值在go语言和初始化数据
【7月更文挑战第10天】本文介绍在Go语言中如何初始化数据,未初始化的变量会有对应的零值:bool为`false`,int为`0`,byte和string为空,pointer、function、interface及channel为`nil`,slice和map也为`nil`。。本文档作为指南,帮助理解Go的数据结构和正确使用它们。
54 22
零值在go语言和初始化数据
|
5天前
|
Cloud Native Java Go
为什么要学习Go语言?
GO logo的核心理念,即简单胜于复杂。使用现代斜体无衬线字体与三条简单的运动线相结合,形成一个类似于快速运动的两个轮子的标记,传达速度和效率。字母的圆形暗示了GO地鼠的眼睛,创造了一个熟悉的形状,让标记和吉祥物很好地搭配在一起。
22 4
|
10天前
|
设计模式 Go
Go语言设计模式:使用Option模式简化类的初始化
在Go语言中,面对构造函数参数过多导致的复杂性问题,可以采用Option模式。Option模式通过函数选项提供灵活的配置,增强了构造函数的可读性和可扩展性。以`Foo`为例,通过定义如`WithName`、`WithAge`、`WithDB`等设置器函数,调用者可以选择性地传递所需参数,避免了记忆参数顺序和类型。这种模式提升了代码的维护性和灵活性,特别是在处理多配置场景时。
44 8
|
9天前
|
存储 Go
go语言中fmt格式化包和内置函数汇总
【7月更文挑战第10天】本文介绍fmt包和`Errorf`用于创建格式化的错误消息。`fmt`包还涉及一些接口,如`Formatter`、`GoStringer`、`ScanState`、`Scanner`和`Stringer`,支持自定义格式化和输入/输出处理。
19 1
|
9天前
|
Go
go语言中格式化输出的占位符
【7月更文挑战第10天】`fmt` 包在 Go 语言中用于格式化输出,包括不同类型的占位符:%v(默认格式)、%+v(带字段名的结构体)、%#v(Go语法表示)、%T(类型表示)、%%(百分号)。布尔值用%t,整数有%b、%c、%d、%o、%q、%x、%X和%U。浮点数和复数用%b、%e、%E、%f、%g、%G。字符串和字节切片用%s、%q、%x、%X。指针用%p。占位符可配合+、-、#、空格和0进行调整。宽度和精度控制输出格式,例如 %.4g 控制小数精度。Go 没有 `%u`,但无符号整数默认打印为正数。运算符包括逻辑、比较、加减、乘除、移位、按位和按位异或等。
19 1
|
1天前
|
Oracle 关系型数据库 MySQL
|
7天前
|
安全 Go
Go语言map并发安全,互斥锁和读写锁谁更优?
Go并发编程中,`sync.Mutex`提供独占访问,适合读写操作均衡或写操作频繁的场景;`sync.RWMutex`允许多个读取者并行,适用于读多写少的情况。明智选择锁可提升程序性能和稳定性。示例展示了如何在操作map时使用这两种锁。
12 0
|
7天前
|
安全 Go 开发者
Go语言map并发安全使用的正确姿势
在Go并发编程中,由于普通map不是线程安全的,多goroutine访问可能导致数据竞态。为保证安全,可使用`sync.Mutex`封装map或使用从Go 1.9开始提供的`sync.Map`。前者通过加锁手动同步,后者内置并发控制,适用于多goroutine共享。选择哪种取决于具体场景和性能需求。
9 0
|
7天前
|
存储 安全 Java
Go语言中的map为什么默认不是并发安全的?
Go语言的map默认不保证并发安全,以优化性能和简洁性。官方建议在需要时使用`sync.Mutex`保证安全。从Go 1.6起,并发读写map会导致程序崩溃,鼓励开发者显式处理并发问题。这样做的哲学是让代码更清晰,并避免不必要的性能开销。
11 0
|
23天前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)