1.结构体的开篇
一个没有结构体的例子:
package main import "fmt" func main() { // 1.变量 var cat01Name string = "cat_hei" var cat01Age int = 3 fmt.Println(cat01Name,cat01Age) var cat02Name string = "cat_bai" var cat02Age int = 4 fmt.Println(cat02Name,cat02Age) // 2.数组 var catNames [2]string = [...]string{"hei","bai"} var catAges [2]int = [...]int{3,4} fmt.Println(catNames,catAges) // }
使用结构体:结构体对于其他语言就是对象
2.GO语言面向对象编程说明
(1)golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所有我们说Golang支持面向对象编程特性是比较准确的。
(2)Golang没有类(class),GO语言的结构体(struct)和其他语言的类(class)有同等的地位,你可以理解Golang是基于struct来实现OOP特性的。
(3)Golang面向对象编程非常简洁,去掉了传统OOP语言的继承,方法重载,构造函数和析构函数,隐藏的this指针等待
(4)Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其他OOP语言不一样,比如继承:Golang没有extends关键字,继承是通过匿名字段来实现。
(5)Golang面向对象(OOP)很优雅,OOP本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。面向接口编程的思想。
结构体(类)----实例(变量)
package main import "fmt" // // 定义一个结构体 type Cat struct{ Name string Age int Color string } func main() { //使用结构体 var cat1 Cat // 使用一个结构体 cat1.Name = "bai" cat1.Age = 2 cat1.Color = "white" // fmt.Println(cat1) fmt.Println("Name = ",cat1.Name) fmt.Println("Age = ",cat1.Age) fmt.Println("Color = ",cat1.Color) }
1.结构体是自定义的数据类型,代表一类事物。
2.结构体变量(实例)是具体的,实际的,代表一个具体变量。
结构体是一种值类型数据。
声明结构体语法:
type 结构体名称 struct {
filed1 type
field2 type
field3 type
}
3.结构体的字段/属性
(1)基本介绍:
1.从概念上加法:结构体字段=属性=field
2.字段是结构体的一个组成部分,一般是基本数据类型,数组,也是引用类型。
(2)注意事项和细节说明
1.字典声明语法变量,示例: 字段名 字段类型
2.字段的类型可以为:基本类型、数组或引用类型
3.在创建一个结构体变量,如果没有给字段赋值都有对应的默认值。大概如下:
布尔类型是false,数值是0,字符串是””
数组类型默认值和它的元素类型相关,比如 score[3] int则为[0,0,0]
指针,slice和map的默认值是nil,即没有分配空间。
4.结构体的字段是独立的,互不影响,一个结构体变量字段的更改,不影响另外一个。
结构体里使用:数组,slice和map的数据
package main import "fmt" // 如果结构体的字段为:指针,slice和map的默认值都是nil // 如果需要使用这些字段,需要先make才能使用 type Person struct { Name string Age int Scores [5]float64 // ptr *int // 指针 Slice []int // 切片 Map map[string]string // map } func main(){ // 定义结构体 var person1 Person fmt.Println(person1) // { 0 [0 0 0 0 0] <nil> [] map[]} if person1.ptr == nil{ fmt.Println("ok1") } if person1.Slice == nil { fmt.Println("ok2") } if person1.Map == nil { fmt.Println("OK3") } // 使用slice先make person1.Slice = make([]int,10) person1.Slice[0] = 10 fmt.Println(person1.Slice) // 使用map先make person1.Map = make(map[string] string) person1.Map["hh"] = "666" fmt.Println(person1.Map) }
结构体里面的属性之间不会相互影响例子:
package main import "fmt" type Cat struct{ Name string Age int } func main(){ // var cat1 Cat cat1.Name = "bai" cat1.Age = 2 cat2 := cat1 cat2.Age = 3 fmt.Println(cat1) fmt.Println(cat2) }
4.创建结构体变量和访问结构体方式
方式1:直接声明
var person01 Person
方式2:{}
p2 := Person{"two",20}
方式3:&
var p3 *Person = new(Person)
方式4:
例子如下:
package main import "fmt" type Person struct{ Name string Age int } func main(){ // 方式1:var p1 Person var p1 Person p1.Name = "one" p1.Age = 19 fmt.Println(p1) // 方式2:p2 := Person{"two",20} p2 := Person{"two",20} fmt.Println(p2) // 方式3: var p3 *Person = new(Person) // p3为结构体的指针 // 因为p3是个指针,因此标准的写法如下: (*p3).Name = "Three" (*p3).Age = 21 fmt.Println(p3) // 可简化为:因为创建者底层对上面指针做了处理 // p3.Name = "Three" // p3.Age = 21 // 方式4:var p4 *Person = &Person() // p4为指针 var p4 *Person = &Person{} // 也可以直接在{}进行赋值 (*p4).Name = "four" (*p4).Age = 22 fmt.Println(p4) // p4为结构体的指针 fmt.Println(p4.Name) fmt.Println(p4.Age) }
5.结构体的内存分配机制
例:
package main import "fmt" type Person struct{ Name string Age int } func main(){ // 以下p1 和 p2 共享一个内存地址 var p1 Person p1.Age = 10 p1.Name = "小明" var p2 *Person = &p1 // p2是一个指针 fmt.Println((*p2).Age) //golang中 (*p2) 与 p2相同 fmt.Println(p2.Age) p2.Name = "SIX" fmt.Println(p2.Name) fmt.Println(p1.Name) fmt.Println((*p2).Name) }
6.结构体的注意事项和使用细节
(1)结构体的所有字段的内存是连续的[例1]
(2)两个结构体间转换必须两个结构体字段相同[例2]
(3)结构体进行type重新定义(相当于取别名),Golang认为是重新定义数据类型,但是互相之间可以强转。[例2]
(4)struct的每个字段,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化。
例子1:
package main import "fmt" type Point struct { x int y int } type Rect struct { leftUp, rightDown Point } type RectPointer struct { leftUp, rightDown *Point } func main() { // r1 := Rect{Point{1, 2}, Point{3, 4}} // r1 有4个int ,在内存中是连续分布的 fmt.Println("r1.leftUp.x地址:", &r1.leftUp.x) fmt.Println("r1.leftUp.y地址:", &r1.leftUp.y) fmt.Println("r1.rightDown.x地址:", &r1.rightDown.x) fmt.Println("r1.rightDown.x地址:", &r1.rightDown.y) // // 八进制 一个地址八个字节 /* r1.leftUp.x地址: 0xc000054120 r1.leftUp.y地址: 0xc000054128 r1.rightDown.x地址: 0xc000054130 r1.rightDown.x地址: 0xc000054138 */ fmt.Println("结构体下面的值为地址的情况") r2 := RectPointer{&Point{1, 2},&Point{3, 4}} fmt.Println("r2.leftUp.x地址:", &r2.leftUp.x) fmt.Println("r2.leftUp.y地址:", &r2.leftUp.y) fmt.Println("r2.rightDown.x地址:", &r2.rightDown.x) fmt.Println("r2.rightDown.x地址:", &r2.rightDown.y) fmt.Println(r2.rightDown.x) // x值 }
例2:
package main import "fmt" type A struct{ Num int } type B struct{ Num int } func main() { // 如果要在两个结构体间转换必须两个结构体字段相同 // 字段个数,字段名称,字段类型相同 var a A var b B a = A(b) // 强转 fmt.Println(a,b)
例4:
package main import "fmt" import "encoding/json" type Monster struct{ Name string `json:"name"` // `json:"name"` 这个就是结构体的标签 Age int `json:"age"` // 效果是可以首字母大写的变量可以在json返回小写 Skill string `json:"skill"` } func main(){ // 1.创建一个Monster变量 monster := Monster{"ZEOR",25,"coding go"} // 2.将monster变量序列化为json格式的字符串 jsononster,err := json.Marshal(monster) if err != nil{ fmt.Println("json处理错误") } fmt.Println("jsononster:",string(jsononster)) // jsononster数据类型为bytes需求强转为string }