go学习之结构体
go中的结构体类似python的类
结构体定义type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
例如:
type p struct {
name string
city string
age int8
}
结构体实列化 只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。 结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型 格式如下: var 结构体实例 结构体类型 例子: var p1 p p1.name = "张三" p1.city = "北京" p1.age = 18
创建指针类型结构体
可以使用 new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体 通过使用new关键字对结构体进行实例化,得到的是结构体的地址。 格式如下: var p2 = new(p) 需要注意的是在Go语言中支持对结构体指针直接使用.来访问结构体的成员 例如: var p1=new(p) fmt.Printf("%T\n", p1)//*main.p p1.name="小王" fmt.Printf("%s\n", p1.name)
取结构体的地址实例化 使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。 p2 := &p{} fmt.Printf("%T\n", p2)//*main.p p2.name="小王" fmt.Printf("%s\n", p2.name)//小王 p2.name = "小王"其实在底层是(*p2).name = "小王",这是Go语言帮我们实现的语法糖。
结构体初始化 1、定义未初始化时其成员变量都是对应其类型的零值 2、使用键值初始化 例如: p1 := p{ name: "lisi", city: "北京", } 3、使用值的列表初始化 例如: p1 := p{ "lisi", "北京", }
构造函数 Go语言的结构体没有构造函数,可以自己实现 struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型
构造函数也是用来对结构体进行实列化 func newPerson(name, city string) *person { return &person{ name: name, city: city, } } 调用 p1 := newPerson("张三", "上海")
Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver) 方法定义格式如下: func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { 函数体 } 说明: 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型 方法名、参数列表、返回参数:具体格式与函数定义相同 例子: type Person struct { name string age int8 } func (p Person) Dream() { fmt.Printf("%s的梦想是学好Go语言!\n", p.name) }
指针类型的接收者VS值类型的接收者 // 使用指针接收者 func (p *Person) SetAge(newAge int8) { p.age = newAge } func (p Person) SetAge2(newAge int8) { p.age = newAge } 说明 当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身 指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的
练习:函数版简单学生管理系统
package main import "fmt" var student_info map[int]string func add_student(name string,id int){ student_info[id]=name fmt.Println("add student success") } func show_student(){ for k,v := range student_info{ fmt.Println(k, v) } } func del_student(){ fmt.Println("请输入要删除的学生id:") var s_id int fmt.Scan(&s_id) delete(student_info,s_id) fmt.Println("现有学生如下:") show_student() } func ShowMessage() int { fmt.Println("要执行的操作:") fmt.Print(` 1. 添加 2. 查看 3. 删除 `) var do int _, err := fmt.Scan(&do) if err != nil { fmt.Println("输入有误!") } return do } func main() { student_info=make(map[int]string,10) for { num :=ShowMessage() switch num { case 1 : fmt.Println("你选择的是:添加") var id int fmt.Println("请输入id:") fmt.Scan(&id) var name string fmt.Println("name:") fmt.Scan(&name) add_student(name,id) case 2 : fmt.Println("你选择的是:查看") fmt.Println("学生信息如下:") show_student() case 3 : fmt.Println("你选择的是:删除") del_student() default : fmt.Println("你选择有误请重新选择") } } }
执行效果 go run student.go 要执行的操作: 1. 添加 2. 查看 3. 删除 1 你选择的是:添加 请输入id: 1 name: zhangsan add student success 要执行的操作: 1. 添加 2. 查看 3. 删除 2 你选择的是:查看 学生信息如下: 1 zhangsan 要执行的操作: 1. 添加 2. 查看 3. 删除 3 你选择的是:删除 请输入要删除的学生id: 1 现有学生如下: 要执行的操作: 1. 添加 2. 查看 3. 删除
结构体“继承”
结构体与JSON序列化和反序列化
//JSON序列化:结构体-->JSON格式的字符串 data, err := json.Marshal(s) if err != nil { fmt.Println("json marshal failed") return } //JSON反序列化:JSON格式的字符串-->结构体 err = json.Unmarshal([]byte(str), c1) if err != nil { fmt.Println("json unmarshal failed!") return }
结构体Tag type Student struct { ID int `json:"id"` //通过指定tag实现json序列化该字段时的key Gender string //json序列化是默认使用字段名作为key name string //私有不能被json包访问 }
练习:结构体版学生管理
package main import ( "fmt" "os" ) func showMenu(){ fmt.Println("welcomm sms!") fmt.Println(` 1. 查看学生 2. 添加学生 3. 修改学生 4. 删除学生 5. 退出 `) } //定义一个学生结构体,用来保存单个学生信息 type student struct{ id int name string } //定义一个学生管理的结构体,包含各种学生管理相关的方法 type studentMgr struct { allStudent map[int]student //定义一个allStudent map,其中键是int,值是student的结构体(即上面定义的) } //查看学生信息 func (s studentMgr) showStudent() { fmt.Println("学生信息如下:") for _,v :=range s.allStudent { fmt.Println(v.id, v.name) } } //添加学生 func (s studentMgr) addStudent() { var stuId int var stuName string fmt.Print("请输入学号:") fmt.Scanln(&stuId)//获取指针以便修改值 fmt.Print("请输入姓名:") fmt.Scanln(&stuName) newstu := student{ id: stuId, name: stuName, } s.allStudent[newstu.id] = newstu } //修改 func (s studentMgr) editStudent() { fmt.Println("请输入学生的学号") var stuId int fmt.Scanln(&stuId) v1, ok := s.allStudent[stuId] // 直接使用 map 的key 就能取值 if !ok { fmt.Println("查无此人") return } fmt.Println("将修改学生的名字", v1.name) fmt.Print("请输入学生的新名字") var newName string fmt.Scanln(&newName) v1.name = newName // 直接进行更改 s.allStudent[stuId] = v1 // 再更改到map 里 fmt.Print("学生信息修改成功") } //删除学生 func (s studentMgr) deleteStudent() { fmt.Println("请输入你要删除学生的学号") var studID int fmt.Scanln(&studID) // 要删除学生的ID delete(s.allStudent, studID) fmt.Println("删除成功") } // var smr studentMgr func main(){ smr = studentMgr{ allStudent: make(map[int]student), } for { showMenu() fmt.Println("请输入你的操作") var choice int fmt.Scanln(&choice) switch choice { case 1: smr.showStudent() case 2: smr.addStudent() case 3: smr.editStudent() case 4: smr.deleteStudent() case 5: os.Exit(1) default: fmt.Println("选择错误,请重新选择") } } }
执行结果 welcomm sms! 1. 查看学生 2. 添加学生 3. 修改学生 4. 删除学生 5. 退出 请输入你的操作 2 请输入学号:001 请输入姓名:root welcomm sms! 1. 查看学生 2. 添加学生 3. 修改学生 4. 删除学生 5. 退出 请输入你的操作 1 学生信息如下: 1 root welcomm sms! 1. 查看学生 2. 添加学生 3. 修改学生 4. 删除学生 5. 退出 请输入你的操作 2 请输入学号:002 请输入姓名:admin welcomm sms! 1. 查看学生 2. 添加学生 3. 修改学生 4. 删除学生 5. 退出 请输入你的操作 3 请输入学生的学号 001 将修改学生的名字 root 请输入学生的新名字root1 学生信息修改成功welcomm sms! 1. 查看学生 2. 添加学生 3. 修改学生 4. 删除学生 5. 退出 请输入你的操作 4 请输入你要删除学生的学号 001 删除成功