Go struct结构体

本文最后更新于:10 天前

Golang中没有类的概念,Golang中的结构体和其他语言中的类有点类似,和他语言的面向对象相比,Golang语言的结构体具有更高灵活性和扩展性

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

1 结构体是值类型

结构体首字母可以大写也可以小写,大写表示这个结构体(包括结构体里面的字段名称也是一样的)是共有的,其他包可以调用,小写是不可以,只可以内部函数自己使用

2 Golang中 type关键词

Golang中通过type关键词定义一个结构体,type可以自定义类型和类型别名

2.1 自定义类型

在go语言中有一些基本的数据类型,如string 整形,浮点型,布尔等类型,Go语言中可以使用type关键字来定义自定义类型。

type myInt int

上面代码表示: 将myInt 定义为int类型,通过type关键词字的定义。myInt就是一种新的类型,它具有int的特性

2.2 类型别名

类型别名规定: TypeAlias 只是Type的别名,本质上TypeAlias与Type是同一个类型

package main

import "fmt"

func main() {
	type myInt int               //自定义int类型
	type myFn func(int, int) int //自定义方法类型
	type myFloat = float64       //类型别名 : 从打印结果可以看到,类型别名打印的还是底层的类型
	var a myInt = 10
	var b myFloat = 10.1
	fmt.Printf("a类型: %T\n", a) //打印结果 a类型: main.myInt
	fmt.Printf("b类型: %T\n", b) //打印结果:b类型: float64

}

3 结构体定义和初始化

3.1 结构体的定义

type 累姓名 struct {
  字段 字段类型
  字段 字段类型
  ...
}

3.2 结构体实例化

初始化结构体的七种方法

package main

import (
	"fmt"
)

type Person struct {
	name string
	age  int
	sex  string
}

func main() {
	var p1 Person
	p1.name = "bds"
	p1.sex = "nan"
	p1.age = 20
	fmt.Printf("p1: %v 类型:  %T\n", p1, p1) //value: {bds 20 nan} 类型:  main.Person
	fmt.Printf("p1: %#v 类型: %T\n", p1, p1) //value: main.Person{name:"bds", age:20, sex:"nan"} 类型:main.Person

	var p2 = new(Person)
	p2.name = "bds2"
	p2.sex = "nan"
	p2.age = 22
	fmt.Printf("p2值:%#v,类型:%T\n", p2, p2)
	var p3 = &Person{}
	p3.name = "bds3"
	p3.sex = "nan"
	p3.age = 23
	fmt.Printf("p3值:%#v,类型:%T\n", p3, p3)
	var p4 = Person{
		name: "bds4",
		sex:  "nan",
		age:  24,
	}
	fmt.Printf("p4值:%#v,类型:%T\n", p4, p4)
	var p5 = &Person{
		name: "bds5",
		age:  25,
		sex:  "nan",
	}
	fmt.Printf("p5值:%#v,类型:%T\n", p5, p5)
	var p6 = &Person{
		name: "bds6",
	}
	fmt.Printf("p6值:%#v,类型:%T\n", p6, p6)
	var p7 = &Person{
		"bds",
		21,
		"男",
	}
	fmt.Printf("p7值:%#v,类型:%T\n", p7, p7)
}

/*
p1: {bds 20 nan} 类型:  main.Person
p1: main.Person{name:"bds", age:20, sex:"nan"} 类型: main.Person
p2值:&main.Person{name:"bds2", age:22, sex:"nan"},类型:*main.Person
p3值:&main.Person{name:"bds3", age:23, sex:"nan"},类型:*main.Person
p4值:main.Person{name:"bds4", age:24, sex:"nan"},类型:main.Person
p5值:&main.Person{name:"bds5", age:25, sex:"nan"},类型:*main.Person
p6值:&main.Person{name:"bds6", age:0, sex:""},类型:*main.Person
p7值:&main.Person{name:"bds", age:21, sex:"男"},类型:*main.Person

*/

4 结构体方法和继承者

在Go语言中,没有类的概念,但是可以给结构体定义方法,就是定义了一个接受者函数,接收者的概念就类似于其他语言中的this 或者self

方法如下:

func (接收者变量 接受者类型) 方法名字(参数列表) (返回参数) {
	函数体
}
  • 接受者变量: 接受者中的参数变量名字在命名时候,官方建议使用接收者类型名字的第一个小写字母,而不是self,this之类的名字,例如Person类型的接收者变量名字应该为p

  • 接受者类型: 接收者类型和参数类似,可以是指针类型和非指针类型

  • 方法名,参数列表,返回参数 具体格式与函数定义相同

例子: 给结构体Persion 定义一个打印Pricntinfo方法和SetInfo方法

package main

import "fmt"

type Person struct {
	Name   string
	Age    int
	worker string
}

func (p Person) Pringinfo() {
	fmt.Printf("姓名:%v,年龄:%v\n", p.Name, p.Age)
}
// 因为传入的是指针变量,所以这里修改值,会直接影响原来的结构体里面的值。
func (p *Person) SetInfo() {   
	p.Name = "fanbu"
	p.Age = 1
	p.worker = "golang"
}

func main() {
	var p1 = Person{
		Name:   "bds1",
		Age:    21,
		worker: "linux",
	}
	p1.Pringinfo()
	p1.SetInfo()
	p1.Pringinfo()
}
/*
姓名:bds1,年龄:21
姓名:fanbu,年龄:1
*/

5 给自定义类型添加方法

package main

import "fmt"

type myInt int   //比如我们也可以给myInt 定义方法

func (m myInt) Print() {
	fmt.Println("我是myInt 自定义方法")
}

func main() {
	var a myInt = 10
	a.Print()

}

6 结构体的匿名字段和其他类型使用

结构体允许其成员没有字段名字而只有类型。这种没有名字的字段就成为匿名字段,匿名字段名字必须有唯一性,所以如果一个结构体里面有同样类型的时,就会报错。

6.1 使用其他数据类型

package main

import "fmt"

/*
结构体字段类型可以是 基本数据类型 ,也可以是 切片, Map 以及 结构体
如果结构体的字段类型是: 指针 slice ,map的零值都是nil 即还没有分配内存空间
如果需要使用这样的字段: 需要先make ,才能使用
*/
type Person1 struct {
	Name  string
	hobby []string
	map1  map[string]string
}

func main() {
	var p1 Person1
	p1.Name = "bds"
	p1.hobby = make([]string, 4, 4)
	p1.hobby[0] = "sleep"
	p1.hobby[1] = "moving"

	p1.map1 = make(map[string]string)
	p1.map1["phone"] = "1333123123"
	p1.map1["addr"] = "BeiJing"
	fmt.Printf("%#v", p1)

}
//main.Person1{Name:"bds", hobby:[]string{"sleep", "moving", "", ""}, map1:map[string]string{"addr":"BeiJing", "phone":"1333123123"}}

6.2 结构体嵌套

有名字的嵌套

type Person1 struct {
	Name  string
	hobby []string
	map1  map[string]string
	Body  BodyInfo
}
type BodyInfo struct {
	weight string
	age    int
}
func main() {
  var p2 Person1
	p2.Name = "bds1"
	p2.Body.weight = "180"   //嵌套的结构体赋值通过 点. 操作
	p2.Body.age = 18     
  
	fmt.Printf("%v", p2)
  fmt.
}

匿名嵌套

type Person1 struct {
	Name  string
	hobby []string
	map1  map[string]string
	BodyInfo                    //匿名字段  Persion1结构体嵌套BodyInfo结构体
}
type BodyInfo struct {
	weight string
	age    int
}
func main() {
  var p2 Person1
	p2.Name = "bds1"
	p2.BodyInfo.weight = "180"
	p2.BodyInfo.age = 18
	fmt.Printf("%v\n", p2)
	fmt.Println(p2.BodyInfo.age)  //18 
  // 如果是匿名字段嵌套,可以直接使用匿名字段里面的字段进行取值。
	fmt.Println(p2.age)           //18 也就是 先在当前结构体查找,找不到去结构体里面查找。然后取值。
}

7 结构体的继承

package main

import "fmt"
//父亲结构体
type Animal struct {
	Name string
}

//儿子结构体 ,继承了父亲的结构体里面的属性和方法
type Dog struct {
	color string
	age   int
	wang  string
	Animal        //其实就是通过嵌套来实现继承
}

func (a Animal) do() {
	fmt.Printf("%v\n", a.Name)
}
// 继承父亲的 Name属性
func (d Dog) run() {
	fmt.Printf("%v 在%v\n", d.Name, d.wang)
}
func main() {
	var s1 = Dog{
		wang:  "旺旺旺",
		color: "writer",
		Animal: Animal{
			Name: "小花",
		},
	}
	fmt.Println(s1)
  //因为Dog 继承父亲。所以可以用父亲的do方法
	s1.do()
	s1.run()

}
/*
{writer 0 旺旺旺 {小花}}
小花 在运动园 
小花 旺旺旺
*/

8 结构体 json数据转换

比如我们Golang要给app或者小程序提供api接口数据,这个时候就需要涉及到结构体和json之间的相互转换

Golang Json序列化是指: 把结构体数据转化为json格式的字符串,Golang json的反序列化是指把json数据转为Golang中的结构体对象

Golang中的序列化和反序列化主要通过encoding/json包中的json.Marshal()和json.Unmarshal()

package main

import (
	"encoding/json"
	"fmt"
)
type Stu struct {
	Name  string
	Age   int
	class string
}
func main() {
	var s = Stu{
		Name:  "bds1",
		class: "高中",
		Age:   18,
	}
	fmt.Println(s)

	stuByte, err := json.Marshal(s)
	if err != nil {
		fmt.Println(err)
	} else {
		jsonStu := string(stuByte)
		fmt.Println(jsonStu)     //转换成字符串
	}
}

Json 转换为 结构体

var b = `{"Name":"bds2","Age":19}`
var stuJson = Stu{}
err1 := json.Unmarshal([]byte(b), &stuJson)
if err1 != nil {
	fmt.Println(err1)
} else {
	fmt.Println(stuJson)
	fmt.Println(stuJson.Name)
 }

9 结构体标签Tag

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

`key:"value" key2:"value2"`

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

注意: 不在key和value之间加上空格

package main

import (
	"encoding/json"
	"fmt"
)

type stuf struct {
	Name string `json:"name"`  //通过tag 来自定义转换成json格式后的key
	Age  int    `json:"age"`
}

func main() {
	var student = stuf{
		Name: "bds1",
		Age:  19,
	}

	jsonStudent, err := json.Marshal(student)
	if err != nil {
		fmt.Println(err)
	} else {
		//通过定义tag标签 我们可以看到最后的json格式的key是 我们结构体中自定义的tag标签里面的key。
		fmt.Println(string(jsonStudent))   //{"name":"bds1","age":19} 
	}
}

举一个稍微复杂的例子

package main

import (
	"encoding/json"
	"fmt"
)

type class struct {
	Name     string    `json:"name"`
	Students []student `json:"stu"`   //切片结构体类型
}
type student struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	Addr string `json:"address"`
}

func main() {
	var c1 = class{
		Name:     "一年一班",
		Students: make([]student, 0),
	}
	for i := 0; i < 10; i++ {
		var stu = student{
			Name: fmt.Sprintf("stu_%v", i),
			Age:  18,
			Addr: fmt.Sprint("Bj_1001", i),
		}
		c1.Students = append(c1.Students, stu)
	}
	fmt.Println(c1)
	JsonClass, err := json.Marshal(c1)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(string(JsonClass))
	}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!