前言
编程就是要通过编程语言表达给计算机,让计算机帮助我们达到解决现实生活问题的目的!
不管是Python还是Golang...这些编程语言,由于历史原因、遇到的痛点、解决的问题不同,导致语法追求、本身特性不同。但是遇到的问题、解决问题的思想是一致的。
面向对象编程 :就是按照自己的理解 尽量把程序里出现的所有东西 抽象得划分为1个个的不同的分类,这些分类中包含自身独有的数据、也有自己独特的方法!
如果想要开发1款游戏,游戏中的人物不仅有角色属性、也有交易、攻击这些作为。
单纯得使用数据类型int、string ..函数去表示1个人物,复杂不利于代码灵活、扩展,于是想办法如何把数据和方法集合到1块进行表示。
Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。
Go语言中通过结构体的内嵌再配合接口 比面向对象具有更高的扩展性和灵活性。
结构体:就是可以把多种不同的基本数据类型,封装到1个整体里面。在golang中这个整体称为结构体。
自定义类型
自定义类型可以对Go中现有的数据类型的方法进行扩展
在Go语言中我们无法直接对在其他包中定义的结构体添加方法,但是可以通过类型别名的方式迂回到达目的。
类型别名
//自定义类型和类型别名 //在Go语言里使用 type声明类型 //type 我 package main import "fmt" type myInt int //自定义类型 type yourInt =int //类型别名 /*类型别名:还记得用于表示英文字符和中文字符的 byte和rune是int的类型别名吗? 就是这个意思yourInt本质上还是int,二者视为同1个类型 为什么会有类型别名? 我们使用int类型声明1个字符变量看起来不贴切于人类的思维,为了代码清晰使用类型别名 rune和bute来表示字符 */ func main() { var n myInt n=100 var m yourInt m=100 var c1 byte //byte是由uint8实现,所以byte是uint8的别名,在Go中使用数字表示字符。二者本质还是uint8 c1='H' var c2 uint8 c2='i' var c3 rune//rune是由int32实现,所以rune是int32的别名,在Go中使用数字表示字符。二者本质还是int32 c3='根' var c4 int32 c4='哥' fmt.Printf("%T\n",n)//main.myInt fmt.Printf("%T\n",m)//int fmt.Printf("%T\n",c1)//uint8 fmt.Printf("%T\n",c2)//uint8 fmt.Printf("%T\n",c3)//int32 fmt.Printf("%T\n",c4)//int32 }
结构体声明
structure结构:顾名思义肯定是由不同的东西组合而成。
在我们写代码的时候如果 需要定义1个由多个基本数据类型组成的数据类型时(例如人有性别string、年龄uint8、爱好得有多个[]string、吃、喝、拉、撒、睡、学习、工作func)创造这种具有多维度的属性时的物时,无法使用单一的数据类型表示全面,所有我们只能使用结构体。
结构体的语法
使用type
和struct
关键字来定义结构体,具体代码格式如下:
type 类型名 struct { 字段名 字段类型 字段名 字段类型 … }
定义1个简单的结构体
package main import "fmt" //定义1个person类的结构体 type person struct{ name string age uint8 married bool hobbies []string education map [string]string } func main() { //声明1个person类型的变量P var p person //给 p赋值 p.name="Abe" p.age=64 p.married=true p.hobbies=[]string{"爱好广泛","涉猎课本以外的世界"} fmt.Println(p) fmt.Printf("%T\n",p)//属于main.person类 //访问变量p的字段 fmt.Println(p.hobbies) }
匿名结构体
在Go语言中如果我们一次性使用结构体的话, 还可以定义 匿名的结构体。
package main import "fmt" func main(){ //定义1个匿名结构体:多应用于临时场景,不使用多次 var Martin struct{ married bool age uint8 } Martin.married=false Martin.age=18 fmt.Printf(" type:%T\ndetails:%#v\n",Martin,Martin) }
方式2
func main() { var struct1 = struct { name string age uint8 }{"Tony", 20} fmt.Println(struct1.name) }
创建指针类型结构体
Go中的结构体不同于Python的class,它是值类型不可以被修改要修改可以用指针,所以Python里面没有指针,使用copy、 deep copy。
package main import "fmt" type person struct{ name string age uint8 } //修改结构体:在Go语言里面参数永远都是副本,无法修改变量p func modify(p person){ p.age=19 } func main(){ var p person p.name="Martin" p.age=18 //开始修改变量p modify(p) //不会影响变量p fmt.Println(p.age)//18 }
通过指针创建1个可以字段可以被修改的结构体
package main import "fmt" type person struct { name string age uint8 } func modifyReally(p *person) { // (*p).age=17//通过指针拿到变量值进行修改 p.age = 17 //Go支持的快捷方式:自动根据指针找到变量 } func main() { var p person p.name = "Martin" p.age = 18 fmt.Println(p.age) //18 //传入1个指针进行修改 modifyReally(&p) fmt.Println(p.age) //17 }
结构体初始化
Go中结构体初始化的方式很多主要有变量赋值、构造函数初始化2种方式,于Python不同的是go中没有自带构造方法,需要自己构建。
//结构体初始化的方式 package main import "fmt" //1.声明1个结构体 type person struct { name string age uint8 } //2.返回值类型struct的构造函数 func newPersonValue(name string, age uint8) person { //别人调用我,我retrun一个person类型的变量(值类型) return person{ name: name, age: age, } } //3.返回指针类型struct的构造函数 func newPerson(name string, age uint8) *person { //别人调用我,我retrun一个person类型的变量的指针 return &person{ name: name, age: age, } } //4.初始化struct func main() { //方法1:通过变量声明然后赋值初始化 var p person p.name = "Tom" p.age = 19 //方法2:键值对初始化 var p2 = person{ name: "Jack", age: 20, } //方法3:值列表初始化 var p3 = person{ "Werwilson", 23, } //方法4:构造函数初始化(指针类型) p4 := newPerson("Jessica", 28) //方法5:构造函数初始化(值类型) p5 := newPersonValue("Derrick", 38) fmt.Println(p.name) fmt.Println(p2.name) fmt.Println(p3.name) fmt.Println(p4.name) fmt.Println(p5.name) }
实例化
package main import "fmt" type person struct { name string age uint8 } func main() { //1.先声明变量类型然后再进行初始化初始化结构体 var p1 person p1.name = "Martin" p1.age = 18 fmt.Println(p1.age) //18 //2.new开辟内存返回指针初始化结构体 var p2 = new(person) (*p2).name = "Martin" p2.name = "张根" //等同于(*p1).name="Martin" p2.age = 19 fmt.Println(p2.name) fmt.Println(p2.age) //3.声明变量类型同时初始化结构体(key value版) var p3 = &person{name: "Jack", age: 28} fmt.Println(p3.name) fmt.Println(p3.age) // p4 := &person{ "Rose", 23,//注意最后的item也要有逗号!!值顺序和结构体定义的字段顺序一致, } fmt.Println(p4.name) fmt.Println(p4.age) }
构造函数初始化结构体
构造函数 就是1个构造X种结构体变量的函数,其用意是通过 1个函数反复生成某种结构体的变量,提升代码的重用性。
使用变量初始化结构体的方式会造成代码的冗余,我么可以使用一个构造函数来完成struct的初始化。
struct的构造函数约定俗成以 new开头,自定义1个构造函数可以返回1个值类型的struct, 如果1个struct内部字段存储的数据量很大,重复copy造成内存开销过大。也可以返回1个指针类型的struct。
package main import "fmt" type person struct { name string age uint8 } //自定义1个构造函数:返回1个结构体类型 func newPereson(name string, age uint8) person { //在构造函数中完成 struct 的初始化过程 return person{ name: name, age: age, } } //自定义1个构造函数:返回1个指针 //避免struct 内部数据量大的时候,重复copy造成内存开销过大 //struct的构造函数约定俗成以 new开头 func newPeresonPointer(name string, age uint8) *person { //在构造函数中完成 struct 的初始化过程 return &person{ name: name, age: age, } } func main() { //构造函数可以方便、快捷的构造出不同的struct p1 := newPereson("张三", 18) p2 := newPereson("李四", 29) fmt.Println(p1) fmt.Println(p2) //p3是可以修改的,因为initPeresonPointer构造函数返回的是指针类型 p3 := newPeresonPointer("张根", 27) p3.age = 28 //(*p3).age=28 fmt.Println(p3.age) }
结构体数据类型内存管理机制
结构体类型属于golang中的一种数据类型且是值类型,默认情况(非指针类型结构体)这种数据类型的变量被赋值之后,会重新拷贝一份。但是注意arry和map的底层存储原理。
如果是指针类型的结构体被赋值之后则不会开辟新的内存空间。
如果希望2个结构体对象的值同步变化,就使用指针类型的结构体,否则不使用。
1.结构体内存管理机制
p1 := Peron{name: "武沛齐", age: 18} //1.赋值之后会重新拷贝一份p1的数据赋值给p2 p2 := p1 fmt.Println(p1) //{武沛齐 18} fmt.Println(p2) //{武沛齐 18} p1.name = "alex" fmt.Println(p1) //{alex 18} fmt.Println(p2) //{武沛齐 18}
2.结构体指针类型变量内存管理机制
p1 := &Peron{name: "武沛齐", age: 18} //1.赋值之后会重新拷贝一份p1的数据赋值给p2 p2 := p1 fmt.Println(p1) //&{武沛齐 18} fmt.Println(p2) //&{武沛齐 18} p1.name = "alex" fmt.Println(p1) //&{alex 18} fmt.Println(p2) //&{alex 18}
3.嵌套结构体内存管理机制
如果存在结构体嵌套,在结构体对象被赋值之后也会重新拷贝1份。
type Address struct { city, state string } type Person struct { name string age int address Address //嵌套结构体 } p1 := Person{name: "二狗子", age: 19, address: Address{city: "北京", state: "BJ"}} p2 := p1 fmt.Println(p1.address, p2.address) //{北京 BJ} {北京 BJ} p1.address.city = "上海" p1.address.state = "SH" fmt.Println(p1.address) //{上海 SH} fmt.Println(p2.address) //{北京 BJ}
4.结构体中包含引用数据类型
当1个结构体类型变量赋值给另1个新的变量时,本质上会copy一份新的。
由于struct中包含的数据的存储方式不同,导致有的copy的是内存地址(pointer)有的copy的是值。
感觉拷贝:整型、布尔、字符串、数组
感觉不拷贝:切片、字典
所以想要达到让1个结构体实例化出来的2个对象数据保持一直,可以借助指针。
type Address struct { city, sate string owners []string } type Person struct { name string age int children [2]string hobbies []string parent map[string]string address Address } p1 := Person{name: "二狗", age: 69, children: [2]string{"小奶狗", "小狼狗"}, hobbies: []string{"吃", "喝"}, parent: map[string]string{"": "Tom", "mother": "Rose"}, address: Address{city: "北京", sate: "BJ", owners: []string{"二狗", "二狗夫人"}}} p2 := p1 fmt.Println(p1) //{二狗 69 [小奶狗 小狼狗] [吃 喝] map[:Tom mother:Rose] {北京 BJ [二狗 二狗夫人]}} fmt.Println(p2) //{二狗 69 [小奶狗 小狼狗] [吃 喝] map[:Tom mother:Rose] {北京 BJ [二狗 二狗夫人]}} p1.children[0] = "Joy" //修改值类型的字段不会影响全局 fmt.Println(p1) //{二狗 69 [Joy 小狼狗] [吃 喝] map[:Tom mother:Rose] {北京 BJ [二狗 二狗夫人]}} fmt.Println(p2) //{二狗 69 [小奶狗 小狼狗] [吃 喝] map[:Tom mother:Rose] {北京 BJ [二狗 二狗夫人]}} p1.parent["father"] = "隔壁老王" //修改引用类型(map)的字段会影响全局 p1.hobbies[1] = "AllIn" //修改引用类型(arry)的字段会影响全局 p1.address.owners[0] = "王先生" //修改引用类型(map)的字段会影响全局 fmt.Println(p1) //{二狗 69 [Joy 小狼狗] [吃 AllIn] map[:Tom father:隔壁老王 mother:Rose] {北京 BJ [王先生 二狗夫人]}} fmt.Println(p2) //{二狗 69 [小奶狗 小狼狗] [吃 AllIn] map[:Tom father:隔壁老王 mother:Rose] {北京 BJ [王先生 二狗夫人]}} /* */
结构体模拟面向对象继承效果
面向对象中的继承可以,重用代码,避免重复造轮子,那么怎么使用Go的struct模拟继承的效果呢?
匿名字段struct
package main import "fmt" //匿名字段:匿名字段就是没有字段名称 //匿名字段适用于 struct字段较少的场景 type person struct{ string int32 } func main(){ p1:=person{"Martin",20} //如果没有字段名称通过什么取值呢?数据类型! fmt.Println(p1.string) fmt.Println(p1.int32) }
嵌套struct
为了实现更深层的数据封装,结构体里也可以套结构体。
//嵌套结构体 package main import "fmt" //员工个公司共有的地址属性 type address struct { province string city string } //员工信息struct type employee struct { name string age int8 addr address //嵌套了结构体address } //公司信息struct type company struct { name string addr address //嵌套了结构体address } func main() { employee1 := employee{ name: "Robinz", age: 29, addr: address{ province: "山西省", city: "阳泉市", }, } company1 := company{ name: "baix", addr: address{province: "北京市", city: "海淀区"}, } fmt.Println(employee1.name) fmt.Println(employee1.addr.province) fmt.Println(employee1.addr.city) fmt.Println(company1.addr.city) }
匿名嵌套struct
//匿名嵌套结构体 package main import "fmt" //员工个公司共有的地址属性 type address struct { province string city string } //员工信息struct type employee struct { name string age int8 address //嵌套匿名字段的结构体address } //公司信息struct type company struct { name string address //嵌套了匿名字段的结构体address } func main() { employee1 := employee{ name: "Robinz", age: 29, address:address{ province: "山西省", city: "阳泉市", }, } company1 := company{ name: "baix", address:address{province: "北京市", city: "海淀区"}, } fmt.Println(employee1.name) fmt.Println(employee1.province) fmt.Println(employee1.city)//先在自己的struct里面查询字段 再去匿名嵌套的struct查询 fmt.Println(company1.city)//先在自己的struct里面查询字段 再去匿名嵌套的struct查询 }
如果1个struct嵌套了2个字段相同的匿名struct,现在是2个平级,那么我该去这2个匿名struct中哪一个里面取查找呢?
在Golang中会引发冲突,和Python不同的是golang只能深度找,不能广度找。
//匿名嵌套结构体 package main import "fmt" //员工个公司共有的地址属性 type address struct { province string city string } //工作地址:和 adress中的 province好city字段出现了冲突 type workAdreess struct { province string city string } //员工信息struct type employee struct { name string age int8 address //嵌套匿名字段的结构体address 字段冲突 workAdreess////嵌套匿名字段的结构体workAdreess字段冲突 } //公司信息struct type company struct { name string address //嵌套了匿名字段的结构体address } func main() { employee1 := employee{ name: "Robinz", age: 29, address: address{ province: "山西省", city: "阳泉市", }, workAdreess: workAdreess{province: "山东省", city: "威海"}, } company1 := company{ name: "baix", address: address{province: "北京市", city: "海淀区"}, } fmt.Println(employee1.name) fmt.Println(employee1.name) // fmt.Println(employee1.city)//先在自己的struct里面查询字段 再去匿名嵌套的struct查询 fmt.Println(company1.city) //先在自己的struct里面查询字段 再去匿名嵌套的struct查询 //如果employee结构体中嵌套了2个含有相同字段的匿名结构体,会引起查询冲突,只能按照以下方式取值 fmt.Println(employee1.address.city) fmt.Println(employee1.workAdreess.province) }
模拟继承
利用Go的struct可以嵌套struct,当前struct中没有的字段 自动去嵌套了struct的匿名字段中查找的特性,实现继承的效果。
package main import "fmt" //基类 type animal struct { kind string gender string age uint8 } //子类(人类) type perosn struct { animal } //子类(犬类) type dog struct { animal } //给基类增加walk方法 func (a animal) walk() { fmt.Printf("%s are walking.. \n", a.kind) } func main() { p1 := perosn{ animal: animal{kind: "People", age: 18, gender: "男性"}, } //p1 struct里面没有walk方法,就自动去匿名字段animal这个匿名结构体中查找 p1.walk() d1 := dog{ animal: animal{kind: "Dogs", age: 3, gender: "雄性"}, } d1.walk() //d1 struct里面没有walk方法,也自动去匿名字段animal这个匿名结构体中查找 }
结构体内存布局
结构体占用一块连续的内存。
package main import "fmt" //结构体占用1块连续的内存 type x struct { a int8 //8位=1个字节 b int8 c int8 } func main() { m := x{ 1, 2, 3, } fmt.Printf("字段a的内存地址:%p\n", &m.a) fmt.Printf("字段b的内存地址:%p\n", &m.b) fmt.Printf("字段c的内存地址:%p\n", &m.c) } /* 字段a的内存地址:0xc00004a080 字段b的内存地址:0xc00004a081 字段c的内存地址:0xc00004a082 */
struct的方法和接受者
前面的struct中我只是封装了数据,那么我想对struct中的这些数据进行操作呢?就需要给struct绑定上1个方法。
Go语言中的方法(Method)
是一种作用于特定类型变量的函数。
这种特定类型变量叫做接收者(Receiver)
。接收者的概念就类似于其他语言中的this
或者 self
。
Go里面接收者(某1个数据类型)+方法 这套语法,实现了类似于Python类中的方法!
方法和函数的不同是函数不从属于任何数据类型,而方法作用于某种数据类型。
语法
原来Go的函数名前面还可以指定接收者
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
值类型的接受者
接受者必须指定某1个数据类型
package main import "fmt" type dog struct{ kind string age uint8 } //方法和接收者 func newDog(kind string,age uint8)(*dog){ return &dog{ kind:kind, age:age, } } //函数名前面可以指定这个函数的接受者,如果该函数指定了接收这,这个函数就叫method方法 //因为指定了接收者,所以方法是作用于特定类型的函数 //接受者使用类型的首字母 小写表示 // dog 类型是接收者 bark就是仅作用于dog类的方法 func (d dog)bark(){ fmt.Printf("A %s barks at you~\n",d.kind) } func main(){ d1:=newDog("中华田园犬",2) //因为接受者和方法做了绑定,所以dog类的对象都可以调用方法 bark 方法 d1.bark() }
指针类型的接受者
接受者还可以为 某种数据类型的指针,接受者为数据类型指针时就实现了对struct 字段的修改。
package main import "fmt" //定义1个struct person type person struct { name string age uint8 } //定义1个用于初始 person结构体的构造函数 func newPerson(name string, age uint8) *person { return &person{ name:name, age:age, } } //使用指针接受者:接收者不仅可以为自定义的数据类型,也可以是数据类型的指针类型 func (p *person)aged(years uint8){ p.age+=years } func main() { p1:=newPerson("Someone you don't like",39) fmt.Println(p1.age) p1.aged(10) fmt.Println(p1.age) p1.aged(10) fmt.Println(p1.age) p1.aged(10) fmt.Println(p1.age) }
任意类型添加方法
在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int32
类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。
package main import "fmt" //给go内置的数据类型扩展方法 type myInt int64 //给int32扩展1个翻x倍的方法 func(m *myInt)autoTimes(n int64 ){ (*m)*=myInt(n) } func main(){ var salary myInt salary=2500 //娶媳妇的年级了,给自己涨点工资吧.... salary.autoTimes(1000000) fmt.Println(salary) }
结构体序列化
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON键值对是用来保存JS对象的一种方式,键/值对组合中的键名写在前面并用双引号""
包裹,使用冒号:
分隔,然后紧接着值;多个键值之间使用英文,
分隔。
序列化和反序列化
//1.序列化:把Go语言中的结构体变量------json格式的字符串 //2.反序列化:把json格式的字符串---------Go语言能识别的结构体变量 /* 由于我们使用了第三方的包,而main包中声明的变量 无法在第三包中使用(除非大写才能被main包之外的包使用) 所以想要访问main中的变量必须大写! 如果必须大写。我们产生的json数据也会变成大写,为了避免数据失真,可以使用tag */ type person struct { Name string `json:"name" db:"name" ini:"name"` Age uint8 `json:"age" db:"name" ini:"name"` } func main() { p1 := person{ Name: "Martin", Age: 18, } //序列化 b, err := json.Marshal(p1) if err != nil { fmt.Printf("marshal faild err:%v", err) return } fmt.Printf("%#v\n", string(b)) //字符串本身是由字节切片组成的,所有支持强制转换。 //反序列化 var p2 person json.Unmarshal([]byte(string(b)), &p2) fmt.Printf("%#v\n", p2) }
复杂json结构
package main import ( "encoding/json" "fmt" ) type Person struct{ Name string Age int } var group1 []*Person func main(){ group1=[]*Person{ &Person{ "Martion", 18, }, &Person{ "Toney", 38, }, } b,err:=json.Marshal(group1) if err!=nil{ fmt.Println("序列化失败",err) } //fmt.Println(b,err) err=json.Unmarshal(b,&group1) if err!=nil{ fmt.Println("反序列化失败",err) } fmt.Printf("%#v\n", group1[0].Name) }
在golang中我们不仅可以把json转换成结构体,还可以把json转换成map类型。
package main import ( "encoding/json" "fmt" ) type Person struct { Name string `json:"name"` Age int `json:"age"` } func main() { p1 := Person{Name: "Martin", Age: 19} b, err := json.Marshal(p1) if err != nil { fmt.Println(err) } //json字符串 fmt.Println(string(b)) //声明map maping:=map[string]interface{}{} //在golang中我们不仅可以把json转换成结构体,还可以把json转换成map类型。 err=json.Unmarshal(b,&maping) if err!=nil{ fmt.Println(err) } fmt.Println(maping) }
结构体实现链表
链表反转
// package main // import "fmt" // type person struct { // name string // age uint8 // } // //自定义1个构造函数:返回1个结构体类型 // func newPereson(name string, age uint8) person { // //在构造函数中完成 struct 的初始化过程 // return person{ // name: name, // age: age, // } // } // //自定义1个构造函数:返回1个指针 // //避免struct 内部数据量大的时候,重复copy造成内存开销过大 // //struct的构造函数约定俗成以 new开头 // func newPeresonPointer(name string, age uint8) *person { // //在构造函数中完成 struct 的初始化过程 // return &person{ // name: name, // age: age, // } // } // func main() { // //构造函数可以方便、快捷的构造出不同的struct // p1 := newPereson("张三", 18) // p2 := newPereson("李四", 29) // fmt.Println(p1) // fmt.Println(p2) // //p3是可以修改的,因为initPeresonPointer构造函数返回的是指针类型 // p3 := newPeresonPointer("张根", 27) // p3.age = 28 //(*p3).age=28 // fmt.Println(p3.age) // } package main import "fmt" //链表就是:每1个节点都会记录本节点next节点的指针(内存地址) type linkedList struct { value int //在结构体里面引用结构体自己也是可以的 next *linkedList } var linkedList1 = &linkedList{ value: 1, next: &linkedList{ value: 2, next: &linkedList{ value: 3, next: &linkedList{ value: 4, next: &linkedList{ value: 5, next: nil, }, }, }, }, } func reverseList(head *linkedList) *linkedList { //关键点:新的反转链表第1个值个应该为nil,才可以站在当前位置 设置old 链表的下1值,的前1个为自己! var newPreviousOne *linkedList //当前结构体 current := head //当前结构体的值为空时说明遍历链表到了尽头 for current != nil { //把旧链表中的下1个节点的值缓存起来 oldNextOne := current.next //开始反转新的链表 current.next = newPreviousOne //1步步地向前反转 newPreviousOne= current //保持for循环1步步地在old链表里向前进行 current=oldNextOne } return newPreviousOne } func main() { ret := reverseList(linkedList1) for ret != nil { fmt.Println(ret.value, "->") ret = ret.next } }
练习
员工管理函数版
/* 函数版学生管理系统: 该系统能查看/新增/删除学生使用函数实现 */ package main import ( "fmt" "os" ) //变量声明 var ( database map[int]student studentID int studentName string studentAge uint8 oprate uint8 exitSignal byte ) type student struct { name string age uint8 } //学生结构体 func newStuden(name string, age uint8) student { return student{ name: studentName, age: studentAge, } } //增加学生 func addStudent() { fmt.Print("请输入学生id:") fmt.Scan(&studentID) fmt.Print("请输入姓名:") fmt.Scan(&studentName) fmt.Print("请输入年龄:") fmt.Scan(&studentAge) database[studentID] = newStuden(studentName, studentAge) } //查看数据库 func showStudents() { for id, s := range database { fmt.Printf("学生ID:%d 姓名:%s 年龄:%d \n", id, s.name, s.age) } } //根据主键删除 func deleteStudent() { fmt.Print("请输入学生id:") fmt.Scan(&studentID) delete(database, studentID) } //for {}死循环显示程序菜单 func showOperation() { for { fmt.Println(` 1.新增学生 2.删除学生 3.查看学生 4.退出 `) fmt.Printf("请输出操作:") fmt.Scan(&oprate) switch oprate { case 1: addStudent() case 2: deleteStudent() case 3: showStudents() case 4: os.Exit(1) } } } func main() { //初始化数据库 database = make(map[int]student, 20) //死循环:程序菜单 showOperation() }
员工管理面向对象版
manage_unit.go
package manage import ( "fmt" "sort" ) var ( id int name string age uint8 ) //Employee 雇员 type Employee struct { name string age uint8 } //newEmployee 构造函数 func newEmployee(name string, age uint8) Employee { return Employee{ name: name, age: age, } } //修改 employee信息 func(E *Employee)modifyEmployee(name string,age uint8 ){ E.name=name E.age=age } //Employer 雇主 type Employer struct { db map[int]Employee } //NewEmployer 构造函数 func NewEmployer(count int) Employer { return Employer{ db: make(map[int]Employee, count), } } //Add 方法 func (E *Employer) Add() { fmt.Print(`请输入员工ID: `) fmt.Scan(&id) fmt.Print(`请输入员工姓名:`) fmt.Scan(&name) fmt.Print(`请输入员工年龄:`) fmt.Scan(&age) E.db[id] = newEmployee(name,age) } //Del 删除员工信息 func (E *Employer) Del() { fmt.Print(`请输入员工ID: `) fmt.Scan(&id) delete(E.db, id) } //Update 更新员工信息 func (E *Employer) Update() { fmt.Print(`请输入员工ID: `) fmt.Scan(&id) fmt.Print(`请输入员工姓名:`) fmt.Scan(&name) fmt.Print(`请输入员工年龄:`) fmt.Scan(&age) worker:=E.db[id] fmt.Printf("原来work内存地址:%p\n",&worker) worker.modifyEmployee(name,age) fmt.Printf("现worker内存地址%p\n",&worker) E.db[id]=worker } //ShowAll 方法 func (E *Employer) ShowAll() { fmt.Println("开始查看") if len(E.db) == 0 { fmt.Println("暂无员工数据") return } //按id大小顺序 显示员工信息 var keys=make([]int, 0,len(E.db)) for key:= range E.db{ keys = append(keys,key) } sort.Ints(keys) for _, key := range keys { worker:=E.db[key] fmt.Printf("员工ID:%d 员工姓名:%s 员工年龄:%d\n", key, worker.name, worker.age) } }
main.go
package main import ( "fmt" //Go导入包从环境变量配置的go project/src文件夹下开始 //tools文件夹下.go文件中 package的manage manage "hello/crm/tools" "os" ) var ( operate uint8 ) func screen() { //初始化1个雇主的对象(有自己的数据库,拥有对数据库正删改查的权限) employerObj := manage.NewEmployer(200) for { fmt.Println(` 1.添加员工 2.删除员工 3.修改员工 4.查看所有 5.退出 `) fmt.Print(">>") fmt.Scan(&operate) switch operate { case 1: employerObj.Add() case 2: employerObj.Del() case 3: employerObj.Update() case 4: employerObj.ShowAll() case 5: os.Exit(1) default: fmt.Println("输入无效") } } } func main() { screen() }