Go学习笔记

一、数据结构

1.1 变量

1.1.1 变量数据类型

整型

类型描述
uint8无符号 8位整型 (0 到 255)
uint16无符号 16位整型 (0 到 65535)
uint32无符号 32位整型 (0 到 4294967295)
uint64无符号 64位整型 (0 到 18446744073709551615)
int8有符号 8位整型 (-128 到 127)
int16有符号 16位整型 (-32768 到 32767)
int32有符号 32位整型 (-2147483648 到 2147483647)
int64有符号 64位整型 (-9223372036854775808 到 9223372036854775807)
uint32位操作系统上就是uint32,64位操作系统上就是uint64
int32位操作系统上就是int32,64位操作系统上就是int64
uintptr无符号整型,用于存放一个指针

浮点型

类型描述
float3232位浮点型
float6464位浮点型

复数

var c complex64 = 1 + 2i
var d complex128 = 3 + 4i
类型描述
complex6464位复数
complex128128位复数

布尔值

var f bool = true

字符串

内部实现使用UTF-8编码

var s1 string = "hello"
s2 := "你好"

1.1.2 变量声明方式

//方法一:声明一个变量 默认的值是0
var a int

//方法二:声明一个变量,初始化一个值
var b int = 100

//方法三:在初始化的时候,可以省去数据类型,通过值自动匹配当前的变量的数据类型
var c = 100

//方法四:(常用的方法) 省去var关键字,直接自动匹配(注:此方式不适用于全局变量)
e := 100

// 声明多个变量
var xx, yy int = 100, 200
var kk, ll = 100, "Aceld"

//多行的多变量声明
var (
	vv int  = 100
	jj bool = true
)

1.1.3 常量、iota枚举

//常量(只读属性)
const a int = 10
//const 来定义枚举类型
const (
	BEIJING  = 10
	SHANGHAI = 43
	SHENZHEN = 32
)

const (
	//可以在const() 添加一个关键字 iota, 每行的iota都会累加1, 第一行的iota的默认值是0
	BEIJING  = 10 * iota //iota = 0
	SHANGHAI             //iota = 1
	SHENZHEN             //iota = 2
)

const (
	a, b = iota + 1, iota + 2 // iota = 0, a = iota + 1, b = iota + 2, a = 1, b = 2
	c, d                      // iota = 1, c = iota + 1, d = iota + 2, c = 2, d = 3
	e, f                      // iota = 2, e = iota + 1, f = iota + 2, e = 3, f = 4

	g, h = iota * 2, iota * 3 // iota = 3, g = iota * 2, h = iota * 3, g = 6, h = 9
	i, k                      // iota = 4, i = iota * 2, k = iota * 3 , i = 8, k = 12
)

1.2 数组和切片

1.2.1 声明

var arr1 [3]int           //声明数组,初始化值为0
arr2 := [3]int{1, 2, 3}   //声明时即初始化完成
arr3 := [...]int{1, 2, 3} //在编译期间通过源代码推导数组的大小

var slice1 []int         //声明slice1是一个切片,但是并没有给slice分配空间
slice2 := []int{1, 2, 3} //声明slice2是一个切片,并且初始化,默认值是1,2,3。 长度len是3
slice3 := make([]int, 3) //len=3,cap=3

s := make([]int, 4, 5) //len=4,cap=5
s = append(s, 1) //len=5,cap=5
s = append(s, 1) //len=6,cap=10

1.2.2 传递

//值拷贝
func operate1(myArray [3]int) {
	myArray[0] = 111
}

//引用拷贝
func operate2(myArray []int) {
	myArray[0] = 111
}

arr1 := [3]int{1, 2, 3}
operate1(arr1) //[1,2,3]

arr2 := []int{1, 2, 3}
operate2(arr2) //[111,2,3]
s := []int{1, 2, 3} //len=3 cap=3 [1,2,3]

s1 := s[0:2] //左开右闭区间[0,2) len=2 cap=3 [1,2]
s1[0] = 100  //会将s一起修改 s=[100,2,3]

s2 := make([]int, 3) //s2 = [0,0,0]
copy(s2, s)  //将s中的值依次拷贝到s2中
s2[0] = 400  //不会影响s中的值

1.3 map

var map1 map[string]string  //声明map1是一种map类型,string->string,未分配空间时为nil
map1 = make(map[string]string, 10) //在使用map前,需要先用make给map分配数据空间
map2 := make(map[int]string)
map3 := map[string]string{
	"one":   "php",
	"two":   "c++",
	"three": "python",
}
//引用传递
func ChangeValue(cityMap map[string]string) {
	cityMap["England"] = "London"
}

func main() {
	//声明
	cityMap := make(map[string]string)

	//添加
	cityMap["China"] = "Beijing"
	cityMap["Japan"] = "Tokyo"
	cityMap["USA"] = "NewYork"

	//遍历
	for key, value := range cityMap {
		fmt.Println("key = ", key)
		fmt.Println("value = ", value)
	}

	//删除
	delete(cityMap, "China")

	//修改
	cityMap["USA"] = "DC"
	
	//方法调用
	ChangeValue(cityMap)
}

二、方法

2.1 形参

func foo1(a string, b int) {}

func foo1(a string, b int) int {
	return 3
}

2.2 返回值

1- 返回单个返回值

func foo1(a string, b int) int {
	return 3
}

2- 返回多个返回值,匿名的

func foo2(a string, b int) (int, int) {
	return 666, 777
}

3- 返回多个返回值, 有形参名称的

func foo3(b int) (r1 int, r2 int) {
	//r1 r2 属于foo3的形参,  初始化默认的值是0
	//r1 r2 作用域空间 是foo3 整个函数体的{}空间
	fmt.Println("r1 = ", r1)
	fmt.Println("r2 = ", r2)
	//给有名称的返回值变量赋值
	r1 = b
	r2 = b + 10
	return
}

func foo4(b int) (r1, r2 int) {
	r1 = b
	r2 = b + 2000
	return
}

三、对象

3.1 封装

type User struct {
	// 变量名首字母大写,说明其他包可以访问
	Name string
	Age  int
}

func (this *User) Show() {
	fmt.Println("名字:", this.Name)
	fmt.Println("年龄:", this.Age)
}

func (this *User) GetName() string {
	return this.Name
}

func (this *User) SetName(name string) {
	this.Name = name
}

func main() {
	user := User{"jack", 18}
	user.SetName("tom")
	user.Show()
}

3.2 封装

type User struct {
	Name string
	Age  int
}

type Student struct {
	User  // 继承父类User
	Class string
}

func main() {
	user := User{"jack", 18}
	student := Student{user, "六班"}
	fmt.Println(student)
}

3.3 多态

//接口父类
type User interface {
	GetName() string
	SetName(name string)
}

//实现接口全部方法的子类
type Student struct {
	Name string
}

func (this *Student) GetName() string {
	return this.Name
}

func (this *Student) SetName(name string) {
	this.Name = name
}

func main() {
	var u User
	u = &Student{"tom"}
	fmt.Println(u.GetName())
}

interface{}空接口

int 、string、float32、 float64、struct .... 都实现了interface{}

就可以⽤interface{}类型 引⽤任意的数据类型

func main() {
	var x interface{} = "djj"
	value, ok := x.(string)
	if !ok {
		fmt.Println("x is not string")
	} else {
		fmt.Printf("%v %T", value, value)
	}
}

四、并发编程

4.1 goroutine

runtime.Goexit()

会退出当前goroutine,结束全部方法

go func() {
	defer fmt.Println("defer A")
	func() {
		defer fmt.Println("defer B")
		runtime.Goexit()
		fmt.Println("==B==")
	}()
	fmt.Println("==A==")
}()
---
defer B
defer A

4.2 Channel

Channel是goroutine之间交换信息的媒介

上图中的两个 Goroutine,一个会向 Channel 中发送数据,另一个会从 Channel 中接收数据,它们两者能够独立运行并不存在直接关联,但是能通过 Channel 间接完成通信。

定义方式

make(chan Type)  //等价于make(chan Type, 0)
make(chan Type, capacity)

使用方式

channel <- value   //将value发送到channel

<- channel         //接收并将其丢弃
x := <- channel    //接收并将其赋值给x
x, ok := <-channel //功能同上,同时检查通道是否已关闭或是否为空
channel := make(chan int, 1)

channel <- 3

go func() {
	x := <-channel
	fmt.Println("==", x)
}()

无缓冲vs有缓冲

无缓冲通道,在收发信息时会阻塞。

有缓冲通道,在消息满或空时才会阻塞。

Snipaste_2021-12-17_12-24-30

关闭channel

  • channel不像⽂件⼀样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的结束range循环之类的,才去关闭channel;
  • 关闭channel后,⽆法向channel 再发送数据(引发 panic 错误后导致接收⽴即返回零值)
  • 关闭channel后,可以继续从channel接收数据;
func main() {
	channel := make(chan int)

	go func() {
		for i := 0; i < 5; i++ {
			channel <- i
			fmt.Println("写入", i)
		}
		//关闭channel
		close(channel)
	}()

	for {
		if value, ok := <-channel; ok {
			fmt.Println("接收", value)
		} else {
			fmt.Println("channel已关闭")
			break
		}
	}
}

channel与range

func main() {
	channel := make(chan int)

	go func() {
		for i := 0; i < 5; i++ {
			channel <- i
			fmt.Println("写入", i)
		}
		//关闭channel
		close(channel)
	}()

	for data := range channel {
		fmt.Println("接收", data)
	}
}

channel与select

末、杂七杂八

1-导包

初始化方式是递归初始化,初始化一个包时会先初始化全局变量,然后执行init()方法

导包的三种方式

import _ "fmt"   //匿名导入, ⽆法使⽤当前包的⽅法,但是会执⾏当前的包内部的init()⽅法
import aa "fmt"  //给fmt包起⼀个别名,可通过aa.Println()来直接调⽤
import . "fmt"   //导入fmt包中的全部⽅法,可以直接使⽤API来调⽤,不需要fmt.API来调⽤

2-指针

func swap(pa *int, pb *int) {
	var temp int
	temp = *pa //temp = main::a
	*pa = *pb  // main::a = main::b
	*pb = temp // main::b = temp
}

func main() {
	var a int = 10
	var b int = 20
	swap(&a, &b)
}

3-defer

defer会按顺序压入栈中,函数结束后再依次取出

func main() {
	defer fmt.Println("main end1")
	defer fmt.Println("main end2")


	fmt.Println("main::hello go 1")
	fmt.Println("main::hello go 2")
}
----
main::hello go 1
main::hello go 2
main end2
main end1

return语句先执行,defer语句后执行

func f1() int {
	fmt.Println("return func")
	return 3
}

func f2() {
	fmt.Println("defer func")
}

func f3() int {
	defer f2()
	return f1()
}

func main() {
	f3()
}
---
return func
defer func

4-反射

变量结构

reflect包

func main() {
	var x interface{} = "djj"
	fmt.Printf("%T %v\n", reflect.TypeOf(x), reflect.TypeOf(x))
	fmt.Printf("%T %v", reflect.ValueOf(x), reflect.ValueOf(x))
}
---
*reflect.rtype string
reflect.Value djj

5-结构体标签

常见应用:json编解码,orm映射关系

type User struct {
	Name string `info:"name" doc:"aa"`
	Age  int    `info:"age"`
}

func main() {
	var u interface{} = &User{"tom", 17}
	fields := reflect.TypeOf(u).Elem()
	for i := 0; i < fields.NumField(); i++ {
		tagInfo := fields.Field(i).Tag.Get("info")
		tagDoc := fields.Field(i).Tag.Get("doc")
		fmt.Printf("info:%v doc:%v\n", tagInfo, tagDoc)
	}
}

hhhhh