1.初识结构体(struct)
参考李文周老师视频 https://www.liwenzhou.com/
package main
import "fmt"
//结构体
//解决的问题:没有办法在代码里表示具有多维度数据的个体
//构建一个person类型
type person struct {
name string
age int
gender string
hobby []string
}
func main() {
var p person //声明person类型变量
p.name = "lf"
p.age = 21
p.gender = "nan"
p.hobby = []string{"篮球","羽毛球"}
fmt.Println(p)
fmt.Printf("%T",p)
}
2.匿名结构体
/ 匿名结构体:多用于临时场景
//type定义类型 var 声明变量
var s struct{
name string
age int
}
s.name = "lf"
s.age = 21
fmt.Println(s)
fmt.Printf("%T",s)
结构体指针和结构体初始化
package main
import "fmt"
//结构体是值类型
type person struct {
name,gender string //字段类型简写
}
//go语言函数传参永远是拷贝(副本)
func f(s person) {
s.gender = "男"
}
//传递指针地址
func f2(s *person) {
s.gender = "男"
}
func main() {
var p person
p.name = "晓燕"
p.gender = "女"
f(p)
fmt.Println(p)
f2(&p)
fmt.Println(p)
//结构体指针1
//还可以使用new关键字对结构体进行实例化,得到的是结构体的地址
var p2 = new(person) //new(person)就是创建一个内存地址
p2.name = "老男孩"
fmt.Printf("%T\n",p2)
fmt.Printf("%p\n",p2) //p2保存的值就是一个内存地址 比如"老男孩"的内存地址
fmt.Println(&p2) //p2的内存地址
//主要使用这种方式 结构体指针2 可以key-value,也可以省略key,顺序须一致
var p3 = &person{
"lf",
"男",
}
fmt.Printf("%T\n",p3)
fmt.Printf("%#v\n",p3)
fmt.Println(&p3.name,&p3.gender)
}
结构体占用一块连续的内存空间
//结构体占用一块连续的内存空间
type x struct {
a int8
b int8
c string
}
func main() {
m := x{
1,
2,
"3",
}
fmt.Println(&(m.a),&(m.b),&(m.c))
}
方法和接收者
Go语言中的方法(Method)
是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)
。接收者的概念就类似于其他语言中的this
或者 self
。
方法的定义格式如下:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
type dog struct {
name string
}
// 方法是作用域特定类型的函数
//接收者表示的是调用该方法的具体类型变量,多用类型首字母小写表示
func (d dog) wang() {
fmt.Println(d.name + "旺旺")
}
//构造函数
func newDog(name string)dog {
return dog{
name,
}
}
func main() {
d1 := newDog("lf")
d1.wang()
}
值接收者和指针接收者的区别
package main
import "fmt"
//标识符:变量,变量名,函数名,方法名
//GO语言中标识符首字母是大写的,就表示对外部包可见(暴露的,公有的)
//type Dog struct
type dog struct {
name string
}
type person struct {
name string
age int
}
// 方法是作用域特定类型的函数
//2.接收者表示的是调用该方法的具体类型变量,多用类型首字母小写表示
func (d dog) wang() {
fmt.Println(d.name + "旺旺")
}
//3.值接收和指针接收相差一个*
func (p *person) guonian() {
p.age ++
}
func (p *person) dream() {
fmt.Println("make money")
}
//1.构造函数
func newDog(name string)dog {
return dog{
name,
}
}
func newPeople(name string,age int)person {
return person{
name,
age,
}
}
func main() {
d1 := newDog("lf")
d1.wang()
p1 := newPeople("lf",12)
p1.guonian()
fmt.Println(p1.age)
p1.guonian()
fmt.Println(p1.age)
p1.dream()
}
任意类型添加方法
package main
import "fmt"
//给自定义类型添加方法
//不能给别的包里面的类型添加方法,只能给自己包里的类型添加方法
//如果一定要自定义,那么就自定义一个包就可以了
type myInt int
func (m myInt)hello() {
fmt.Println("I\"m int")
}
func main() {
m := myInt(100)
m.hello()
}
函数版学生管理系统
package main
import (
"fmt"
"os"
)
//学生管理系统
/*
函数版
查看\新增\删除
*/
var (
allStudent = make(map[int64]*student,50) //初始化(开辟内存空间)
)
type student struct {
id int64
name string
}
//student类型的构造函数
func newStudent(id int64,name string) *student {
return &student{
id,
name,
}
}
func showAllStudent(){
//遍历所有学生
for k,v := range allStudent{
fmt.Printf("学号:%d 姓名:%s\n",k,v.name)
}
}
func addStudent() {
//向allStudent中添加一个新的学生
//1.创建一个新学生
//1.1获取用户输入
var (
id int64
name string
)
fmt.Println("请输入学生学号:")
fmt.Scanln(&id)
fmt.Println("请输入学生姓名:")
fmt.Scanln(&name)
//1.2创造一个学生()调用构造函数
newStu := newStudent(id,name)
//2.追加到allStudent map中
allStudent[id] = newStu
}
func deleteStudent() {
//1.输入删除学生的学号
var(
deleteId int64
)
fmt.Println("请输入要删除学生学号:")
//2.去allStudent这个map中根据学号删除对于键值对
fmt.Scanln(&deleteId)
delete(allStudent,deleteId)
}
func main() {
for{
//1.打印菜单
fmt.Println("欢迎光临学生管理系统!")
fmt.Println(`
1.查看所有学生
2.新增学生
3.删除学生
4.退出
`)
fmt.Print("请输入你的指令")
//2.等待用户选择做什么
var choice int
fmt.Scanln(&choice)
fmt.Printf("你选择了%d这个选项\n",choice)
//3.执行对应的函数
switch choice {
case 1:
showAllStudent()
case 2:
addStudent()
case 3:
deleteStudent()
case 4:
os.Exit(1) //break仅能退出当前switch循环
default:
fmt.Println("滚~")
}
}
}
结构体的匿名字段和结构体嵌套
//匿名字段
//字段比较少而且简单 不常用
type person struct {
string
int
}
func main() {
p1 := person{
"lf",
10,
}
fmt.Println(p1.string)
fmt.Println(p1.int)
}
package main
import "fmt"
//结构体嵌套
type address struct {
province string
city string
}
type workPlace struct {
province string
city string
}
type person struct {
name string
age int
address //匿名嵌套结构体
//addr address
workPlace
}
type company struct {
name string
addr address
}
func main() {
p1 := person{
"lf",
21,
address{
"湖北",
"武汉",
},
workPlace{
"山东",
"潍坊",
},
}
//1. fmt.Println(p1.addr.province)
//2. fmt.Println(p1.city) //先在自己结构体找这个字段,找不到就去匿名嵌套的结构体中查找该字段
fmt.Println(p1.address.city) //匿名嵌套结构体冲突
}
模拟实现继承
package main
import "fmt"
//结构体模拟继承
type animal struct {
name string
}
func (a animal) move() {
fmt.Println(a.name, "moving")
}
//dog 类
type dog struct {
feet uint8
animal //dog结构体把animal结构体完全包裹,animal拥有的方法dog也拥有
}
//给dog实现一个汪汪汪的方法
func (d dog) wang() {
fmt.Println(d.name,"wanging")
}
func main() {
d1 := dog{
animal:animal{
name:"yy",
},
feet: 4,
}
fmt.Println(d1)
d1.wang()
d1.move()
}
结构体与JSON
经常出现问题:
1.结构体内部的字段要大写!!!不大写别人访问不到
2.反序列化时要传递指针
package main
import (
"encoding/json"
"fmt"
)
//结构体与json
// 1.序列化: go语言中的结构体变量 ->json格式的字符串()
// 2.反序列化: json格式的字符串 -> Go语言能够识别的结构体变量
type person struct {
//需要大写 json包拿不到小写的内容
Name string `json:"name" db:"name" "ini":"name"`
Age int `json:"age"`
}
func main() {
p1 := person{
Name: "lf",
Age: 21,
}
fmt.Println(p1)
//go语言中的结构体变量 ->json格式的字符串()
//json.Marshal(p1)
b,err := json.Marshal(p1)
if err != nil{
fmt.Printf("marshal failed ,err %v",err)
return
}
fmt.Println(string(b))
//反序列化
str := `{"Name":"lf","Age":21}`
var p2 person
//指针是为了能在json.Unmarshal内部修改p2的值
json.Unmarshal([]byte(str),&p2)
fmt.Printf("%#v\n",p2)
}
总结
package main
import (
"encoding/json"
"fmt"
)
type person struct {
name string
age int
}
//构造(结构体变量)函数
func newPerson(name string,age int) person {
return person{
name,
age,
}
}
//方法
//接收者:使用对应类型的首字母小写
//指定了接收者之后,只有接收这个类型的变量才能调用这个方法
func (p person) dream(str string) {
fmt.Printf("%s的梦想是%s\n",p.name,str)
}
//*person 是内存地址,指针接收者
//1.需要修改结构体时的值要使用指针接收者
//2.结构体本身叫较大,拷贝的内存开销大时,使用指针接收者
//3.保持一致性:如果有一个方法使用了指针接收者,其它的方法为了统一也要使用指针接收者
func (p *person) guonian() {
p.age ++ //若是 p person 此处的p是副本
}
func main() {
var x int
p := int(10)
fmt.Println(x,p)
var p1 person
p1.name = "my"
p1.age = 22
p2 := person{ //结构体实例化
"lf",
21,
}
//调用构造函数生成perosn类型变量
p3 := newPerson("帅哥",21)
fmt.Println(p1,p2,p3)
p2.dream("说话")
p3.dream("打架")
p3.guonian()
fmt.Println(p3.age)
//结构体嵌套
type addr struct {
province string
city string
}
type student struct {
name string
addr //匿名嵌套别的结构体
}
type point struct {
X int `json:"x"`
Y int `json:"y"`
}
pq := point{100,200}
//序列化
b,err := json.Marshal(pq)
//如果出错了
if err != nil{
fmt.Printf("marshal failed,err:%v\n",err)
}
fmt.Println(string(b))
// 反序列化
str1 := `{"x":100,"y":200}`
var ps point
err = json.Unmarshal([]byte(str1),&ps) //相当于修改ps的值,
if err != nil{
fmt.Printf("unmarshal failed,%v\n",ps)
}
fmt.Println(ps)
}
两两对应
定义方法
错误
结构体版学生管理系统
//main
package main
import (
"fmt"
"os"
)
//菜单函数
var smr studentManager //声明一个全局学生管理对象:smr
func showMenu() {
fmt.Println("-----------------welocme to sms!------------------")
fmt.Println(
`
1.查看所以学生
2.增加学生
3.修改学生
4.删除学生
5.退出
`)
}
func main() {
smr = studentManager{ //修改的全局的变量
allStudent: make(map[int]student,100),
}
for{
showMenu()
//等待用户输入
fmt.Println("请输入要执行的选项:")
var choice int
fmt.Scanln(&choice)
fmt.Printf("你输入的是%v\n",choice)
switch choice {
case 1:
smr.showStudnets()
case 2:
smr.addStudnets()
case 3:
smr.editStudnets()
case 4:
smr.deleteStudnets()
case 5:
os.Exit(1)
default:
fmt.Println("滚~")
}
}
}
//student_manager
package main
import "fmt"
//学生管理系统
//有一个物件:
//1.它保存了一些数据 --->结构体的字段
//2.它有三个功能 --->结构体的方法
type student struct {
id int
name string
}
//造一个学生的管理者
type studentManager struct {
allStudent map[int]student
}
//查看学生
func (s studentManager) showStudnets() {
//从s.allStudent这个map中把所有的学生逐个打印
for _,stu := range s.allStudent{ //stu是每一个学生student
fmt.Printf("学号:%d 姓名%s\n",stu.id,stu.name)
}
}
//增加学生
func (s studentManager) addStudnets() {
//1.根据用户输入的内容创建一个新的学生
//2.把新的学生放到s.allstudent这个map中
var (
id int
name string
)
//获取用户输入
fmt.Println("请输入学号:")
fmt.Scanln(&id)
fmt.Println("请输入姓名:")
fmt.Scanln(&name)
//根据用户输入创建结构体对象
newStu := student{
id,
name,
}
s.allStudent[newStu.id] = newStu
fmt.Println("添加成功!")
}
//修改学生
func (s studentManager) editStudnets() {
var (
id int
)
//1.获取用户输入的学号
fmt.Println("请输入需要修改的学号:")
fmt.Scanln(&id)
//2.展示该学号对应的学生信息,如果没有提示查无此人
stuObj,ok := s.allStudent[id]
if !ok{
fmt.Println("查无此人!")
return
}
fmt.Printf("你要修改的学生信息如下:学号:%d 姓名:%s",stuObj.id,stuObj.name)
//3.请输入修改后的学生名
fmt.Println("请输入学生的新名字:")
var newName string
fmt.Scanln(&newName)
//4.更新学生姓名
stuObj.name = newName
s.allStudent[id] = stuObj //更新map中的学生
//因为此处的student结构体并不是指针类型的,所以并不能直接 s.allStudent[id].name = newName来实现
//s.allStudent[id].name = newName map元素为结构体时,用指针才能修改字段
}
//删除学生
func (s studentManager) deleteStudnets() {
//1.请用户输入要删除的学生id
var (
id int
)
//1.获取用户输入的学号
fmt.Println("请输入需要删除的学号:")
fmt.Scanln(&id)
//2.去map里面找有没有这个学生,如果没有就打印查无此人,有的话就删除
_,ok := s.allStudent[id]
if !ok{
fmt.Println("查无此人!")
return
}
delete(s.allStudent,id)
fmt.Println("删除成功!")
}
//var (
// allStudent map[int64]student
//)