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 }