前言

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

不管是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)创造这种具有多维度的属性时的物时,无法使用单一的数据类型表示全面,所有我们只能使用结构体。

 

结构体的语法

使用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\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

匿名字段就是没有字段名称,由于使用数据类型取值,它适用于 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)

	}

 

在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()
}

  

 

 

 

 

 

参考

 
posted on 2020-04-07 08:07  Martin8866  阅读(3077)  评论(0编辑  收藏  举报