Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。
1.开发环境搭建
2.基础
2.1编译
go run 文件名 —–> 直接运行文件
go run build 文件名.exe —->编译成exe可执行文件
go build -o 指定文件名.exe —–>指定编译的文件名
2.2转义字符
\t 制表符
\n 换行
\\ 斜杠\
\“ 分号”
\r 回车
2.3注释
//
/* */
2.4变量的声明
指定变量类型,但是不赋值,此时变量采用默认值
var i int fmt.Println(i)
根据值自动推导类型
var name = "tom" var num = 10
:= 左侧的变量不应该是已经声明过的
name := test
一次声明多个变量
var a,b = 10,12
一次声明多个变量并使用类型推导
a,b,c=10,"test",0
定义多个全局变量
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等价
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,解决了编码乱码的问题
var c2 int = '值'
fmt.Println(c2)
// ----->20540
5.布尔类型
只有false,true
6.字符串类型
字符串 一旦赋值就无法更改了,go中的字符串是不可变的
反引号输出
以字符串的原生形式输出,包括换行和特殊字符
fmt.Print(`\n test`)
字符串的拼接
fmt.Print("test" + "hahaha")
7.数据类型的默认值
var a int //0
var b float32 //0
var v float64 //0
var test bool //false
var name string //""
8.类型转换
1.类型转换
Golang中数据类型不能自动转换
T(v)
//T为数据类型 int32 float64
//v为要转换的变量和数据
Go的数据类型可以是范围小到范围大,也可以范围大到范围小
2.基本类型转string
通用:
%v 值的默认格式表示
%+v 类似%v,但输出结构体时会添加字段名
%#v 值的Go语法表示
%T 值的类型的Go语法表示
%% 百分号
布尔值:
%t 单词true或false
整数:
%b 表示为二进制
%c 该值对应的unicode码值
%d 表示为十进制
%o 表示为八进制
%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x 表示为十六进制,使用a-f
%X 表示为十六进制,使用A-F
%U 表示为Unicode格式:U+1234,等价于"U+%04X"
浮点数与复数的两个组分:
%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:
%s 直接输出字符串或者[]byte
%q 该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
%x 每个字节用两字符十六进制数表示(使用a-f)
%X 每个字节用两字符十六进制数表示(使用A-F)
指针:
%p 表示为十六进制,并加上前导的0x
fmt.Sprintf()
var a1 int = 10
var str string= fmt.Sprintf("%d", a1)
print(str)
strconv.FormatInt()
第一个参数是你要转换的整数。
第二个参数是基数,表示你希望整数以哪种进制格式来表示。例如,基数10表示十进制,基数16表示十六进制。
var str1 = strconv.FormatInt(10,10)
//'f'为格式 1表示保留一位小数 64表示为float64
var str2 = strconv.FormatFloat(89.99, 'f', 1, 64)
3.String转基本类型
ParseBool
func ParseBool(str string) (value bool, err error)
返回字符串表示的bool值。它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。
ParseInt
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
func ParseUint(s string, base int, bitSize int) (n uint64, err error)
ParseUint类似ParseInt但不接受正负号,用于无符号整型。
ParseFloat
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。
var str3 string = "true"
var bl bool
bl, _ = strconv.ParseBool(str3)
print(bl)
函数会返回两个值,一个是转换后的值,另一个是错误,可以使用_忽略error
如果string为”hello”并且转为int这时会采用int的默认值0
9.指针
&取地址
fmt.Printf("i的地址为", &a)
*解引用
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
if 10 > 20 {
fmt.Println("data1")
}else {
fmt.Println("data2")
}
switch
switch的后面可以不用加break
switch(a){
case "a":
fmt.Print("test1")
case "b":
fmt.Print("test2")
case "c":
fmt.Print("test3")
default:
fmt.Print("test4")
}
for 循环
for i := 0; i < 100; i++ {
fmt.Println("test")
}
练习 空心金字塔
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乘法表
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.函数基本用法
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语言不支持重载
//如果要导出包,则将首字母大写
func ha() {
fmt.Print("ha")
}
2.go语言支持使用变量接收函数
func fun(a int ,b int) int {
return a+b
}
func main(){
a := fun
var res int = a(10,20)
fmt.Print(res)
}
并且可以当做参数传递
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支持自定义数据类型
type myInt int
var v myInt = myInt(90)
print(v)
go支持给返回值命名
func rs(a int,b int)(sum int,sub int){
sum = a + b
sub = a - b
return
}
可以使用args…类型接收多个参数
args的类型为Slice可以用[index]访问
func add(args ...int) int {
var res int
for i := 0; i < len(args); i++ {
res += args[i]
}
return res
}
3.init函数
在main函数之前执行,用于基本构建
被引入的包的init会先执行
func init() {
fmt.Print("init\n")
}
4.匿名函数
在定义的同时就完成了调用
func (a int,b int) int{
return a+b
}(10,30)
将匿名函数赋值给变量
fu := func (a float64,b float64) float64 {
return a * b
}
将匿名函数赋值给全局变量此时该匿名函数为全局匿名函数
5.defer
即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题
将defer语句放入到栈时,其相关的值也会拷贝一份
遵守先进后出的规则
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) ===>统计长度
按字节返回
fmt.Print(len("test")) //==>4
//遍历中文
str := "你好"
var res = []rune(str)
for i := 0; i < len(res); i++ {
fmt.Printf("%c\n", res[i])
}
2.Atoi
将字符串转为整数
n, err := strconv.Atoi("123")
if err != nil {
fmt.Println("转换错误", err)
} else {
fmt.Println("转换成功", n)
}
3.ItoA
将整数转为字符串
4.字符串转整数
ns := strconv.Itoa(123)
fmt.Printf("%s\n", ns)
5.字符串转为byte
var bytes = []byte("gogogo")
fmt.Printf("bytes=%v\n", bytes)
6.byte转为字符串
var strs = string([]byte{97, 98, 99})
fmt.Printf("strs=%v\n", strs)
7.10转2,8,16进制
返回值为字符串
str = strconv.FormatInt(123,2); //--->转为2进制
8.查找子串是否在字符串内
判断str是否有substr
fmt.Println(strings.Contains("hahaha", "ha")) //==>true
fmt.Println(strings.Count("hahaha","ha")) //文本内有几个substr ===>3
fmt.Println(strings.EqualFold("abc","ABC")) //比较字符串,不区分大小写 ==>true
fmt.Println(strings.Index("abc","b")) //查找substr第一次在str出现的位置,没有为-1 ===>1
strings.LastIndex() //===>与index相同,取得是最后一次出现的位置
9.替换字符
子字符串 需要替换的字符串 替换成的字符串 替换的执行次数
strings.Replace("go go go hello","go","golang",1)
10.分割字符
strings.Split("10,20,30,40",",") //===>拆分字符串","为拆分的字符
11.大小写转换
Strings.ToLower("GO")
strings.ToUpper("go")
12.去掉空格
strings.TrimSpace(" space ") //====>去掉左右两边的字符串
strings.Trim("hahah!","!") //=====>去掉左右两边的!字符
strings.TrimLeft() //=====>去掉左两边的!字符
strings.TrimRight() //=====>去掉右两边的!字符
13.判断字符
strings.hasPrefix("haha","ha") //判断是否以ha开头
strings.HasStuffix("haha","ha") //判断是否以ha结尾
7.日期函数的使用
1.基本使用
now := time.Now()
fmt.Printf("%T,%v", now, now)
//===>time.Time,2024-08-29 17:12:07.0249172 +0800 CST m=+0.002417901
获取年月日时分秒
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 纳秒
now.Unix() //时间戳
now.UnixNano() //时间戳微秒
3.go的错误执行机制
当go程序出现异常时,会抛出panic的异常,然后在defer中用recover捕获这个异常,然后正常处理
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类型的值,表示一个错误
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.定义
var test[10] float64
for i := 0; i < len(test); i++ {
fmt.Println(i)
}
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值
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.切片
切片是数组的引用,切片的长度可变
var 变量名 []类型
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 容量
定义切片
var 切片[]type = make([],len,[cap])
var test []float64 = make([]float64, 5, 10)
fmt.Println(test)
4.切片注意事项
arr[0:len(arr)] 简写为arr[:]
切片定义完后不能直接使用,需要引用一个数组,或者make一个空间来使用
切片可以再次切片,同时切边与使用它切片而成的切片共用同一个内存地址,因此一个改变,两个都会变
var slice1 []int = []int{100, 200, 300}
//通过append给slice追加元素
slice1 = append(slice1, 2)
fmt.Println(slice1)
//将自身追加给自身
slice1 = append(slice1, slice1...)
fmt.Println(slice1)
使用copy完成拷贝
var a []int = []int{10, 20, 30, 40, 50}
var slice2 = make([]int, 10, 20)
copy(slice2, a)
fmt.Println(slice2)
slice具有引用传递
func test(slice []int){
slice[0] =100
}
//当传递一个切片时,该切片会改变
5.修改字符串
将string转为[]byte 或者[]rune ->修改 ->重写成string
var str string = "this is life"
arr := []byte(str)
arr[1] = 'a'
str = string(arr)
fmt.Println(str)
修改为中文
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是无序的
var a map[string]string
a["1"] = "一"
fmt.Println(a) //报错
var a map[string]string = make(map[string]string, 10)
a["1"] = "一"
fmt.Println(a)
定义一个map,map里面还是map
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)
查找键值对
cal, ok := a["1"]
if ok {
fmt.Println(cal)
} else {
fmt.Println("不存在")
}
3.map的遍历
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)
}
//计算长度
length := len(a)
fmt.Println(length)
4.map切片
[]map[string]string
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动态添加属性
//动态增加
new_city := map[string]string{
"beijing": "beijing",
}
cityies = append(cityies, new_city)
fmt.Println(cityies)
5.map的排序
map原本是无序的
map1 := make(map[int]int)
map1[10] = 100
map1[3] = 90
map1[7] = 5
map1[20] = 30
将map的key放入切片中
对切片进行排序
遍历切片,按照key来输出map的值
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
type 结构体名 struct{
field1 type
field2 type
}
2.声明结构体
var person Person
var person Person = Person{}
var person *Person = new(Person)
(*person).属性="值"
//也可以简化访问
var person *Person = &Person()
3.序列化
通过tag给struct指定tag,用于序列化,保证兼容性
type Person struct{
name string `json:"name"`
age int `json:age`
}
//序列化
jsonStr, _ := json.Marshal(map1)
fmt.Println(jsonStr)
2.方法
type Person struct {
name string
age int
}
// 给person绑定一个方法
func (p Person) test() {
fmt.Println("test")
}
3.继承
嵌套匿名结构体实现继承
type Animal struct{
name string
move string
}
type Dog struct{
Animal //嵌套匿名结构体实现继承
age int
}
匿名结构体字段访问可以简化
直接变量名.继承的父类的属性
4.多重继承
可以嵌入一个基本数据类型
type A struct{
Name string
}
type B struct{
int
A
}
//访问
var b B
b.int = 30
嵌套多个匿名结构体,实现多重继承
5.接口
interface不能包含任何变量
//定义接口
type C interface {
Start()
End()
}
type D struct {
}
//实现接口方法
func (d D) Start(){
}
func (d D) End(){
}
一个自定义类只有实现了某个接口,才能将自定义类型的实例赋值给接口类型
只要是自定义类型都可以实现接口,不仅仅是结构体
func (i integer)Start(){
}
6.类型断言
b = a.(类型) 类型断言
func main(){
var a interface{}
var b float32 = 10
a = b
b = a.(float32)
}
在进行断言时,带上检测机制
if b, ok := a.(float64); ok {
fmt.Printf("%T %v", b, b)
} else {
fmt.Printf("error")
}
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()
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
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.将文件移动到另一个目录
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.接收参数的传递
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.获取解析命令行参数
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.序列化对象
student := Student{
Name: "tom",
Age: 18,
}
data, err := json.Marshal(&student)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data))
2.序列化map
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.序列化切片
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的使用
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
}
7.反序列化
json.Unmarshal()
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框架
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核数
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 只读
var intChan chan int
intChan = make(chan int)
fmt.Printf("%v", intChan)
写入
intChan <- 10
取出
num2 := <-intChan
fmt.Println(num2)
关闭
close(intChan)
遍历应该使用for range遍历,如果在遍历前没有关闭则会报错
6.管道和协程的共同使用
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
select {
case v: <- intChan:
fmt.Println("读取")
}
使用recover解决管道阻塞问题
defer func() {
err := recover() //可以捕获到异常
if err != nil {
fmt.Println("err=", err)
}
}()
14.反射
反射(Reflection)在编程中通常被定义为在运行时检查程序的能力。这种能力使得一个程序能够操纵像变量、数据结构、方法和类型这样的对象的各种属性和行为。这一机制在Go中主要通过
reflect
标准库实现。
在Go中,接口(interface)和反射是紧密相关的。事实上,你可以认为接口是实现反射的“入口”。当你将一个具体类型的变量赋给一个接口变量时,这个接口变量内部存储了这个具体变量的类型信息和数据。
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()方法
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反射的基本实践
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.服务监听
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)
}
循环等待
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)
}
}
监听客户端,发送消息
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)
}
循环发送客户端消息
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
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类型)
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类型)
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)
}
获取所有的字段和值
value, err := redis.Strings(conn.Do("HGetall", "value"))
设置过期时间
conn.Do("expire", "value", "name", 10) //10秒过期