前言
编程就是要通过编程语言表达给计算机,让计算机帮助我们达到解决现实生活问题的目的!
不管是Python还是Golang...这些编程语言,由于历史原因、遇到的痛点、解决的问题不同,导致语法追求、本身特性不同。但是遇到的问题、解决问题的思想是一致的。
面向对象编程 :就是按照自己的理解 尽量把程序里出现的所有东西 抽象得划分为1个个的不同的分类,这些分类中包含自身独有的数据、也有自己独特的方法!
如果想要开发1款游戏,游戏中的人物不仅有角色属性、也有交易、攻击这些作为。
单纯得使用数据类型int、string ..函数去表示1个人物,复杂不利于代码灵活、扩展,于是想办法如何把数据和方法集合到1块进行表示。
Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。
Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。
结构体:就是可以把多种不同的基本数据类型,封装到1个整体里面。在golang中这个整体称为结构体。
自定义类型
自定义类型可以对Go中现有的数据类型的方法进行扩展
在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",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, statestring}
type Personstruct{
namestringageintaddress 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, satestringowners []string}
type Personstruct{
namestringageintchildren [2]stringhobbies []stringparent map[string]stringaddress 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
匿名字段就是没有字段名称,由于使用数据类型取值,它适用于 struct字段较少的场景。
既然我们可以把结构体中 数据类型当做字段名称来获取值,虽然限制了结构体中相同字段只能有1种数据类型。当可以模拟其他语言中的继承。
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
既然嵌套的int和string类型可以把据数据类型名称当做字段名称使用查找到对应的值。
那么我自己通过type关键字定义的数据类型,也是可以的。
匿名嵌套结构体: 先在自己的struct里面查找字段,如果查找不到该字段,再去匿名嵌套的struct查找。
注意:如果1个struct中嵌套了2个结构体体,这些子结构体中存在相同的字段的,就无法查找。
//匿名嵌套结构体
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)
}
结构体实现链表
链表反转
// 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()
}