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 }