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 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))
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)
5.布尔类型
只有false,true
6.字符串类型
字符串 一旦赋值就无法更改了,go中的字符串是不可变的
反引号输出
以字符串的原生形式输出,包括换行和特殊字符
字符串的拼接
1 fmt.Print("test" + "hahaha" )
7.数据类型的默认值 1 2 3 4 5 var a int var b float32 var v float64 var test bool var name string
8.类型转换 1.类型转换
Golang中数据类型不能自动转换
Go的数据类型可以是范围小到范围大,也可以范围大到范围小
2.基本类型转string 通用:
1 2 3 4 5 %v 值的默认格式表示 %+v 类似%v,但输出结构体时会添加字段名 %#v 值的Go语法表示 %T 值的类型的Go语法表示 %% 百分号
布尔值:
整数:
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)
指针:
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 )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 2 3 4 var a int = 10 var ptr *int = &afmt.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" ) defer file.Close() if failureX { return false } if failureY { return false } return true }
defer在函数执行完毕后,可以及时释放函数创建的资源。
当函数完毕后,系统会依次从defer栈中。取出语句,关闭资源
6.字符串常用的函数 1.len
len(str) ===>统计长度
按字节返回
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 );
8.查找子串是否在字符串内
判断str是否有substr
1 fmt.Println(strings.Contains("hahaha" , "ha" ))
1 fmt.Println(strings.Count("hahaha" ,"ha" ))
1 fmt.Println(strings.EqualFold("abc" ,"ABC" ))
1 2 fmt.Println(strings.Index("abc" ,"b" )) strings.LastIndex()
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" ) strings.HasStuffix("haha" ,"ha" )
7.日期函数的使用 1.基本使用 1 2 3 4 now := time.Now() fmt.Printf("%T,%v" , now, now)
获取年月日时分秒
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() }
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) 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 2 3 4 5 6 7 var intarr [5 ]int = [...]int {1 , 2 , 3 , 4 , 5 }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 }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 Personvar 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 } 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 Bb.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 { 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 Studenterr = 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 () { num := runtime.NumCPU() fmt.Println(num) 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 2 num2 := <-intChan fmt.Println(num2)
关闭
遍历应该使用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) inspect(y) }
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) 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 ) 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) 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 mainimport ( "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 )
4.Redis连接池