::: hljs-center

image.png

:::

::: hljs-center

Go语言基础

:::

++Go语言的优势++

1.高性能,高并发

拥有和java,c++相媲美的性能,内嵌了对高并发的支持(不需引入第三方库)

2.语法简单,学习曲线平缓

如下仅用十行代码即实现了可承载静态访问,支持高并发的服务器

1
2
3
4
5
6
7
8
9
10
11
12

package main

import (
"net/http"
)

func main(){
http.Handle("/",http.FileServer(http.Dir(".")))
http.ListenAndServe(":8080",nil)
}

3.丰富的标准库

go提供大量强大的标准库,很多时候不需要使用第三方库即可实现复杂的功能,大大降低了学习成本

4.完善的工具链

包括包管理,ide等完善稳定的工具

5.静态编译

在go语言中,默认所有结果都是静态链接的,只需拷贝编译后的一个唯一文件即可部署运行,镜像体积可以控制的非常小,部署方便快捷。

6.快速编译

拥有静态语言中最快速的编译速度,在字节跳动大量的微服务在线上部署之前编译时间小于一分钟,在真正本地开发的时候,修改完一行代码都能在一秒钟之内增量编译完成

7.跨平台

8.垃圾回收

go语言带有类似java的垃圾回收能力,写代码时只需专注业务逻辑,无需考虑内存释放

++Go的基础语法++

go的hello world

go的helloworld长这样:

1
2
3
4
5
6
7
8
9
10
11
12
//程序的入口包
package main

//导入了标准库里的fmt,这个库主要用于输出格式化字符串等
import (
"fmt"
)

func main() {
fmt.Println("hello world")
}

在硬盘中以main.go保存,想直接运行程序使用

go run main.go

go支持直接编译成二进制运行,使用

go build main.go

随后输入

./main

不出意外将输出

hello world

变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {
//go的变量声明

//go拥有自动推断变量类型,方式如下
var a = 1
//当然也可以显式的声明变量类型
var b int = 2
//还有这种更常用的方式
text := "hello world"
//常量的声明
//此外go的浮点数不使用double,float区分
//float64代表8精度的浮点数,32代表4精度的浮点数(double)
const pi float64 = 3.14

fmt.Println(text, a, b, pi)
}

if-else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {

//写法与大部分语言类似,不同的是if后面不需要括号
//(不是很习惯,写了括号保存了的时候编辑器还是会去掉括号)
if 7%2 == 0 {
fmt.Println(7, "is even")
} else {
fmt.Println(7, "is odd")
} //另外,大括号是必要的,只有一行的逻辑的时候也去不掉

//
if num := 9; num >= 0 {
fmt.Println("9 >= 0")
} else if num <= 10 {
fmt.Println("9 <=10")
}
}

循环

go中没有while,do while等,只有for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {

for {
//死循环
fmt.Println("hhh")
}

for i := 0; i < 3; i++ {
fmt.Println(i)
}

for n:=0;n<5;n++{
if(n==2) {
continue;
}
fmt.Println(n)
}

}

switch

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
package main

import (
"fmt"
"time"
)

func main() {

a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other number")
} //golang中的switch不会像java,c一样不加break语句遇到符合的将继续运行下去
//如程序这里执行到“case 2”的逻辑便会返回

t := time.Now()
switch {
// switch的分支中支持使用条件判断,可以这样来取代if-else
case t.Hour() < 12:
fmt.Println("it's before noon")
default:
fmt.Println("it's after noon")

}

}

切片

在真正业务逻辑中很少使用数组,更多使用的是 切片
切片相较于数组长度不可变的局限性,你可以任意的去更改它的长度,拥有更多丰富的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {
//使用make创建一个切片
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get", s[2])

s = append(s, "d") //向切片追加一个新的元素
fmt.Println(s) //输出 a b c d

c := make([]string, len(s))
copy(c, s) //将s拷贝至c切片中
fmt.Println(c)

//拥有像python那样的切片操作
fmt.Println(s[0:2]) //输出a,b 区间为左闭右开
fmt.Println(s[:4]) //输出a,b,c,d
}

map

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

import "fmt"

func main() {
m := make(map[string]int) //string 为key类型,int为value类型
m["apple"] = 10
m["pen"] = 20
fmt.Println(m["pen"])

r, contains := m["apple"]
fmt.Println(r, contains) //输出 10 true (contains 接收是否存在元素
}

range

对于切片或map,可以使用range来快速遍历。这样代码将更加简洁

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
package main

import "fmt"

func main() {
var arry [5]int
arry[0] = 1
arry[1] = 2
arry[2] = 3
arry[3] = 4
arry[4] = 5
//输出结果:
//idx= 0 value= 1
//idx= 1 value= 2
//idx= 2 value= 3
//idx= 3 value= 4
//idx= 4 value= 5
for idx, value := range arry {
fmt.Println("idx=", idx, " value=", value)
}

m := make(map[string]int)
m["apple"] = 10
m["pen"] = 5
for k, v := range m {
fmt.Println("item=", k, " price=", v)
}

}

函数

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
package main

import "fmt"

// 返回a+b的结果
// 注意声明方式
func add(a int, b int) int {
return a + b
}
func add0(a, b int) int {
return a + b
}

// go中原生支持返回多个值
// 该函数返回指定key对应的值以及是否存在该键值对
func exists(m map[string]string, k string) (value string, contains bool) {
v, ok := m[k]
return v, ok
}

func main() {
res := add(1, 2)
fmt.Println(res) //out: 3

value, contains := exists(map[string]string{"a": "A"}, "a")
fmt.Println(value, contains) //out:A true
}

指针

go也初步支持指针,但远没有c那样强大,以下是一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

// 视图将n+2,但实际上是无效的
// 传过来的n参数仅为一个拷贝
func add2(n int) {
n += 2
}

// 有效的写法(将指针作为参数参数
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 2
add2(n)
fmt.Println(n) //2
add2ptr(&n)
fmt.Println(n) //4
}

结构体

类似java中的类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

// 结构体
type user struct {
name string
password string
}

// 结构体方法
func (u *user) resetPassword(newpassword string) {
u.password = newpassword
}

func main() {
a := user{name: "zsh", password: "1024"}
b := user{name: "zyy"}
b.password = "1024"
fmt.Println(a.name, "password: ", a.password) //out: zsh password: 1024
a.resetPassword("zyy i love u")
fmt.Println(a.password) //out: zyy i love u
}

异常处理

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
package main

import (
"errors"
"fmt"
)

// 结构体
type user struct {
name string
password string
}

// error表示可能出现的错误
func findUser(users []user, name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil
}
}
//未寻找到用户,new一个errors作为第二个参数返回
return nil, errors.New("not found")
}

func main() {
u, err := findUser([]user{{"zsh", "1024"}}, "zsh")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name)
}

字符串操作

strings下包含了许多对字符串的处理

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

import (
"fmt"
"strings"
)

func main() {
text := "zsh"
fmt.Println(strings.Contains(text, "zs")) //out: true
fmt.Println(strings.Contains(text, "zyy")) //out: false
fmt.Println(strings.Count(text, "z")) //out: 1 (统计字符出现次数
fmt.Println(len(text)) //out: 3 (统计长度
fmt.Println(len(text)) //out: 3 (统计长度
fmt.Println(strings.Split(text, "-")) //out [z,s,h] (切割字符串,返回数组
}

JSON处理

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
package main

import (
"encoding/json"
"fmt"
)

type user struct {
//如果key需要小写的话,在后面追加`json: key"
idx int `json:"idx"`
Name string
Password string
}

func main() {
zsh := user{idx: 1, Name: "zsh", Password: "1024"}
//将结构体序列化为json文本
buf, err := json.Marshal(zsh)
if err != nil {
panic(err)
}
fmt.Println(buf) // [12334....]
fmt.Println(string(buf)) // {"Name":"zsh","Password":"1024"}

//反序列化
var zshCpy user
json.Unmarshal(buf, &zshCpy)
fmt.Println(zshCpy.Name) //zsh
}

时间处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"time"
)

func main() {
//获取当前时间
now := time.Now()
fmt.Println(now) //out: 2023-01-16 05:51:18.9810204 +0800 CST m=+0.002828701
//构造一个时间
t := time.Date(2022, 1, 1, 20, 20, 30, 0, time.UTC)
fmt.Println(t.Year()) //out: 2022
//减去时间段
diff := now.Sub(t)
fmt.Println(diff) //9097h33m1.4173081s
//获取时间毫秒数
fmt.Println(now.Unix())
}

字符串与数字的转换

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

import (
"fmt"
"strconv"
)

func main() {
//参数为: 字符串,精度
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) //1.234
//参数为: 字符串,进制(0为自动推测),精度
n, _ := strconv.ParseInt("10", 10, 64)
fmt.Println(n) //10

n2, _ := strconv.Atoi("123")
fmt.Println(n2) //123

}

进程信息

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

import (
"fmt"
"os"
)

func main() {
//使用命令 go run main.go a b c d
//输出:
//[C:\Users\hasee\AppData\Local\Temp\go-build580179449\b001\exe\main.exe a b c d]
//第一个参数为二进制文件地址 接下来是args
fmt.Println(os.Args)
//获取环境变量
fmt.Println(os.Getenv("PATH"))
//写入环境变量
fmt.Println(os.Setenv("zsh", "1024"))
}

++小结++

标准库

fmt 提供格式化输入输出
os 提供与操作系统交互的函数
string 提供字符串操作的函数
net 提供网络编程的函数
strconv 提供数字与字符之间的转换操作
time 时间处理
encoding/json json的相关操作

一个练习的小项目,二分查找猜谜游戏

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
package main

import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)

func main() {
//使用当前时间戳初始化随机种子
//否则会输出同一个数
rand.Seed(time.Now().UnixNano())
maxNum := 100
rNumber := rand.Intn(maxNum)

fmt.Println("Please input your anser")
//转换为一个只读的流
reader := bufio.NewReader(os.Stdin)

for {
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
//处理换行符
input = strings.TrimSuffix(input, "\r\n")
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("error input,it not is number")
return
}
fmt.Println("your guess is", guess)
if guess > rNumber {
fmt.Println("你输入的数字大于答案,请再次输入")
} else if guess < rNumber {
fmt.Println("你输入的数字小于答案,请再次输入")
} else {
fmt.Println("Accept!")
break
}
}

}

系统会预生成一个[0,100)的随机数
用户通过连续的输入将得到
“输入的数大于答案” or “输入的数小于答案”
直到猜测正确完成游戏
Riddle.png