29_Go基础(结构体)
1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "unsafe" 7 ) 8 9 // 1. 自定义变量类型 10 type newInt int 11 type myInt = int 12 13 // 2. 定义结构体 14 type person struct { 15 name, city string 16 age int8 17 } 18 19 type student struct { 20 name string 21 age int 22 } 23 24 type test struct { 25 a int8 26 b int8 27 c int8 28 d int8 29 } 30 31 func f1() { 32 var a newInt 33 var b myInt 34 fmt.Printf("type a: %T\n", a) // type a: main.newInt 35 fmt.Printf("type b: %T\n", b) // type b: int 36 } 37 38 func f2() { 39 var p1 person 40 p1.name = "张三" 41 p1.city = "西安" 42 p1.age = 16 43 fmt.Printf("p1=%v\n", p1) // p1={张三 西安 16} 44 fmt.Printf("p1=%#v\n", p1) // p1=main.person{name:"张三", city:"西安", age:16} 45 } 46 47 // 3. 匿名结构体 48 func f3() { 49 var s1 struct { 50 name string 51 age int8 52 } 53 s1.name = "李四" 54 s1.age = 16 55 56 fmt.Printf("s1=%v\n", s1) // s1={李四 16} 57 fmt.Printf("s1=%#v\n", s1) // s1=struct { name string; age int8 }{name:"李四", age:16} 58 } 59 60 // 4. 指针类型结构体 61 func f4() { 62 var p1 = new(person) // 使用new关键字对结构体进行实例化,得到的是结构体的地址 63 fmt.Printf("p1:%T\n", p1) // p1:*main.person 64 fmt.Printf("p1:%#v\n", p1) // p1:&main.person{name:"", city:"", age:0} 65 p1.name = "小王子" 66 p1.age = 28 67 p1.city = "上海" // 赋值 注意有语法糖 底层是 (*p1).city 68 fmt.Printf("p1:%#v\n", p1) // p1:&main.person{name:"小王子", city:"上海", age:28} 69 70 p2 := &person{} // 使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。 71 fmt.Printf("p2: %T\n", p2) // p2: *main.person 72 fmt.Printf("p2: %#v\n", p2) // p2: &main.person{name:"", city:"", age:0} 73 74 } 75 76 // 5. 结构体初始化 77 func f5() { 78 // 没有初始化的结构体,其成员变量都是对应其类型的零值 79 var p1 person 80 fmt.Printf("p1:%T\n", p1) // p1:main.person 81 fmt.Printf("p1:%#v\n", p1) // p1:main.person{name:"", city:"", age:0} 82 83 // 使用键值对初始化 可部分初始化,一部分默认零值 84 p2 := person{ 85 name: "王五", 86 age: 17, 87 } 88 fmt.Printf("p2:%#v\n", p2) // p2:main.person{name:"王五", city:"", age:17} 89 90 // 结构体指针键值对初始化 91 p3 := &person{ 92 name: "赵六", 93 } 94 fmt.Printf("p3:%#v\n", p3) // p3:&main.person{name:"赵六", city:"", age:0} 95 96 // 使用值列表初始化 字段、顺序必须一一对应 97 p4 := &person{ 98 "隔壁老王", 99 "隔壁", 100 35, 101 } 102 fmt.Printf("p4:%#v\n", p4) // p4:&main.person{name:"隔壁老王", city:"隔壁", age:35} 103 104 } 105 106 // 6. 结构体内存布局 结构体占用一块连续的内存。 107 func f6() { 108 p := test{ 109 1, 2, 3, 4, 110 } 111 fmt.Printf("type of p: %T\n", p) // type of p: main.test 112 fmt.Printf("val of p: %#v\n", p) // val of p: main.test{a:1, b:2, c:3, d:4} 113 fmt.Printf("p.a: %v\n", &p.a) // p.a: 0xc000014190 114 fmt.Printf("p.b: %v\n", &p.b) // p.a: 0xc000014191 115 fmt.Printf("p.c: %p\n", &p.c) // p.a: 0xc000014192 116 fmt.Printf("p.d: %p\n", &p.d) // p.a: 0xc000014193 117 118 // 传值 与 传地址 119 p1 := &test{ 120 5, 6, 7, 8, 121 } 122 fmt.Printf("type of p1: %T\n", p1) // type of p1: *main.test 123 fmt.Printf("val of p1: %#v\n", p1) // val of p1: &main.test{a:5, b:6, c:7, d:8} 124 fmt.Printf("p1.a: %#v\n", &p1.a) // p1.a: (*int8)(0xc0000141c0) 125 fmt.Printf("p1.b: %#v\n", &p1.b) // p1.b: (*int8)(0xc0000141c1) 126 fmt.Printf("p1.c: %#p\n", &p1.c) // p1.c: c0000141c2 127 fmt.Printf("p1.d: %#p\n", &p1.d) // p1.d: c0000141c3 %p 前加 # 没用 128 } 129 130 // 7. 空结构体 不占空间 131 func f7() { 132 var v struct{} 133 var s = []int{1, 2, 3} 134 fmt.Println(s) // [1 2 3] 135 fmt.Println(unsafe.Sizeof(v)) // 0 136 fmt.Println(unsafe.Sizeof(s)) // 24 137 } 138 139 // 8. 面试题 140 func f8() { 141 m := make(map[string]*student) 142 stus := []student{ 143 {name: "小王子", age: 18}, 144 {name: "娜扎", age: 23}, 145 {name: "大王八", age: 9000}, 146 } 147 148 fmt.Println(stus) // [{小王子 18} {娜扎 23} {大王八 9000}] 149 fmt.Printf("%#v\n", m) // map[string]*main.student{} 150 for _, stu := range stus { 151 fmt.Println(stu.name, &stu) // &{小王子 18 &{娜扎 23} &{大王八 9000} 152 fmt.Printf("%p\n", &stu) // 0xc0001120c0 地址是不变的 153 m[stu.name] = &stu 154 } 155 156 fmt.Println(m) // map[大王八:0xc0000040d8 娜扎:0xc0000040d8 小王子:0xc0000040d8] 157 for k, v := range m { 158 fmt.Println(k, "=>", v.name) 159 // 小王子 => 大王八 160 // 娜扎 => 大王八 161 // 大王八 => 大王八 162 163 } 164 } 165 166 // 9. 构造函数 167 func newPerson(name, city string, age int8) *person { 168 return &person{ 169 name: name, 170 city: city, 171 age: age, 172 } 173 } 174 175 func f9() { 176 var p1 = newPerson("张三", "西安", 16) 177 fmt.Printf("%#v\n", p1) // &main.person{name:"张三", city:"西安", age:16} 178 } 179 180 // 10. 方法和接收者 方法属于特定的类型 181 func (p person) Dream() { 182 fmt.Printf("%s的梦想是学号Go语言\n", p.name) 183 p.age = 100 // 值类型的接收者,修改的是副本 184 fmt.Printf("修改副本:%v\n", p) // 修改副本:{小明 西安 100} 185 } 186 187 func (p *person) setAge(newAge int8) { 188 p.age = newAge // 方法类型的接收者,修改原值 189 } 190 191 func f10() { 192 var p1 = newPerson("小明", "西安", 10) 193 p1.Dream() 194 fmt.Printf("Dream后:%v\n", p1) // Dream后:&{小明 西安 10} 195 p1.setAge(11) 196 fmt.Printf("setAge后:%v\n", p1) // setAge后:&{小明 西安 11} 197 } 198 199 // 11. 结构体匿名字段 200 type anonymousPerson struct { 201 string 202 int8 203 } 204 205 func f11() { 206 p1 := anonymousPerson{ 207 "小王子", 208 18, 209 } 210 fmt.Printf("%#v\n", p1) // main.anonymousPerson{string:"小王子", int8:18} 211 // 匿名字段的说法并不代表没有字段名,而是默认会采用类型名作为字段名, 212 // 结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。 213 } 214 215 // 12. 嵌套结构体 216 type Address struct { 217 Provice string 218 City string 219 } 220 221 type User struct { 222 Name string 223 Age int8 224 Address Address 225 } 226 227 type User1 struct { 228 Name string 229 Age int8 230 Address // 匿名字段 231 } 232 233 func f12() { 234 p1 := User{ 235 Name: "隔壁老王", 236 Age: 30, 237 Address: Address{ 238 Provice: "陕西", 239 City: "西安", 240 }, 241 } 242 fmt.Println(p1) // {隔壁老王 30 {陕西 西安}} 243 fmt.Printf("%#v\n", p1) // main.User{Name:"隔壁老王", Age:30, Address:main.Address{Provice:"陕西", City:"西安"}} 244 245 var p2 User1 246 p2.Name = "小红" 247 p2.Age = 28 248 p2.Address.Provice = "陕西" // 匿名字段默认使用类型名作为字段名 249 p2.City = "商洛" // 匿名字段可以省略, 直接用 p2 250 fmt.Println(p2) // {小红 28 {陕西 西安}} 251 fmt.Printf("%#v\n", p2) // main.User1{Name:"小红", Age:28, Address:main.Address{Provice:"陕西", City:"商洛"}} 252 } 253 254 // 13. 结构体的继承 255 type Animal struct { 256 name string 257 } 258 259 func (a *Animal) Move() { 260 fmt.Printf("%s 会跑\n", a.name) 261 } 262 263 type Dog struct { 264 feet int 265 *Animal // 通过结构体匿名字段实现继承 (字段,方法) 266 } 267 268 func (d *Dog) Bark() { 269 // 自己的方法 270 fmt.Printf("%s 会汪汪汪的叫\n", d.name) 271 } 272 273 func f13() { 274 d := &Dog{ 275 feet: 4, 276 Animal: &Animal{ 277 name: "吉娃娃", 278 }, 279 } 280 d.Move() // 吉娃娃 会跑 281 d.Bark() // 吉娃娃 会汪汪汪的叫 282 } 283 284 // 14. 结构体与JSON序列化 285 type Student1 struct { 286 ID int 287 Name string 288 } 289 290 type Class struct { 291 Name string 292 Students []*Student1 293 } 294 295 func f14() { 296 cls := &Class{ // 班级初始化 297 Name: "三年级一班", 298 Students: make([]*Student1, 0, 200), // 学生初始化 299 } 300 301 for i := 0; i < 3; i++ { 302 stu := &Student1{ 303 ID: i, 304 Name: fmt.Sprintf("stu%02d", i), 305 } 306 cls.Students = append(cls.Students, stu) 307 } 308 309 // JSON序列化 310 data, err := json.Marshal(cls) 311 if err != nil { 312 fmt.Println("json序列化失败") 313 return 314 } 315 fmt.Printf("json: %s\n\n", data) 316 /* 317 json: { 318 "Name":"三年级一班", 319 "Students":[ 320 {"ID":0,"Name":"stu00"}, 321 {"ID":1,"Name":"stu01"}, 322 {"ID":2,"Name":"stu02"} 323 ] 324 } 325 */ 326 327 // JSON 反序列化 328 str := `{"Name":"三年级一班","Students":[{"ID":0,"Name":"stu00"},{"ID":1,"Name":"stu01"},{"ID":2,"Name":"stu02"}]}` 329 cls1 := &Class{} 330 err = json.Unmarshal([]byte(str), cls1) 331 if err != nil { 332 fmt.Println("jsonF反序列化失败") 333 return 334 } 335 fmt.Printf("%#v\n", cls1) 336 /* 337 &main.Class{ 338 Name:"三年级一班", 339 Students:[]*main.Student1{ 340 (*main.Student1)(0xc0000a61f8), 341 (*main.Student1)(0xc0000a6210), 342 (*main.Student1)(0xc0000a6228) 343 } 344 } 345 */ 346 347 } 348 349 // 15. 注意 因为slice和map这两种数据类型都包含了指向底层数据的指针,因此我们在需要复制它们时要特别注意 350 type Person2 struct { 351 name string 352 age int8 353 dreams []string 354 } 355 356 // 错误的做法 357 func (p *Person2) SetDreams(dreams []string) { 358 p.dreams = dreams 359 } 360 361 // 正确的做法 362 func (p *Person2) SetDreams1(dreams []string) { 363 p.dreams = make([]string, len(dreams)) 364 copy(p.dreams, dreams) 365 } 366 367 func f15() { 368 p1 := Person2{name: "小王子", age: 18} 369 data := []string{"吃饭", "睡觉", "打豆豆"} 370 p1.SetDreams(data) // p1.SetDreams1(data) 371 372 // 你真的想要修改 p1.dreams 吗? 373 data[1] = "不睡觉" 374 fmt.Println(p1.dreams) // [吃饭 不睡觉 打豆豆] 并没有 setDreams,但 dreams 却改了 375 } 376 377 func main() { 378 // f1() 379 // f2() 380 // f3() 381 // f4() 382 // f5() 383 // f6() 384 // f7() 385 // f8() 386 // f9() 387 // f10() 388 // f11() 389 // f12() 390 // f13() 391 // f14() 392 f15() 393 }