Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。

1.开发环境搭建

https://golang.google.cn/

2.基础

2.1编译

go run 文件名 —–> 直接运行文件

go run build 文件名.exe —->编译成exe可执行文件

go build -o 指定文件名.exe —–>指定编译的文件名

2.2转义字符

\t 制表符

\n 换行

\\ 斜杠\

\“ 分号”

\r 回车

2.3注释

//

/* */

2.4变量的声明

指定变量类型,但是不赋值,此时变量采用默认值

1
2
var i int
fmt.Println(i)

根据值自动推导类型

1
2
var name = "tom"
var num = 10

:= 左侧的变量不应该是已经声明过的

1
name := test

一次声明多个变量

1
var a,b = 10,12

一次声明多个变量并使用类型推导

1
a,b,c=10,"test",0

定义多个全局变量

1
2
3
4
var (
v1 = "money"
v2 = 10
)

3.数据类型

fmt.Printf("%T\n", c1) 打印出c1的类型

1.整形

int8 1字节

int16 2字节

int32 4字节

int64 8字节

int

uint

rune 与int32一样 等价int32,表示一个Unicode编码

byte 与int8等价

1
2
3
import "unsafe"

fmt.Print(unsafe.Sizeof(j)) //---> 8

2.浮点数

单精度float32

双精度float64

3.14e2 科学计数法 3.14*10^2

4.字符类型char

go没有装门的字符类型,可以用byte保存(ASCII码值)

go的字符串是由字节组成的

Go语言采用的编码是utf-8,解决了编码乱码的问题

1
2
3
var c2 int = '值'
fmt.Println(c2)
// ----->20540

5.布尔类型

只有false,true

6.字符串类型

字符串 一旦赋值就无法更改了,go中的字符串是不可变的

反引号输出

以字符串的原生形式输出,包括换行和特殊字符

1
fmt.Print(`\n test`)

字符串的拼接

1
fmt.Print("test" + "hahaha")

7.数据类型的默认值

1
2
3
4
5
var a int //0
var b float32 //0
var v float64 //0
var test bool //false
var name string //""

8.类型转换

1.类型转换

Golang中数据类型不能自动转换

1
2
3
T(v)
//T为数据类型 int32 float64
//v为要转换的变量和数据

Go的数据类型可以是范围小到范围大,也可以范围大到范围小

2.基本类型转string

通用:

1
2
3
4
5
%v	值的默认格式表示
%+v 类似%v,但输出结构体时会添加字段名
%#v 值的Go语法表示
%T 值的类型的Go语法表示
%% 百分号

布尔值:

1
%t	单词true或false

整数:

1
2
3
4
5
6
7
8
%b	表示为二进制
%c 该值对应的unicode码值
%d 表示为十进制
%o 表示为八进制
%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x 表示为十六进制,使用a-f
%X 表示为十六进制,使用A-F
%U 表示为Unicode格式:U+1234,等价于"U+%04X"

浮点数与复数的两个组分:

1
2
3
4
5
6
7
%b	无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat
%e 科学计数法,如-1234.456e+78
%E 科学计数法,如-1234.456E+78
%f 有小数部分但无指数部分,如123.456
%F 等价于%f
%g 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
%G 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)

字符串和[]byte:

1
2
3
4
%s	直接输出字符串或者[]byte
%q 该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
%x 每个字节用两字符十六进制数表示(使用a-f)
%X 每个字节用两字符十六进制数表示(使用A-F)

指针:

1
%p	表示为十六进制,并加上前导的0x    

fmt.Sprintf()

1
2
3
var a1 int = 10
var str string= fmt.Sprintf("%d", a1)
print(str)

strconv.FormatInt()

第一个参数是你要转换的整数。

第二个参数是基数,表示你希望整数以哪种进制格式来表示。例如,基数10表示十进制,基数16表示十六进制。

1
2
3
4
var str1 = strconv.FormatInt(10,10)

//'f'为格式 1表示保留一位小数 64表示为float64
var str2 = strconv.FormatFloat(89.99, 'f', 1, 64)

3.String转基本类型

ParseBool

1
func ParseBool(str string) (value bool, err error)

返回字符串表示的bool值。它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。

ParseInt

1
func ParseInt(s string, base int, bitSize int) (i int64, err error)

返回字符串表示的整数值,接受正负号。

base指定进制(2到36),如果base为0,则会从字符串前置判断,”0x”是16进制,”0”是8进制,否则是10进制;

bitSize指定结果必须能无溢出赋值的整数类型,0、8、16、32、64 分别代表 int、int8、int16、int32、int64;返回的err是*NumErr类型的,如果语法有误,err.Error = ErrSyntax;如果结果超出类型范围err.Error = ErrRange。

ParseUint

1
func ParseUint(s string, base int, bitSize int) (n uint64, err error)

ParseUint类似ParseInt但不接受正负号,用于无符号整型。

ParseFloat

1
func ParseFloat(s string, bitSize int) (f float64, err error)

解析一个表示浮点数的字符串并返回其值。

如果s合乎语法规则,函数会返回最为接近s表示值的一个浮点数(使用IEEE754规范舍入)。bitSize指定了期望的接收类型,32是float32(返回值可以不改变精确值的赋值给float32),64是float64;返回值err是*NumErr类型的,语法有误的,err.Error=ErrSyntax;结果超出表示范围的,返回值f为±Inf,err.Error= ErrRange。

1
2
3
4
var str3 string = "true"
var bl bool
bl, _ = strconv.ParseBool(str3)
print(bl)

函数会返回两个值,一个是转换后的值,另一个是错误,可以使用_忽略error

如果string为”hello”并且转为int这时会采用int的默认值0

9.指针

&取地址

1
fmt.Printf("i的地址为", &a)

*解引用

1
2
3
4
var a int = 10
var ptr *int = &a
fmt.Printf("i的地址为\n", &a)
fmt.Printf("%d\n", *ptr)

10.运算符

与c语言类似

11.获取用户终端输入

fmt.Scanf()

fmt.Scanfln()

4.流程控制语句

if else else if

1
2
3
4
5
if 10 > 20 {
fmt.Println("data1")
}else {
fmt.Println("data2")
}

switch

switch的后面可以不用加break

1
2
3
4
5
6
7
8
9
10
switch(a){
case "a":
fmt.Print("test1")
case "b":
fmt.Print("test2")
case "c":
fmt.Print("test3")
default:
fmt.Print("test4")
}

for 循环

1
2
3
for i := 0; i < 100; i++ {
fmt.Println("test")
}

练习 空心金字塔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for i := 0; i <= 9; i++ {
for j := 1; j <= 9-i; j++ {
fmt.Print(" ")
}
if i != 9 && i != 0 {
fmt.Print("*")
for j := 1; j <= i*2+1-2; j++ {
fmt.Print(" ")
}
fmt.Print("*")
} else {
for j := 1; j <= i*2+1; j++ {
fmt.Print("*")
}
}
fmt.Println()
}

99乘法表

1
2
3
4
5
6
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d * %d = %d\t", i, j, i*j)
}
fmt.Println()
}

goto 与C语言类似

5.函数

1.函数基本用法

1
2
3
4
5
6
func test5(a string, b string) float64 {
var res1, res2 float64
res1, _ = strconv.ParseFloat(a, 64)
res2, _ = strconv.ParseFloat(b, 64)
return res1 + res2
}

打包的基本语法

package util

引入包的基本语法

import “包的路径” 一般路径是GOPATH的src目录下开始

首字母大写类似public,小写为private

go语言不支持重载

1
2
3
4
//如果要导出包,则将首字母大写
func ha() {
fmt.Print("ha")
}

2.go语言支持使用变量接收函数

1
2
3
4
5
6
7
8
9
func fun(a int ,b int) int {
return a+b
}

func main(){
a := fun
var res int = a(10,20)
fmt.Print(res)
}

并且可以当做参数传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func fun(a int, b int) int {
return a + b
}

func fun1(funcvar func(int, int) int, c int) int {
return funcvar(10, 40) + c
}

func main() {
a := fun
var res int = a(10, 20)
fmt.Println(res)
var res1 = fun1(a, 30)
fmt.Println(res1)
}

go支持自定义数据类型

1
2
3
type myInt int
var v myInt = myInt(90)
print(v)

go支持给返回值命名

1
2
3
4
5
func rs(a int,b int)(sum int,sub int){
sum = a + b
sub = a - b
return
}

可以使用args…类型接收多个参数

args的类型为Slice可以用[index]访问

1
2
3
4
5
6
7
func add(args ...int) int {
var res int
for i := 0; i < len(args); i++ {
res += args[i]
}
return res
}

3.init函数

在main函数之前执行,用于基本构建

被引入的包的init会先执行

1
2
3
func init() {
fmt.Print("init\n")
}

4.匿名函数

在定义的同时就完成了调用

1
2
3
func (a int,b int) int{
return a+b
}(10,30)

将匿名函数赋值给变量

1
2
3
fu := func (a float64,b float64) float64 {
return a * b
}

将匿名函数赋值给全局变量此时该匿名函数为全局匿名函数

5.defer

即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题

将defer语句放入到栈时,其相关的值也会拷贝一份

遵守先进后出的规则

1
2
3
4
5
6
7
8
9
10
11
func ReadWrite() bool {
file.Open("file") //打开file文件
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}

defer在函数执行完毕后,可以及时释放函数创建的资源。

当函数完毕后,系统会依次从defer栈中。取出语句,关闭资源

6.字符串常用的函数

1.len

len(str) ===>统计长度

按字节返回

1
fmt.Print(len("test"))   //==>4
1
2
3
4
5
6
//遍历中文
str := "你好"
var res = []rune(str)
for i := 0; i < len(res); i++ {
fmt.Printf("%c\n", res[i])
}

2.Atoi

将字符串转为整数

1
2
3
4
5
6
n, err := strconv.Atoi("123")
if err != nil {
fmt.Println("转换错误", err)
} else {
fmt.Println("转换成功", n)
}

3.ItoA

将整数转为字符串

4.字符串转整数

1
2
ns := strconv.Itoa(123)
fmt.Printf("%s\n", ns)

5.字符串转为byte

1
2
var bytes = []byte("gogogo")
fmt.Printf("bytes=%v\n", bytes)

6.byte转为字符串

1
2
var strs = string([]byte{97, 98, 99})
fmt.Printf("strs=%v\n", strs)

7.10转2,8,16进制

返回值为字符串

1
str = strconv.FormatInt(123,2); //--->转为2进制

8.查找子串是否在字符串内

判断str是否有substr

1
fmt.Println(strings.Contains("hahaha", "ha"))  //==>true
1
fmt.Println(strings.Count("hahaha","ha"))  //文本内有几个substr  ===>3
1
fmt.Println(strings.EqualFold("abc","ABC"))  //比较字符串,不区分大小写  ==>true
1
2
fmt.Println(strings.Index("abc","b")) //查找substr第一次在str出现的位置,没有为-1   ===>1
strings.LastIndex() //===>与index相同,取得是最后一次出现的位置

9.替换字符

子字符串 需要替换的字符串 替换成的字符串 替换的执行次数

1
strings.Replace("go go go hello","go","golang",1)  

10.分割字符

1
strings.Split("10,20,30,40",",") //===>拆分字符串","为拆分的字符

11.大小写转换

1
2
Strings.ToLower("GO")
strings.ToUpper("go")

12.去掉空格

1
2
3
4
strings.TrimSpace(" space ") //====>去掉左右两边的字符串
strings.Trim("hahah!","!") //=====>去掉左右两边的!字符
strings.TrimLeft() //=====>去掉左两边的!字符
strings.TrimRight() //=====>去掉右两边的!字符

13.判断字符

1
2
strings.hasPrefix("haha","ha")  //判断是否以ha开头
strings.HasStuffix("haha","ha") //判断是否以ha结尾

7.日期函数的使用

1.基本使用

1
2
3
4
now := time.Now()
fmt.Printf("%T,%v", now, now)

//===>time.Time,2024-08-29 17:12:07.0249172 +0800 CST m=+0.002417901

获取年月日时分秒

1
fmt.Print(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())

time.format(“2006/01/02 15:04:05”)

字符数字不能改动

time.format(“2006-01-02 15:04:05”)

2.时间的常量

Sleep(d Duration) ==>休眠的时间

Hour 小时

Minute 分钟

Second 秒

Millisecond 毫秒

Microscend 微秒

Nanosecond Duration 纳秒

1
2
now.Unix()   //时间戳
now.UnixNano() //时间戳微秒

3.go的错误执行机制

当go程序出现异常时,会抛出panic的异常,然后在defer中用recover捕获这个异常,然后正常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func test() {
defer func() {
err := recover() //可以捕获到异常
if err != nil {
fmt.Println("err=", err)
}
}()
num1 := 10
num2 := 0
res := num1 / num2
fmt.Println(res)
}

func main() {
test() //==>err= runtime error: integer divide by zer
}

4自定义错误

errors.New(“错误说明”),返回一个error类型的值,表示一个错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func readfile(name string) (error error) {
if name == "123" {
return nil
} else {
return errors.New("读取文件错误")
}

}

func testfile() {
err := readfile("12")
if err != nil {
panic(err)
}
fmt.Println("成功执行")
}

func main() {
testfile() //==>读取文件错误
}

8.数组

1.定义

1
2
3
4
var test[10] float64
for i := 0; i < len(test); i++ {
fmt.Println(i)
}
1
2
3
4
5
6
var num = [...]int{8, 9, 10}
fmt.Println("num=", num)

//1为索引 索引:值
var num1 = [...]int{1: 800, 0: 1000, 2: 888}
fmt.Println("num1=", num1)

2.遍历

index索引

value值

1
2
3
4
5
6
var num1 = [...]int{1: 800, 0: 1000, 2: 888}
fmt.Println("num1=", num1)

for index, value := range num1 {
fmt.Printf("index=%d,value=%d\n", index, value)
}

3.切片

切片是数组的引用,切片的长度可变

1
var 变量名 []类型
1
2
3
4
5
6
7
var intarr [5]int = [...]int{1, 2, 3, 4, 5}

//引用intarr的下标1-3的位置(不包含3)
slice := intarr[1:3]
fmt.Println("slice的元素", slice)
fmt.Println("slice的长度", len(slice))
fmt.Println("slice的容量", cap(slice))

slice的元素 [2 3]
slice的长度 2
slice的容量 4

slice底层是一个结构体

  • ptr*[2]int 指针引用的位置
  • len int 切片长度
  • cap int 容量

定义切片

1
var 切片[]type = make([],len,[cap])
1
2
var test []float64 = make([]float64, 5, 10)
fmt.Println(test)

4.切片注意事项

arr[0:len(arr)] 简写为arr[:]

切片定义完后不能直接使用,需要引用一个数组,或者make一个空间来使用

切片可以再次切片,同时切边与使用它切片而成的切片共用同一个内存地址,因此一个改变,两个都会变

1
2
3
4
5
6
7
var slice1 []int = []int{100, 200, 300}
//通过append给slice追加元素
slice1 = append(slice1, 2)
fmt.Println(slice1)
//将自身追加给自身
slice1 = append(slice1, slice1...)
fmt.Println(slice1)

使用copy完成拷贝

1
2
3
4
var a []int = []int{10, 20, 30, 40, 50}
var slice2 = make([]int, 10, 20)
copy(slice2, a)
fmt.Println(slice2)

slice具有引用传递

1
2
3
4
func test(slice []int){
slice[0] =100
}
//当传递一个切片时,该切片会改变

5.修改字符串

将string转为[]byte 或者[]rune ->修改 ->重写成string

1
2
3
4
5
var str string = "this is life"
arr := []byte(str)
arr[1] = 'a'
str = string(arr)
fmt.Println(str)

修改为中文

1
2
3
4
5
var str string = "this is life"
arr := []rune(str)
arr[1] = '好'
str = string(arr)
fmt.Println(str)

9.Map

1.map

给一个空map赋值报错,在使用前需要先make,给map分配数据空间

map的key-value是无序的

1
2
3
var a map[string]string
a["1"] = "一"
fmt.Println(a) //报错
1
2
3
var a map[string]string = make(map[string]string, 10)
a["1"] = "一"
fmt.Println(a)

定义一个map,map里面还是map

1
2
3
4
5
var students map[string]map[string]string = make(map[string]map[string]string, 3)
students["1001"] = map[string]string{"name": "tom", "age": "18"}
students["1002"] = map[string]string{"name": "jack", "age": "19"}
students["1003"] = map[string]string{"name": "jerry", "age": "20"}
fmt.Println(students)

2.更新修改

map[key] = “value” 如果有就是覆盖,没有就是增加

delete(map名,key)

查找键值对

1
2
3
4
5
6
cal, ok := a["1"]
if ok {
fmt.Println(cal)
} else {
fmt.Println("不存在")
}

3.map的遍历

1
2
3
4
5
6
7
8
9
var a map[string]string = make(map[string]string, 10)
a["1"] = "一"
a["2"] = "二"
a["3"] = "三"
fmt.Println(a)

for i, v := range a {
fmt.Printf("i=%v\t v=%v\n", i, v)
}
1
2
3
//计算长度
length := len(a)
fmt.Println(length)

4.map切片

[]map[string]string

1
2
3
4
5
6
7
8
9
var cityies = []map[string]string{}
cityies = make([]map[string]string, 3)

cityies[0] = make(map[string]string, 2)
cityies[1] = make(map[string]string, 2)

cityies[0]["guangdong"] = "guangzhou"
cityies[1]["hunan"] = "changsha"
fmt.Println(cityies)

通过append动态添加属性

1
2
3
4
5
6
//动态增加
new_city := map[string]string{
"beijing": "beijing",
}
cityies = append(cityies, new_city)
fmt.Println(cityies)

5.map的排序

map原本是无序的

1
2
3
4
5
map1 := make(map[int]int)
map1[10] = 100
map1[3] = 90
map1[7] = 5
map1[20] = 30

将map的key放入切片中

对切片进行排序

遍历切片,按照key来输出map的值

1
2
3
4
5
6
7
8
9
var keys []int
for k, _ := range map1 {
keys = append(keys, k)
}
sort.Ints(keys)
fmt.Println(keys)
for _, k := range keys {
fmt.Printf("map1[%v]=%v\n", k, map1[k])
}

10.面向对象

1.stuct

字段为大写开头 –>public 小写–>private

1
2
3
4
type 结构体名 struct{
field1 type
field2 type
}

2.声明结构体

1
2
3
4
5
6
7
8
9
var person Person
var person Person = Person{}

var person *Person = new(Person)
(*person).属性="值"
//也可以简化访问


var person *Person = &Person()

3.序列化

通过tag给struct指定tag,用于序列化,保证兼容性

1
2
3
4
type Person struct{
name string `json:"name"`
age int `json:age`
}
1
2
3
//序列化
jsonStr, _ := json.Marshal(map1)
fmt.Println(jsonStr)

2.方法

1
2
3
4
5
6
7
8
9
type Person struct {
name string
age int
}

// 给person绑定一个方法
func (p Person) test() {
fmt.Println("test")
}

3.继承

嵌套匿名结构体实现继承

1
2
3
4
5
6
7
8
9
type Animal struct{
name string
move string
}

type Dog struct{
Animal //嵌套匿名结构体实现继承
age int
}

匿名结构体字段访问可以简化

直接变量名.继承的父类的属性

4.多重继承

可以嵌入一个基本数据类型

1
2
3
4
5
6
7
8
9
10
11
type A struct{
Name string
}

type B struct{
int
A
}
//访问
var b B
b.int = 30

嵌套多个匿名结构体,实现多重继承

5.接口

interface不能包含任何变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//定义接口
type C interface {
Start()
End()
}

type D struct {
}
//实现接口方法
func (d D) Start(){

}

func (d D) End(){

}

一个自定义类只有实现了某个接口,才能将自定义类型的实例赋值给接口类型

只要是自定义类型都可以实现接口,不仅仅是结构体

1
2
3
func (i integer)Start(){

}

6.类型断言

b = a.(类型) 类型断言

1
2
3
4
5
6
func main(){
var a interface{}
var b float32 = 10
a = b
b = a.(float32)
}

在进行断言时,带上检测机制

1
2
3
4
5
if b, ok := a.(float64); ok {
fmt.Printf("%T %v", b, b)
} else {
fmt.Printf("error")
}
1
2
3
4
5
6
7
8
9
10
11
12
func TypeJudge(items ...interface{}) {
for index, x := range items {
switch x.(type) {
case bool:
fmt.Printf("%d %v", index, x)
case int:
fmt.Printf("%d %v", index, x)
default:
fmt.Printf("%d %v", index, x)
}
}
}

11.文件操作

1.os.Open

file.Close()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import (
"bufio"
"fmt"
"io"
"os"
)

func main() {
file, err := os.Open("D:\\MYcode\\code_go\\src\\ts.txt")

if err != nil {
fmt.Println(err)
}

defer file.Close()

reader := bufio.NewReader(file)
for {
//读到\n就结束
str, err := reader.ReadString('\n')
if err == io.EOF {
break
} else {
fmt.Print(str)
}
}
}

1.写入文件

bufio.NewWriter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
filePath := "D:/MYcode/code_go/src/abc.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
}
defer file.Close()
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
writer.WriteString("Hello\n")
}
//将缓存的数据写入磁盘
writer.Flush()
}

2.判断目录或文件是否存在

os.Stat()

err为nil存在 不为nil 不存在

3.将文件移动到另一个目录

1
2
3
4
5
6
7
filePath := "D:/MYcode/code_go/src/abc.txt"
NewfilePath := "D:/MYcode/code_go/src/abcd.txt"
content, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Println(err)
}
err = ioutil.WriteFile(NewfilePath, content, 0666)

拷贝文件

io.Copy(writer,reader) 使用bufio.NewWriter bufio.NewReader获取writer,reader

4.接收参数的传递

1
2
3
4
5
6
func main() {
fmt.Println(len(os.Args))
for i, v := range os.Args {
fmt.Println("args[%v]=%v", i, v)
}
}

.\main.exe 1 go 2 go

5.获取解析命令行参数

1
2
3
4
5
6
7
8
9
10
11
var user string
var pwd string
var host string
var port int
flag.StringVar(&user, "u", "", "默认为空")
flag.StringVar(&pwd, "P", "", "默认为空")
flag.StringVar(&host, "h", "", "默认为空")
flag.IntVar(&port, "p", 3306, "默认为3306")

flag.Parse()
fmt.Printf("%v %v %v %v", user, pwd, host, port)

.\main.exe -u root -P 123456 -h 127.0.0.1 -p 3306

===>root 123456 127.0.0.1 3306

5.序列化json对象

1.序列化对象

1
2
3
4
5
6
7
8
9
10
student := Student{
Name: "tom",
Age: 18,
}

data, err := json.Marshal(&student)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data))

2.序列化map

1
2
3
4
5
6
7
8
9
a := make(map[string]interface{})
a["name"] = "jerry"
a["age"] = 33

data, err = json.Marshal(a)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data))

3.序列化切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var slice []map[string]interface{}

var m1 map[string]interface{}
m1 = make(map[string]interface{})
m1["name"] = "alice"
m1["age"] = "14"

var m2 map[string]interface{}
m2 = make(map[string]interface{})
m2["name"] = "jack"
m2["age"] = "15"

slice = append(slice, m1)
slice = append(slice, m2)

data, err = json.Marshal(slice)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data))

===>[{“age”:”14”,”name”:”alice”},{“age”:”15”,”name”:”jack”}]

6.序列化json时的tag的使用

1
2
3
4
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
}

7.反序列化

json.Unmarshal()

1
2
3
4
5
6
7
8
var mystudent Student

err = json.Unmarshal([]byte(string(data)), &mystudent)

if err != nil {
fmt.Println(err)
}
fmt.Printf("{name:%v,age:%v\n}", mystudent.Name, mystudent.Age)

12.单元测试

testing框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import "testing"

func TestAdd(t *testing.T) {
res := Add(5)
if res != 55 {
t.Fatalf("执行错误,期望值%v,实际值%v", 55, res)
return
}
t.Logf("成功执行")
}

func Add(a int) int {
return a + 50
}

文件名以_test.go结尾

测试函数以Test开头

go test -v

Pass表示成功 Fail表示失败

13.go进程

1.go协程

go test() 函数调用开启协程

2.MPG模式

M操作系统的主线程

P协程需要的上下文

G协程

3.设置CPU核数

1
2
3
4
5
6
7
8
9
10
11
12
import (
"fmt"
"runtime"
)

func main() {
//获取当前设备的CPU数
num := runtime.NumCPU()
fmt.Println(num)
//设置最大使用的CPU数
runtime.GOMAXPROCS(4)
}

4.全局互斥锁

lock sync.Mutex

//加锁

lock.Lock()

//解锁

lock.Unlock()

5.channel

channel是一个队列

channel是引用类型,需要make才能使用,管道是有类型的,intChar只能写入整数int

var intChan chan<- int 只写

var intChan <-chan int 只读

1
2
3
var intChan chan int
intChan = make(chan int)
fmt.Printf("%v", intChan)

写入

1
intChan <- 10

取出

1
2
num2 := <-intChan
fmt.Println(num2)

关闭

1
close(intChan)

遍历应该使用for range遍历,如果在遍历前没有关闭则会报错

6.管道和协程的共同使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
func putNumber(intChan chan int) {
for i := 1; i <= 8000; i++ {
intChan <- i
}
close(intChan)
}

func checkNumber(intChan chan int, resChan chan int, exitChan chan bool) {
var flag bool
for {
num, ok := <-intChan
if !ok {
break
}
flag = true
for i := 2; i < num; i++ {
if num%i == 0 {
flag = false
break
}
}
if flag {
resChan <- num
}
}
exitChan <- true
}

func main() {

intChan := make(chan int, 1000)
resChan := make(chan int, 2000)
exitChan := make(chan bool, 4)
go putNumber(intChan)
go checkNumber(intChan, resChan, exitChan)
go checkNumber(intChan, resChan, exitChan)
go checkNumber(intChan, resChan, exitChan)
go checkNumber(intChan, resChan, exitChan)

go func() {
for i := 0; i < 4; i++ {
<-exitChan
}
close(resChan)
}()
for v := range resChan {
fmt.Printf("取到的值%d\n", v)
}
}

通过select解决管道阻塞问题

当阻塞时会自动过渡到下一个case

1
2
3
4
select {
case v: <- intChan:
fmt.Println("读取")
}

使用recover解决管道阻塞问题

1
2
3
4
5
6
defer func() {
err := recover() //可以捕获到异常
if err != nil {
fmt.Println("err=", err)
}
}()

14.反射

反射(Reflection)在编程中通常被定义为在运行时检查程序的能力。这种能力使得一个程序能够操纵像变量、数据结构、方法和类型这样的对象的各种属性和行为。这一机制在Go中主要通过reflect标准库实现。

在Go中,接口(interface)和反射是紧密相关的。事实上,你可以认为接口是实现反射的“入口”。当你将一个具体类型的变量赋给一个接口变量时,这个接口变量内部存储了这个具体变量的类型信息和数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Any interface{}

func inspect(a Any) {
t := reflect.TypeOf(a)
v := reflect.ValueOf(a)
fmt.Println("Type:", t, "Value:", v)
}

func main() {
var x int = 10
var y float64 = 20.0
inspect(x) // Type: int Value: 10
inspect(y) // Type: float64 Value: 20
}

1.使用注意事项

reflect.Value.Kind() 获取变量的类别,返回的是一个常量

.Elem()相当于取到了地址,并且使用setInt()方法

1
2
3
4
5
6
7
8
9
10
11
func reflect1(b interface{}) {
rVal := reflect.ValueOf(b)
//tTyp := reflect.TypeOf(b)
rVal.Elem().SetInt(10)
}

func main() {
var num int = 10
reflect1(&num)
fmt.Println(num)
}

2.go反射的基本实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import (
"fmt"
"reflect"
)

type Cal struct {
Num1 int
Num2 int
}

func (c Cal) GetSub(name string) {
fmt.Printf("%s完成了%d-%d=%d", name, c.Num2, c.Num1, c.Num2-c.Num1)
}

func main() {
var c *Cal
var rval reflect.Value
c = &Cal{}
rval = reflect.ValueOf(c)
rval = rval.Elem()
rval.FieldByName("Num1").SetInt(3)
rval.FieldByName("Num2").SetInt(8)

//Call传递的参数为reflect.Value切片类型
var params []reflect.Value
params = append(params, reflect.ValueOf("tom"))
rval.Method(0).Call(params)
}

15.网络编程

1.服务监听

1
2
3
4
5
6
7
8
9
10
11
func main() {
listen, err := net.Listen("tcp", "0.0.0.0:8888")
if err != nil {
fmt.Println(err)
}

defer listen.Close() //延时关闭


fmt.Printf("begin to listen%v", listen)
}

循环等待

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func process(conn net.Conn) {
defer conn.Close()

for {
buf := make([]byte, 1024)

n, err := conn.Read(buf)
//n为接收的字节数
if err != nil {
fmt.Println(err)
return
}

fmt.Printf("recive message:%v", string(buf[:n]))
}
}

func main() {
listen, err := net.Listen("tcp", "0.0.0.0:8888")
if err != nil {
fmt.Println(err)
}

defer listen.Close() //延时关闭

//循环等待
fmt.Printf("begin to listen%v address\n", listen)
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("connect %v", conn)

}
go process(conn)
}
}

监听客户端,发送消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
//建立连接
conn, err := net.Dial("tcp", "10.34.111.137:8888")
if err != nil {
fmt.Println(err)
}
//从控制台输入内容
reader := bufio.NewReader(os.Stdin)
strs, err := reader.ReadString('\n')
if err != nil {
fmt.Println(err)
}
//发送请求
n, err := conn.Write([]byte(strs))
if err != nil {
fmt.Println(err)
}
fmt.Print(n)
}

循环发送客户端消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func main() {
//建立连接
conn, err := net.Dial("tcp", "10.34.111.137:8888")
if err != nil {
fmt.Println(err)
}

//发送请求
for {
//从控制台输入内容
reader := bufio.NewReader(os.Stdin)
strs, err := reader.ReadString('\n')
if err != nil {
fmt.Println(err)
}
strs = strings.Trim(strs, " \r\n")
if strs == "exit" {
return
} else {
n, err := conn.Write([]byte(strs))
if err != nil {
fmt.Println(err)
}
fmt.Print(n)
}

}
}

16.Redis+Go

1.redigo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"redigo-master/redis"
)

func main() {
conn, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
fmt.Println(err)
}

fmt.Println(conn)
}

2.go操作redis(string类型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
conn, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
fmt.Println(err)
}
defer conn.Close()
fmt.Println(conn)

_, err = conn.Do("set", "name", 6)
if err != nil {
fmt.Println(err)
}
value, err := redis.Int(conn.Do("get", "name"))
if err != nil {
fmt.Println(err)
}
fmt.Printf("%c", value)
}

3.go操作redis(hash类型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
conn, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
fmt.Println(err)
}
defer conn.Close()

_, err = conn.Do("HSet", "value", "name", "name")
if err != nil {
fmt.Println(err)
}
value, err := redis.String(conn.Do("HGetall", "value", "name"))
if err != nil {
fmt.Println(err)
}
fmt.Printf("%s", value)
}

获取所有的字段和值

1
value, err := redis.Strings(conn.Do("HGetall", "value"))

设置过期时间

1
conn.Do("expire", "value", "name", 10) //10秒过期

4.Redis连接池