关于 Golang 结构体

Golang 中没有“类”的概念,Golang 中的结构体和其他语言中的类有点相似。和其他面向对
象语言中的类相比,Golang 中的结构体具有更高的扩展性和灵活性。
Golang 中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全
部或部分属性时,这时候再用单一的基本数据类型就无法满足需求了,Golang 提供了一种
自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称 struct。
也就是我们可以通过 struct 来定义自己的类型了。
type myInt int
type TypeAlias = Type

 自定义类型和类型别名的区别

类型别名与自定义类型表面上看只有一个等号的差异,我们可以通过下面的这段代码来理解它们
之间的区别。
package main

import "fmt"

//自定义类型
type myInt int16

//类型别名
type myFloat = float32

func main() {

	var x myInt = 10

	fmt.Printf("%v %T\n", x, x) 

	var y myFloat = 12.3
	fmt.Printf("%v %T", y, y) 
}
type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
}
package main

import "fmt"

type Car struct{
	name string
	color string
	price int
}

func main() {

	var s1 Car //实例化结构体
	s1.name = "朗逸"
	s1.color = "黑色"
	s1.price = 118000
	fmt.Printf("值:%v 类型:%T\n", s1, s1) 
	fmt.Printf("值:%#v 类型:%T", s1, s1)  
}
注意:结构体首字母可以大写也可以小写,大写表示这个结构体是公有的,在其他的包里面
可以使用。小写表示这个结构体是私有的,只有这个包里面才能使用。
var 结构体实例 结构体类型
package main

import "fmt"

type Car struct{
	name string
	color string
	price int
}

func main() {

	var s1 Car //实例化结构体
	s1.name = "朗逸"
	s1.color = "黑色"
	s1.price = 118000
	fmt.Printf("值:%v 类型:%T\n", s1, s1) 
	fmt.Printf("值:%#v 类型:%T", s1, s1)  
}
package main

import "fmt"


type Car struct{
	name string
	color string
	price int
}


func main() {

	var s2 = new(Car)
	(*s2).name = "奥迪"
	(*s2).color = "黑色"
	(*&s2.price) = 1000
	fmt.Printf("值:%#v 类型:%T\n", s2, s2)
}
package main

import "fmt"


type Car struct{
	name string
	color string
	price int
}


func main() {

	var s2 = new(Car)
	s2.name = "奥迪"
	s2.color = "黑色"
	s2.price = 1000
	fmt.Printf("值:%#v 类型:%T\n", s2, s2)
}

这两个的写法实质是一样的;就是对开头的一个解释

从打印的结果中我们可以看出 s2 是一个结构体指针。 

结构体实例化(第三种方法)

使用&对结构体进行取地址操作相当于对该结构体类型进行了一次 new 实例化操作。 

	var s3 = &Car{}
	s3.name = "宝贝马"
	s3.color = "红"
	s3.price = 12000
	fmt.Printf("值:%#v 类型:%T\n", s3, s3)

结构体实例化(第四种方法)

键值对初始化

    var s3 = Car{
	name : "宝贝马",
	color: "红",
	price:12000,
	}
	fmt.Printf("值:%#v 类型:%T\n", s3, s3)

 注意:最后一个属性的,要加上逗号(键值对的需要加逗号)

结构体实例化(第五种方法)

结构体指针进行键值对初始化
		var s5 = &Car{
		name : "宝贝马",
		color: "红",
		}
		fmt.Printf("值:%#v 类型:%T\n", s5, s5)
	var s6 = &Car{
	"宝贝马",
	"红",
	100000,
	}
	fmt.Printf("值:%#v 类型:%T\n", s6, s6)

使用这种格式初始化时,需要注意:

1.必须初始化结构体的所有字段。
2.初始值的填充顺序必须与字段在结构体中的声明顺序一致。
3.该方式不能和键值初始化方式混用。

结构体是值类型还是引用类型

值类型 : 改变变量副本值的时候,不会改变变量本身的值 (数组、基本数据类型、结构体)

引用类型:改变变量副本值的时候,会改变变量本身的值  (切片、map)

来个案例实验一下:(看副本改变,主体会不会改变)

/*
	值类型 : 改变变量副本值的时候,不会改变变量本身的值 (数组、基本数据类型、结构体)
	引用类型:改变变量副本值的时候,会改变变量本身的值  (切片、map)

*/

package main

import "fmt"


type Car struct{
	Name string
	Color string
	Price int64

}

func main() {
	var s1 = Car{
		"奥迪迦",
		"红",
		187123,
	}

	s2 := s1
	s2.Name = "帕莎特"
	fmt.Printf("%#v\n", s1) 
	fmt.Printf("%#v", s2) 


}

 结构体是值类型,改变副本不会改变其主的内存值

结构体方法和接收者

在 go 语言中,没有类的概念但是可以给类型(结构体,自定义类型)定义方法。所谓方法
就是定义了接收者的函数。接收者的概念就类似于其他语言中的 this 或者 self。
方法的定义格式如下:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
    函数体
}

package main

import "fmt"


 type Car struct{
	Name string
	Color string
	Price int32

 }

 func (c Car) PrintInfo(){
	fmt.Printf("车辆名称:%v 颜色为:%v 价格为:%v\n",c.Name,c.Color,c.Price)
 }

func main() {

	var c1 = new(Car)
	c1.Name = "奥托"
	c1.Color = "黑"
	c1.Price = 123423
	c1.PrintInfo()

	var c2 = Car{
		"斑马",
		"红",
		123143,
	}
	c2.PrintInfo()
	
}

package main

import "fmt"


 type Car struct{
	Name string
	Color string
	Price int32

 }

 func (c Car) PrintInfo(){
	fmt.Printf("车辆名称:%v 颜色为:%v 价格为:%v\n",c.Name,c.Color,c.Price)
 }

 func (c1 *Car) SetInfo(name string,color string){
	c1.Name = name
	c1.Color = color
 }
func main() {

	var c1 = new(Car)
	c1.Name = "奥托"
	c1.Color = "黑"
	c1.Price = 123423
	c1.PrintInfo()

	var c2 = Car{
		"斑马",
		"红",
		123143,
	}
	c2.PrintInfo()

	c1.SetInfo("宝贝马","绿")
	c1.PrintInfo()


	
}
package main

import "fmt"

//注意事项: 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。
type MyInt int

func (m MyInt) PrintInfo() {
	fmt.Println("我是自定义类型里面的自定义方法")
}

func main() {

	var a MyInt = 20

	a.PrintInfo()
}

结构体的匿名字段 

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

匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个

案例如下:
package main

import "fmt"

type Car struct{
	string
	int
}

func main() {

	c := Car{
		"法拉奔",
		123123,
	}
	fmt.Printf("汽车品牌:%v,价格为:%v",c.string,c.int)
}
package main

import "fmt"

type Car struct{
	Name string
	Price int
	Seller []string
	Buyer map[string]string
}

func main() {

	var c Car
	c.Name = "宝贝马"
	c.Price = 1231334
	c.Seller = make([]string,3,10) // 指针,slice,和map的零值都是 nil ,即还没有分配空间;如果需要使用这样的字段,需要先make,才能使用.
	c.Seller[0] = "卫宫士郎"
	c.Seller[1] = "远坂樱"
	c.Seller[2] = "鸣人"
	c.Buyer = make(map[string]string)
	c.Buyer["宫本"] = "已购"
	c.Buyer["宋璇"] = "已购"
	fmt.Printf("%#v\n",c)
	fmt.Println("----------------------------")
	fmt.Printf("%v\n",c.Buyer)



}
package main

import "fmt"

/*
结构体嵌套
*/
type Person struct{
	Name string
	Age int
	Infomation 
}

type Infomation struct{
	Address string
	Country string
}

func main() {


	var p Person
	p.Name = "奥特曼"
	p.Age = 21
	p.Infomation.Address = "m78"
	p.Infomation.Country = "光"
	
	fmt.Printf("%#v",p)




}
package main

import "fmt"

type Person struct{
	Name string
	Age int
	Infomation
}

type Infomation struct{
	City string
	Country string
}

func main() {
	var a Person
	a.Name = "奥特曼"
	a.Age = 12
	a.Country = "M78"
	a.City = "光"
	fmt.Printf("%v\n", a)
	fmt.Printf("%#v\n", a)
	fmt.Println(a.Infomation.City)
}
比如上面的,我们可以直接在a中赋值information的数据
package main

import "fmt"
type Person struct{
	Name string
	Age int
	Hobby string
	Infomation
}

type Infomation struct{
	City string
	Country string
	Hobby string
}

func main() {
	var a Person
	a.Name = "奥特曼"
	a.Age = 12
	a.Country = "M78"
	a.City = "光"
	a.City = "日本" //当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
	a.Hobby = "唱跳"
	a.Infomation.Hobby = "睡觉"
	fmt.Printf("%#v\n", a)
	

}

 关于嵌套结构体的字段名冲突

嵌套结构体内部可能存在相同的字段名。这个时候为了避免歧义需要指定具体的内嵌结构体的字段。
package main

import "fmt"

type Persion struct{
	Name string
	// Hobby string
	Information
	Realation
	
}
type Information struct{
	Hobby string
}
type Realation struct{
	Hobby string
}

func main() {

	var p Persion
	p.Name = "奥特"
	p.Hobby = "唱跳"
	fmt.Printf("%#v\n", p)
	

}

	var p Persion
	p.Name = "奥特"
	// p.Hobby = "唱跳"
	p.Information.Hobby= "唱跳"
	fmt.Printf("%#v\n", p)
package main

import "fmt"

//父结构体
type Faher struct{
	Name string
}
 
func (a Faher) fn1(){
	fmt.Printf("%v发动x射线\n",a.Name)
}
//子结构体
type Son struct{
	Fight string
	Faher //结构体嵌套 继承
}
func (s Son) fn2(){
	fmt.Printf("%v超人飞踢",s.Name)
}
func main() {
	var c = Son{
		Faher: Faher{
			Name: "奥托之父",
		},
	}
	c.fn1()
	c.fn2()
}
package main

import "fmt"

//父结构体
type Faher struct{
	Name string
}
 
func (a Faher) fn1(){
	fmt.Printf("%v发动x射线\n",a.Name)
}
//子结构体
type Son struct{
	Fight string
	*Faher //结构体嵌套 继承
}
func (s Son) fn2(){
	fmt.Printf("%v超人飞踢",s.Name)
}
func main() {
	var c = Son{
		Faher: &Faher{
			Name: "奥托之父",
		},
	}
	c.fn1()
	c.fn2()
}