Go基础(结构体)
package main
import (
"encoding/json"
"fmt"
"unsafe"
)
// 1. 自定义变量类型
type newInt int
type myInt = int
// 2. 定义结构体
type person struct {
name, city string
age int8
}
type student struct {
name string
age int
}
type test struct {
a int8
b int8
c int8
d int8
}
func f1() {
var a newInt
var b myInt
fmt.Printf("type a: %T\n", a) // type a: main.newInt
fmt.Printf("type b: %T\n", b) // type b: int
}
func f2() {
var p1 person
p1.name = "张三"
p1.city = "西安"
p1.age = 16
fmt.Printf("p1=%v\n", p1) // p1={张三 西安 16}
fmt.Printf("p1=%#v\n", p1) // p1=main.person{name:"张三", city:"西安", age:16}
}
// 3. 匿名结构体
func f3() {
var s1 struct {
name string
age int8
}
s1.name = "李四"
s1.age = 16
fmt.Printf("s1=%v\n", s1) // s1={李四 16}
fmt.Printf("s1=%#v\n", s1) // s1=struct { name string; age int8 }{name:"李四", age:16}
}
// 4. 指针类型结构体
func f4() {
var p1 = new(person) // 使用new关键字对结构体进行实例化,得到的是结构体的地址
fmt.Printf("p1:%T\n", p1) // p1:*main.person
fmt.Printf("p1:%#v\n", p1) // p1:&main.person{name:"", city:"", age:0}
p1.name = "小王子"
p1.age = 28
p1.city = "上海" // 赋值 注意有语法糖 底层是 (*p1).city
fmt.Printf("p1:%#v\n", p1) // p1:&main.person{name:"小王子", city:"上海", age:28}
p2 := &person{} // 使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。
fmt.Printf("p2: %T\n", p2) // p2: *main.person
fmt.Printf("p2: %#v\n", p2) // p2: &main.person{name:"", city:"", age:0}
}
// 5. 结构体初始化
func f5() {
// 没有初始化的结构体,其成员变量都是对应其类型的零值
var p1 person
fmt.Printf("p1:%T\n", p1) // p1:main.person
fmt.Printf("p1:%#v\n", p1) // p1:main.person{name:"", city:"", age:0}
// 使用键值对初始化 可部分初始化,一部分默认零值
p2 := person{
name: "王五",
age: 17,
}
fmt.Printf("p2:%#v\n", p2) // p2:main.person{name:"王五", city:"", age:17}
// 结构体指针键值对初始化
p3 := &person{
name: "赵六",
}
fmt.Printf("p3:%#v\n", p3) // p3:&main.person{name:"赵六", city:"", age:0}
// 使用值列表初始化 字段、顺序必须一一对应
p4 := &person{
"隔壁老王",
"隔壁",
35,
}
fmt.Printf("p4:%#v\n", p4) // p4:&main.person{name:"隔壁老王", city:"隔壁", age:35}
}
// 6. 结构体内存布局 结构体占用一块连续的内存。
func f6() {
p := test{
1, 2, 3, 4,
}
fmt.Printf("type of p: %T\n", p) // type of p: main.test
fmt.Printf("val of p: %#v\n", p) // val of p: main.test{a:1, b:2, c:3, d:4}
fmt.Printf("p.a: %v\n", &p.a) // p.a: 0xc000014190
fmt.Printf("p.b: %v\n", &p.b) // p.a: 0xc000014191
fmt.Printf("p.c: %p\n", &p.c) // p.a: 0xc000014192
fmt.Printf("p.d: %p\n", &p.d) // p.a: 0xc000014193
// 传值 与 传地址
p1 := &test{
5, 6, 7, 8,
}
fmt.Printf("type of p1: %T\n", p1) // type of p1: *main.test
fmt.Printf("val of p1: %#v\n", p1) // val of p1: &main.test{a:5, b:6, c:7, d:8}
fmt.Printf("p1.a: %#v\n", &p1.a) // p1.a: (*int8)(0xc0000141c0)
fmt.Printf("p1.b: %#v\n", &p1.b) // p1.b: (*int8)(0xc0000141c1)
fmt.Printf("p1.c: %#p\n", &p1.c) // p1.c: c0000141c2
fmt.Printf("p1.d: %#p\n", &p1.d) // p1.d: c0000141c3 %p 前加 # 没用
}
// 7. 空结构体 不占空间
func f7() {
var v struct{}
var s = []int{1, 2, 3}
fmt.Println(s) // [1 2 3]
fmt.Println(unsafe.Sizeof(v)) // 0
fmt.Println(unsafe.Sizeof(s)) // 24
}
// 8. 面试题
func f8() {
m := make(map[string]*student)
stus := []student{
{name: "小王子", age: 18},
{name: "娜扎", age: 23},
{name: "大王八", age: 9000},
}
fmt.Println(stus) // [{小王子 18} {娜扎 23} {大王八 9000}]
fmt.Printf("%#v\n", m) // map[string]*main.student{}
for _, stu := range stus {
fmt.Println(stu.name, &stu) // &{小王子 18 &{娜扎 23} &{大王八 9000}
fmt.Printf("%p\n", &stu) // 0xc0001120c0 地址是不变的
m[stu.name] = &stu
}
fmt.Println(m) // map[大王八:0xc0000040d8 娜扎:0xc0000040d8 小王子:0xc0000040d8]
for k, v := range m {
fmt.Println(k, "=>", v.name)
// 小王子 => 大王八
// 娜扎 => 大王八
// 大王八 => 大王八
}
}
// 9. 构造函数
func newPerson(name, city string, age int8) *person {
return &person{
name: name,
city: city,
age: age,
}
}
func f9() {
var p1 = newPerson("张三", "西安", 16)
fmt.Printf("%#v\n", p1) // &main.person{name:"张三", city:"西安", age:16}
}
// 10. 方法和接收者 方法属于特定的类型
func (p person) Dream() {
fmt.Printf("%s的梦想是学号Go语言\n", p.name)
p.age = 100 // 值类型的接收者,修改的是副本
fmt.Printf("修改副本:%v\n", p) // 修改副本:{小明 西安 100}
}
func (p *person) setAge(newAge int8) {
p.age = newAge // 方法类型的接收者,修改原值
}
func f10() {
var p1 = newPerson("小明", "西安", 10)
p1.Dream()
fmt.Printf("Dream后:%v\n", p1) // Dream后:&{小明 西安 10}
p1.setAge(11)
fmt.Printf("setAge后:%v\n", p1) // setAge后:&{小明 西安 11}
}
// 11. 结构体匿名字段
type anonymousPerson struct {
string
int8
}
func f11() {
p1 := anonymousPerson{
"小王子",
18,
}
fmt.Printf("%#v\n", p1) // main.anonymousPerson{string:"小王子", int8:18}
// 匿名字段的说法并不代表没有字段名,而是默认会采用类型名作为字段名,
// 结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。
}
// 12. 嵌套结构体
type Address struct {
Provice string
City string
}
type User struct {
Name string
Age int8
Address Address
}
type User1 struct {
Name string
Age int8
Address // 匿名字段
}
func f12() {
p1 := User{
Name: "隔壁老王",
Age: 30,
Address: Address{
Provice: "陕西",
City: "西安",
},
}
fmt.Println(p1) // {隔壁老王 30 {陕西 西安}}
fmt.Printf("%#v\n", p1) // main.User{Name:"隔壁老王", Age:30, Address:main.Address{Provice:"陕西", City:"西安"}}
var p2 User1
p2.Name = "小红"
p2.Age = 28
p2.Address.Provice = "陕西" // 匿名字段默认使用类型名作为字段名
p2.City = "商洛" // 匿名字段可以省略, 直接用 p2
fmt.Println(p2) // {小红 28 {陕西 西安}}
fmt.Printf("%#v\n", p2) // main.User1{Name:"小红", Age:28, Address:main.Address{Provice:"陕西", City:"商洛"}}
}
// 13. 结构体的继承
type Animal struct {
name string
}
func (a *Animal) Move() {
fmt.Printf("%s 会跑\n", a.name)
}
type Dog struct {
feet int
*Animal // 通过结构体匿名字段实现继承 (字段,方法)
}
func (d *Dog) Bark() {
// 自己的方法
fmt.Printf("%s 会汪汪汪的叫\n", d.name)
}
func f13() {
d := &Dog{
feet: 4,
Animal: &Animal{
name: "吉娃娃",
},
}
d.Move() // 吉娃娃 会跑
d.Bark() // 吉娃娃 会汪汪汪的叫
}
// 14. 结构体与JSON序列化
type Student1 struct {
ID int
Name string
}
type Class struct {
Name string
Students []*Student1
}
func f14() {
cls := &Class{ // 班级初始化
Name: "三年级一班",
Students: make([]*Student1, 0, 200), // 学生初始化
}
for i := 0; i < 3; i++ {
stu := &Student1{
ID: i,
Name: fmt.Sprintf("stu%02d", i),
}
cls.Students = append(cls.Students, stu)
}
// JSON序列化
data, err := json.Marshal(cls)
if err != nil {
fmt.Println("json序列化失败")
return
}
fmt.Printf("json: %s\n\n", data)
/*
json: {
"Name":"三年级一班",
"Students":[
{"ID":0,"Name":"stu00"},
{"ID":1,"Name":"stu01"},
{"ID":2,"Name":"stu02"}
]
}
*/
// JSON 反序列化
str := `{"Name":"三年级一班","Students":[{"ID":0,"Name":"stu00"},{"ID":1,"Name":"stu01"},{"ID":2,"Name":"stu02"}]}`
cls1 := &Class{}
err = json.Unmarshal([]byte(str), cls1)
if err != nil {
fmt.Println("jsonF反序列化失败")
return
}
fmt.Printf("%#v\n", cls1)
/*
&main.Class{
Name:"三年级一班",
Students:[]*main.Student1{
(*main.Student1)(0xc0000a61f8),
(*main.Student1)(0xc0000a6210),
(*main.Student1)(0xc0000a6228)
}
}
*/
}
// 15. 注意 因为slice和map这两种数据类型都包含了指向底层数据的指针,因此我们在需要复制它们时要特别注意
type Person2 struct {
name string
age int8
dreams []string
}
// 错误的做法
func (p *Person2) SetDreams(dreams []string) {
p.dreams = dreams
}
// 正确的做法
func (p *Person2) SetDreams1(dreams []string) {
p.dreams = make([]string, len(dreams))
copy(p.dreams, dreams)
}
func f15() {
p1 := Person2{name: "小王子", age: 18}
data := []string{"吃饭", "睡觉", "打豆豆"}
p1.SetDreams(data) // p1.SetDreams1(data)
// 你真的想要修改 p1.dreams 吗?
data[1] = "不睡觉"
fmt.Println(p1.dreams) // [吃饭 不睡觉 打豆豆] 并没有 setDreams,但 dreams 却改了
}
func main() {
// f1()
// f2()
// f3()
// f4()
// f5()
// f6()
// f7()
// f8()
// f9()
// f10()
// f11()
// f12()
// f13()
// f14()
f15()
}