go 结构体struct

1.结构体声明:   type  结构体名 struct , 字段: field  type   

type Person struct{
	Name string
	Age int
}

2.结构体名称首字母小写,则包外部不能使用。大写则包外部能使用。 字段名称首字母大写,包外部才能访问,首字母小写包外部不能访问。

3.字段的数据类型:可以是基本数据类型、数组或引用类型

4.创建一个结构体变量以后,如果没有给字段赋值,都对应的是一个“零值”(默认值),默认值规则如下:

        布尔类型是false,数值类型是 0 ,字符串是 "",

        数组类型的默认值和它的元素类型相关,

        比如 array [3]string 则为["","",""]  ,   array2 [2]int 则为[0,0]

        指针,切片,map,channel 的零值是 nil,因为还没有分配内存空间(分配内存空间:指针需要new   切片、map、channel需要make)

5.结构体是值拷贝(不是引用类型)

type Person struct{
	Name string
	Age int
}
func main() {
	a := Person{Name:"Tom",Age: 18}
	b := a  //值拷贝
	b.Name = "Johon"
	fmt.Println(a)
	fmt.Println(b)
}

6.结构体 变量创建(实例化对象),有四种方式

package main

import "fmt"

type Person struct{
	Name string
	Age int
}
func main() {
	//第一种
	var p Person
	p.Name = "Roll"
	p.Age = 18
	fmt.Println(p)

	//第二种
	p2 := Person{Name: "Jack",Age:20}
	p3 := Person{"jack2",21}
	fmt.Println(p2,p3)

	//第三种  new方式 是指针类型变量
	var p4 *Person = new(Person)
	(*p4).Name = "Dool"
	(*p4).Age  = 22
	fmt.Println(*p4)
	//因为 go官方有做底层优化 (*p4).Name 等价于 p4.Name
	p4.Name = "Dool2"
	p4.Age  = 23
	fmt.Println(*p4)

	//第四 种方式 指针类型变量
	var p5 *Person = &Person{Name: "Alon",Age: 18}
	(*p5).Name = "Alon1"
	(*p5).Age  = 19
	var p6 *Person = &Person{}
	p6.Name = "Alon2"
	p6.Age  = 30
	fmt.Println(*p5)
	fmt.Println(*p6)
}

第三种和第四种:用new()和 &取址方式 创建的结构体变量是指针, 按照表标准的操作方式是(*p).Name, 但是go官方认为这种操作方式过于复杂,所以底层做了简化, 可以直接 p.Name 来操作

7.结构体变量是值拷贝,但是指针类型的结构体变量 指向的是一个内存地址,所以指针类型的结构体变量进行赋值操作是 值引用

package main

import "fmt"

type Person struct{
	Name string
	Age int
}
func main() {
	//第一种
	var p Person
	p.Name = "Roll"
	p.Age = 18
	fmt.Println(p)

	p1 := &p  //指针
	fmt.Println(*p1)

	p1.Name = "Jack"
	p1.Age  = 20
	fmt.Println(p)
	fmt.Println(*p1)
}

结构体变量进行 取址赋值 给另一个变量 ,另一个变量则是指针。 指针变量指向的与原变量是同一个内存空间,所以指针变量操作的字段值改变,则原变量的值也会改变。(另外需要注意的是,指针变量操作符,&是取址操作符,*是取值操作符)

8.结构体的指针变量,在取字段值的时候,写法需要注意

原因很简单:因为  点 “.”运算符的优先级比 “*”的高,所以指针变量需要 (*p).Name

9.结构体变量里的 字段内存地址是连续的(字段类型不同,连续的值不同),同一个结构体但不同的结构体变量的内存地址不一定是连续的。 字段内存地址是连续的是因为方便快速查找值。

package main

import "fmt"

type Person struct{
	Name string
	Age int
	Sex bool
}
func main() {
	p := Person{Name: "jack", Age: 18,Sex: false}
	p2 := Person{Name: "roll",Age: 20,Sex: true}
	fmt.Printf("p.Name内存地址:%p    p.Age内存地址:%p    p.Sex内存地址:%p \n",&p.Name,&p.Age,&p.Sex)
	fmt.Printf("p2.Name内存地址:%p   p2.Age内存地址:%p   p2.Sex内存地址:%p \n",&p2.Name,&p2.Age,&p2.Sex)
	fmt.Printf("p内存地址:%p  p2内存地址:%p \n",&p,&p2)
}

10.不同结构体变量之间的强制转换:

转换的要求是严格的: 要求结构体字段名称一致,字段个数一致,字段数据类型一致,如果这三点中有一个不同则不能做强制转换

package main

import "fmt"

type Person struct{
	Name string
	Age int
}
type Student struct{
	Name string
	Age int
}
func main() {
	var p Person
	var s Student
	s.Name = "jk"
	p = Person(s) //Student类型强制转换成Person类型
	fmt.Println(p)
}

11.定义一个结构体类型的结构体(重新定义结构体,相当于给结构体取别名),go会认为是两种数据类型,但是可以互相强制转换。这也说明go对于数据类型是非常严谨的

import "fmt"

type Person struct{
	Name string
	Age int
}
type A Person //重新定义结构体(相当于取别名)

func main() {
	person := Person{"jack",12}
	a := A{}
	a = A(person) //Person变量转成 A
	fmt.Println(a)
}

运行结果:

12.我们实际项目中,数据在网络上传递用的一般是json,我们的结构体也能通过        json.Marshal进行转换成json, 结构体中的字段名称因为需要其他包能访问,所以首字母是大写的,而我们一般网络上进行传递的json字段名称都是一般是小写的,或者是小写加下划线。所以go通过给结构体字段加tag标签,在进行序列化和反序列化时对字段名进行处理,其实现方式用的是反射机制。

package main

import (
	"encoding/json"
	"fmt"
)

type Person struct{
	Name string
	Age int
}
type Student struct {
	Name string `json:"student_name"`
	Age int `json:"student_age"`
}

func main() {
	person := Person{"jack",12}
	student := Student{"roll",18}
	fmt.Println(person,"\n",student)
	//转换成json
	p,err := json.Marshal(person)
	s,err1 := json.Marshal(student)
	if err != nil {
		fmt.Println("trans error:",err)
	}
	if err1 != nil {
		fmt.Println("trans error:",err)
	}
	//打印json字符串
	fmt.Println(string(p))
	fmt.Println(string(s))

}

执行结果:

需要注意的是 tag标签的书写方式,外面包的是 反引号,转的是json类型 名称用双引号包住,同一个字段也可以定义多个tag 用空格 隔开

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值