Golang 结构体(struct)

前言

编程就是要通过编程语言表达给计算机,让计算机帮助我们达到解决现实生活问题的目的!

不管是Python还是Golang...这些编程语言,由于历史原因、遇到的痛点、解决的问题不同,导致语法追求、本身特性不同。但是遇到的问题、解决问题的思想是一致的。

面向对象编程 :就是按照自己的理解 尽量把程序里出现的所有东西   抽象得划分为1个个的不同的分类,这些分类中包含自身独有的数据、也有自己独特的方法!

如果想要开发1款游戏,游戏中的人物不仅有角色属性、也有交易、攻击这些作为。

单纯得使用数据类型int、string ..函数去表示1个人物,复杂不利于代码灵活、扩展,于是想办法如何把数据和方法集合到1块进行表示。

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。

Go语言中通过结构体的内嵌再配合接口  比面向对象具有更高的扩展性和灵活性

 结构体:就是可以把多种不同的基本数据类型,封装到1个整体里面。

自定义类型

自定义类型可以对Go中现有的数据类型的方法进行扩展

类型别名

类型别名还记得用于表示英文字符、中文字符的 byte和rune 是uint8和int32类型的类型别名吗?
为什么会有类型别名?
我们使用int类型声明1个字符变量看起来不贴切于人类的思维,为了让代码看起来更加清晰易懂,Go语言的作者们使用类型别名
rune和bute来表示字符。
//自定义类型和类型别名

//在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)//main.myInt
	fmt.Printf("%T
",m)//int
	fmt.Printf("%T
",c1)//uint8
	fmt.Printf("%T
",c2)//uint8
	fmt.Printf("%T
",c3)//int32
	fmt.Printf("%T
",c4)//int32

}

  

结构体声明

structure别看英文了,结构:顾名思义肯定是由不同的东西组合而成。

在我们写代码的时候如果 需要定义1个由多个基本数据类型组成的数据类型时(例如人有性别string、年龄uint8、爱好得有多个[]string、吃、喝、拉、撒、睡、学习、工作func)创造这种具有多维度的属性时的物时,无法使用单一的数据类型表示全面,所有我们只能使用结构体。

结构体的语法

使用typestruct关键字来定义结构体,具体代码格式如下:

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
",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
details:%#v
",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)

}

  

结构体模拟面向对象继承效果

面向对象中的继承可以,重用代码,避免重复造轮子,那么怎么使用Go的struct模拟继承的效果呢?

匿名字段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 

上面我们使用了嵌套结构体,在使用struct取值时需要 employee1.addr.city、employee1.addr.province点2次,如何不点2次呢?

匿名嵌套结构体: 先在自己的struct里面查找字段,如果查找不到该字段,再去匿名嵌套的struct查找。
注意如果:1个struct中定义了2个匿名字段的struct(且2个匿名struct的字段名1样,就无法查找。)
//匿名嵌套结构体
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个匿名struct中哪一个里面取查找呢?所以会引发冲突

//匿名嵌套结构体
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.. 
", 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
", &m.a)
	fmt.Printf("字段b的内存地址:%p
", &m.b)
	fmt.Printf("字段c的内存地址:%p
", &m.c)

}
/*
字段a的内存地址:0xc00004a080
字段b的内存地址:0xc00004a081
字段c的内存地址:0xc00004a082
*/

  

struct的方法和接受者

方法就是在 函数名前指定了接收者函数指定接收者就是指定了哪1种数据类型的变量,才可以调用这个函数

前面的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~
",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
", string(b)) //字符串本身是由字节切片组成的,所有支持强制转换。

	//反序列化
	var p2 person
	json.Unmarshal([]byte(string(b)), &p2)
	fmt.Printf("%#v
", 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
", group1[0].Name)

	}

  

结构体实现链表

链表反转

// 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 
", 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
",&worker)
	worker.modifyEmployee(name,age)
	fmt.Printf("现worker内存地址%p
",&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
", 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()
}

  

参考

 
原文地址:https://www.cnblogs.com/sss4/p/12651221.html