• 【go语言学习】结构体struct


    Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了。
    Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。 也就是我们可以通过struct来定义自己的类型了。

    一、结构体的定义和初始化

    1、结构体的定义

    结构体的定义使用typestruct关键字

    type 结构体名 struct {
       字段名 字段类型;
       字段名 字段类型;
       ...
       字段名 字段类型;
    }
    

    其中:

    • 类型名:标识自定义结构体的名称,在同一个包内不能重复。通过首字母大小写控制访问。
    • 字段名:表示结构体字段名。结构体中的字段名必须唯一。通过首字母大小写控制访问。
    • 字段类型:表示结构体字段的具体类型。
    package main
    
    // Person 一个结构体,包含name, age两个字段
    type Person struct {
    	Name string // 包外可访问
    	age  int    // 包外不可访问
    }
    
    func main() {
    
    }
    
    2、结构体的初始化
    package main
    
    import "fmt"
    
    // Person 一个结构体,包含name, age两个字段
    type Person struct {
    	Name string // 包外可访问
    	age  int    // 包外不可访问
    }
    
    func main() {
    	// 方法一
    	var p1 Person
    	p1.Name = "tom"
    	p1.age = 20
    	fmt.Println(p1)
    	// 方法二
    	p2 := Person{}
    	p2.Name = "marry"
    	p2.age = 18
    	fmt.Println(p2)
    	// 方法三
    	p3 := Person{
    		Name: "jack",
    		age:  23,
    	}
    	fmt.Println(p3)
    	// 方法四
    	p4 := Person{
    		"lili",
    		24,
    	}
    	fmt.Println(p4)
    }
    

    运行结果

    {tom 20}
    {marry 18}
    {jack 23}
    {lili 24}
    

    二、结构体的使用

    1、结构体指针

    数据类型:

    • 值类型:int,string,bool,float,arry,struct
    • 引用类型:slice,map,function,pointer,chan
    package main
    
    import "fmt"
    
    // Person 一个结构体,包含name, age两个字段
    type Person struct {
    	Name string // 包外可访问
    	age  int    // 包外不可访问
    }
    
    func main() {
    	p := Person{
    		Name: "tom",
    		age:  20,
    	}
    	// 方法一 直接创建结构体指针
    	var ptr1 *Person
    	fmt.Printf("%T, %v
    ", ptr1, ptr1)
    	ptr1 = &p
    	// (*ptr1).Name = "jack"
    	ptr1.Name = "jack"
    	fmt.Println(p)
    	// 方法二 使用new创建结构体指针
    	ptr2 := new(Person)
    	fmt.Printf("%T, %v
    ", ptr2, ptr2)
    	ptr2 = &p
    	ptr2.Name = "lili"
    	fmt.Println(p)
    	// 方法三 使用&地址符创建结构体指针
    	ptr3 := &Person{}
    	fmt.Printf("%T, %v
    ", ptr3, ptr3)
    	ptr3 = &p
    	ptr3.Name = "marry"
    	fmt.Println(p)
    }
    

    运行结果

    *main.Person, <nil>
    {jack 20}
    *main.Person, &{ 0}
    {lili 20}
    *main.Person, &{ 0}
    {marry 20}
    
    2、匿名结构体

    没有名字的结构体,在创建结构体的时候同时创建对象。

    package main
    
    import "fmt"
    
    func main() {
    	p := struct {
    		name string
    		age  int
    	}{
    		name: "tom",
    		age:  20,
    	}
    	fmt.Println(p)
    }
    

    运行结果

    {tom 20}
    
    3、匿名字段

    在结构体中,不写字段的名字,只写类型。匿名字段不允许重复。

    package main
    
    import "fmt"
    
    type worker struct {
    	string
    	int
    }
    
    func main() {
    	w := worker{
    		"tom",
    		20,
    	}
    	fmt.Println(w)
    	fmt.Println(w.string, w.int)
    }
    

    运行结果

    {tom 20}
    tom 20
    
    4、结构体嵌套

    一个结构体中的字段,是另一个结构体。

    package main
    
    import "fmt"
    
    // Address 结构体
    type Address struct {
    	Province string
    	City     string
    }
    
    // User 结构体
    type User struct {
    	Name string
    	Age  int
    	// Address Address
    	Address //嵌套匿名字段
    }
    
    func main() {
    	u := User{
    		Name: "tom",
    		Age:  20,
    		Address: Address{
    			Province: "江苏省",
    			City:     "苏州市",
    		},
    	}
    	fmt.Printf("%+v
    ", u)
    }
    

    运行结果

    {Name:tom Age:20 Address:{Province:江苏省 City:苏州市}}
    

    嵌套结构体字段访问

    • 在结构体中属于匿名结构体的字段称为提升字段,因为它们可以被访问,就好像它们属于拥有匿名结构字段的结构一样。
    • 嵌套结构体内部可能存在相同的字段名。在这种情况下为了避免歧义需要通过指定具体的内嵌结构体字段名。
    package main
    
    import "fmt"
    
    // Address 结构体
    type Address struct {
    	Province   string
    	City       string
    	CreateTime string
    }
    
    // Email 结构体
    type Email struct {
    	Email      string
    	CreateTime string
    }
    
    // User 结构体
    type User struct {
    	Name string
    	Age  int
    	// Address Address
    	Address
    	Email
    }
    
    func main() {
    	u := User{
    		Name: "tom",
    		Age:  20,
    		Address: Address{
    			Province:   "江苏省",
    			City:       "苏州市",
    			CreateTime: "2020.09.26",
    		},
    		Email: Email{
    			Email:      "tom@github.com.cn",
    			CreateTime: "2020.09.25",
    		},
    	}
    	fmt.Printf("%+v
    ", u)
    	fmt.Println(u.City)  //City是提升字段
    	fmt.Println(u.Email) //Email是提升字段
    	fmt.Println(u.Email.CreateTime)
    	fmt.Println(u.Address.CreateTime)
    }
    

    运行结果

    {Name:tom Age:20 Address:{Province:江苏省 City:苏州市 CreateTime:2020.09.26} Email:{Email:tom@github.com.cn CreateTime:2020.09.25}}
    苏州市
    {tom@github.com.cn 2020.09.25}
    2020.09.25
    2020.09.26
    
    5、结构体与json序列化

    go语言通过json包下的Marshal(v interface{}) ([]byte, error)函数实现结构体对象的json序列化。
    通过json包下的Unmarshal(data []byte, v interface{}) error函数实现json的反序列化。

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    // Student 结构体
    type Student struct {
    	Name    string `json:"name"`
    	Age     int    `json:"age"`
    	Address string `json:"address"`
    }
    
    func main() {
    	s1 := Student{
    		Name:    "tom",
    		Age:     20,
    		Address: "苏州市",
    	}
    	data, err := json.Marshal(s1)
    	if err != nil {
    		fmt.Println("json.marshal failed, err=", err)
    		return
    	}
    	fmt.Printf("json:%s
    ", data)
    	str := `{"name":"jack","age":21,"address":"上海市"}`
    	s2 := new(Student)
    	err = json.Unmarshal([]byte(str), s2) // s2需要传入一个指针
    	if err != nil {
    		fmt.Println("json.unmarshal failed, err=", err)
    		return
    	}
    	fmt.Printf("%+v
    ", s2)
    }
    

    运行结果

    json:{"name":"tom","age":20,"address":"苏州市"}
    &{Name:jack Age:21 Address:上海市}
    

    三、构造函数

    Go语言的结构体没有构造函数,我们可以自己实现。

    package main
    
    import "fmt"
    
    // Student 结构体
    type Student struct {
    	Name    string
    	Age     int
    	Address string
    }
    
    // NewStudent 构造函数
    func NewStudent(name string, age int, address string) *Student {
    	return &Student{
    		name,
    		age,
    		address,
    	}
    }
    
    func main() {
    	s := NewStudent("lili", 20, "上海市")
    	fmt.Println(s)
    }
    

    运行结果

    &{lili 20 上海市}
    

    四、方法

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

    func (t type) methodName(parameter list) (return list) {
    
    }
    

    其中

    • t接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
    • type接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
    • 方法名、参数列表、返回参数:具体格式与函数定义相同。
    package main
    
    import "fmt"
    
    // Student 结构体
    type Student struct {
    	Name    string
    	Age     int
    	Address string
    }
    
    // NewStudent 构造函数
    func NewStudent(name string, age int, address string) *Student {
    	return &Student{
    		name,
    		age,
    		address,
    	}
    }
    
    // Learn 结构体Student的方法
    func (s Student) Learn(class string) {
    	fmt.Printf("%v正在努力学习%v课
    ", s.Name, class)
    }
    func main() {
    	s := NewStudent("lili", 20, "上海市")
    	fmt.Println(s)
    	s.Learn("english")
    }
    

    运行结果

    &{lili 20 上海市}
    lili正在努力学习english课
    

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

    + 指针类型的接受者

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

    package main
    
    import "fmt"
    
    // Student 结构体
    type Student struct {
    	Name    string
    	Age     int
    	Address string
    }
    
    // SetAge 结构体Student的方法
    func (s *Student) SetAge(age int) {
    	s.Age = age
    }
    
    func main() {
    	s := Student{
    		"lili",
    		20,
    		"苏州市",
    	}
    	fmt.Println(s)
    	s.SetAge(18)
    	fmt.Println(s)
    }
    

    运行结果

    {lili 20 苏州市}
    {lili 18 苏州市}
    

    + 值类型接受者

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

    package main
    
    import "fmt"
    
    // Student 结构体
    type Student struct {
    	Name    string
    	Age     int
    	Address string
    }
    
    // SetAddress 结构体Student的方法
    func (s Student) SetAddress(address string) {
    	s.Address = address
    }
    func main() {
    	s := Student{
    		"lili",
    		20,
    		"苏州市",
    	}
    	fmt.Println(s)
    	s.SetAddress("上海市")
    	fmt.Println(s)
    }
    

    运行结果

    {lili 20 苏州市}
    {lili 20 苏州市}
    

    + 什么时候应该使用指针类型接收者

    • 需要修改接收者中的值
    • 接收者是拷贝代价比较大的大对象
    • 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
  • 相关阅读:
    HLS直播和时移项目上线
    贪心-hdu-1789-Doing Homework again
    算法---天才排序算法---睡眠排序
    项目集成项目管理之项目范围管理
    hdu1429之BFS
    优秀程序员不得不知道的20个位运算技巧
    eclipse报错:Failed to load the JNI shared library
    HDU 2689 sort it
    svn使用经验---不断总结
    linux下svn的用法
  • 原文地址:https://www.cnblogs.com/everydawn/p/13906883.html
Copyright © 2020-2023  润新知