环境搭建
VSCode 下载地址: https://code.visualstudio.com/
下载慢解决方法:
- 官网找到需要的版本后,点击下载,然后复制下载链接
- 将红框内地址更改为国内镜像地址
vscode.cdn.azure.cn
- 浏览器复制链接后重新下载
Golang 下载地址: https://golang.google.cn/dl/
-
VSCode 安装 Go 插件
-
更新 Go 工具
在更新之前需要说明的是,由于国内政策,没办法直接更新,需要代理
1# 旧版,已废弃 2go env -w GO111MODULE=on 3go env -w GOPROXY=https://goproxy.io,direct
1# 新版改成如下链接 2go env -w GO111MODULE=on 3go env -w GOPROXY=https://proxy.golang.com.cn,direct
我这里报错
warning: go env -w GOPROXY=... does not override conflicting OS environment variable
原因是已经有一个了,查看了环境变量是PC前主人的,手动更改了环境变量,而后关掉并重新打开 VSCode,继续下面的步骤。- 在Visual Studio Code中,打开命令面板的**“帮助**>显示所有命令”。 或者使用键盘快捷方式 (Ctrl+Shift+P)
Go: Install/Update tools
搜索 ,然后从托盘运行命令- 出现提示时,选择所有可用的 Go 工具,然后单击“确定”。
- 等待 Go 工具完成更新。
time
time.sleep
golang
的睡眠函数time.sleep(1)
和time.sleep(1 * time.Second)
前者仅睡眠$1$纳秒,后者才是$1$秒
time.Now().Format(“2006-01-02 15:04:05”)
必须是这个时间点,才能格式化。
TrimRight(TrimLeft)
函数:strings.TrimRight(s string, cutset string)
定义:把cutset
里面的字符串拆分成字符,然后从右往左,逐个比对字符串中的每个字符,直到遇到没有在cutset
中出现的字符。
1package main
2
3import (
4 "fmt"
5 "strings"
6)
7
8func main() {
9 fmt.Println("aabbccdd\t:", strings.TrimLeft("aabbccdd", "abcd")) // 空字符串
10 fmt.Println("aabbccdde\t:", strings.TrimLeft("aabbccdde", "abcd")) // e
11 fmt.Println("aabbeccdd\t:", strings.TrimLeft("aabbedcba", "abcd")) // edcba
12 fmt.Println("aabbccdd\t:", strings.TrimRight("aabbccdd", "abcd")) // 空字符串
13 fmt.Println("aabbccdde\t:", strings.TrimRight("aabbccdde", "abcd")) // aabbccdde
14 fmt.Println("aabbeccdd\t:", strings.TrimRight("aabbedcba", "abcd")) //aabbe
15}
16
17**********************************
18aabbccdd :
19aabbccdde : e
20aabbeccdd : edcba
21aabbccdd :
22aabbccdde : aabbccdde
23aabbeccdd : aabbe
iota
iota
是Golang
语言的常量计数器,只能在常量的const
表达式中使用,在const
关键字出现的时将被重置为0
,const
中每新增一行常量声明iota
值自增'1’(iota
可以理解为const
语句块中的行索引)。
iota
只能在常量的表达式中使用,iota
在const
关键字出现时将被重置为0
,不同const
定义块互不干扰。
1const a = iota // a = 0 => iota = 0, a = iota
2const (
3 b = iota // b = 0 => iota = 0, b = iota
4 c // c = 1 => iota ++, c = iota
5 d // d = 2 => iota ++, d = iota
6)
fallthrough
Go
里面switch
默认相当于每个case
最后带有break
,匹配成功后不会自动向下执行其他case
,而是跳出整个switch
, 但是可以使用fallthrough
强制执行后面的**case
**代码。
append()
append()
用来将元素添加到切片末尾并返回结果。- 调用
append()
函数必须用原来的切片变量接收返回值 append()
追加元素,如果slice
还有容量的话,就会将新的元素放在原来slice
后面的剩余空间里,当底层数组装不下的时候,Go
就会创建新的底层数组来保存这个切片,slice
地址也随之改变。- 分配了新的地址后,再把原来
slice
中的元素逐个拷贝到新的slice
中,并返回。
1package main
2import "fmt"
3//切片进阶操作
4
5func main(){
6 //append()为切片追加元素
7 s1 := []string {"火鸡面","辛拉面","汤达人"}
8 fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n",s1,len(s1),cap(s1))
9
10 //调用append函数必须用原来的切片变量接收返回值
11 s1 = append(s1,"小当家") //append动态追加元素,原来的底层数组容纳不下足够多的元素时,切片就会开始扩容,Go底层数组就会把底层数组换一个
12 fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n",s1,len(s1),cap(s1))
13
14 //调用append添加一个切片
15 s2 := []string{"脆司令","圣斗士"}
16 s1 = append(s1,s2...)//...表示拆开切片,再添加
17 fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d",s1,len(s1),cap(s1))
18}
19
20/*
21 s1=[火鸡面 辛拉面 汤达人] len(s1)=3 cap(s1)=3
22 s1=[火鸡面 辛拉面 汤达人 小当家] len(s1)=4 cap(s1)=6
23 s1=[火鸡面 辛拉面 汤达人 小当家 脆司令 圣斗士] len(s1)=6 cap(s1)=6
24*/
string 和 byte 的转换
1s1 := "hello"
2b := []byte(s1) // string to []byte
3s2 := string(b) // []byte to string
Interface 用法
匿名字段和内嵌结构体
当我们需要重写一个 “实现了某个接口的结构体” 的部分方法,而其它方法保持不变 的时候,就需要用到这种用法。
1package main
2
3import (
4 "fmt"
5)
6
7type Interface interface {
8 Less(i, j int) bool
9 Swap(i, j int)
10}
11
12// Array 实现Interface接口
13type Array []int
14
15func (arr Array) Less(i, j int) bool {
16 return arr[i] < arr[j]
17}
18
19func (arr Array) Swap(i, j int) {
20 arr[i], arr[j] = arr[j], arr[i]
21}
22
23// 匿名接口(anonymous interface)
24type reverse struct {
25 Interface
26}
27
28// 重写(override)
29func (r reverse) Less(i, j int) bool {
30 return r.Interface.Less(j, i)
31}
32
33// 构造reverse Interface
34func Reverse(data Interface) Interface {
35 return &reverse{data}
36}
37
38func main() {
39 arr := Array{1, 2, 3}
40 rarr := Reverse(arr)
41 fmt.Println(arr.Less(0,1))
42 fmt.Println(rarr.Less(0,1))
43}
引入其他文件的struct
struct的属性是否被导出,也遵循大小写的原则:首字母大写的被导出,首字母小写的不被导出。
internal 包
protobuf
1type NATSession struct {
2 Vrf int32 `protobuf:"varint,1,opt,name=vrf,proto3" json:"vrf,omitempty"`
3}
在 Golang 中,结构体中的int32
后面的protobuf
标记是用于指定结构体字段在序列化和反序列化时的编码格式。这是由于该结构体可能用于Protocol Buffers(protobuf)的序列化和反序列化操作。
omitempty
- 没有 json 标记时默认字段名称大写则序列化时默认使用该字段名。
- 没有 json 标记时默认字段名称小写则序列化不会包含在内。
- 有 json 标记时没有 omitempty 标记,序列化时将使用配置的 json 名称(字段大写时)
- 有 json 标记时有 omitempty 标记,序列化时将忽略有 omitempty 并且没有赋值的字段,当具有值时则显示。
- 有 json 标记时但名称为-时,当该字段值为空,则序列化时将忽略。
当 struct 中的字段没有值时, json.Marshal()
序列化的时候不会忽略这些字段,而是默认输出字段的类型零值(例如int
和float
类型零值是 0,string
类型零值是""
,对象类型零值是 nil)。如果想要在序列序列化时忽略这些没有值的字段时,可以在对应字段添加omitempty
tag。
1type User struct {
2 Name string `json:"name"`
3 Email string `json:"email"`
4 Hobby []string `json:"hobby"`
5}
6
7func omitemptyDemo() {
8 u1 := User{
9 Name: "左右逢源",
10 }
11 // struct -> json string
12 b, err := json.Marshal(u1)
13 if err != nil {
14 fmt.Printf("json.Marshal failed, err:%v\n", err)
15 return
16 }
17 fmt.Printf("str:%s\n", b)
18}
19
20/*
21 str:{"name":"左右逢源","email":"","hobby":null}
22*/
1// 在tag中添加omitempty忽略空值
2// 注意这里 hobby,omitempty 合起来是json tag值,中间用英文逗号分隔
3type User struct {
4 Name string `json:"name"`
5 Email string `json:"email,omitempty"`
6 Hobby []string `json:"hobby,omitempty"`
7}
8
9/*
10 str:{"name":"左右逢源"} // 序列化结果中没有email和hobby字段
11*/
简式的变量声明
仅可以在函数内部使用
1package main
2myvar := 1 //error
3func main() {
4}
重复声明变量
不能在一个单独的声明中重复声明一个变量,但在多变量声明中这是允许的,其中至少要有一个新的声明变量。 重复变量需要在相同的代码块内,否则将得到一个隐藏变量。
1package main
2func main() {
3 one := 0
4 one := 1 //error
5}
1package main
2func main() {
3 one := 0
4 one, two := 1,2
5 one,two = two,one
6}
命名返回值
Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。
没有参数的 return
语句返回已命名的返回值。也就是 直接
返回。
1package main
2
3import "fmt"
4
5func split(sum int) (x, y int) {
6 x = sum / 3
7 y = sum - x
8 return
9}
10
11func main() {
12 fmt.Println(split(9))
13}
14
15/*
16 3 6
17*/
for 循环
Go 只有一种循环结构:for
循环。
基本的 for
循环由三部分组成,它们用分号隔开:
- 初始化语句:在第一次迭代前执行
- 条件表达式:在每次迭代前求值
- 后置语句:在每次迭代的结尾执行
初始化语句和后置语句是可选的。
1func main() {
2 sum := 1
3 for ; sum < 1000; {
4 sum += sum
5 }
6 fmt.Println(sum) // 1024
7}
去掉分号,可以理解为 while 循环。
1func main() {
2 sum := 1
3 for sum < 1000 {
4 sum += sum
5 }
6 fmt.Println(sum) // 1024
7}
if 语句
if
语句可以在条件表达式前执行一个简单的语句,该语句声明的变量作用域仅在 if
之内。
在 if
的简短语句中声明的变量同样可以在任何对应的 else
块中使用。
1func pow(x, n, lim float64) float64 {
2 if v := math.Pow(x, n); v < lim {
3 return v
4 } else {
5 fmt.Printf("%g >= %g\n", v, lim)
6 }
7 // 这里开始就不能使用 v 了
8 return lim
9}
switch 语句
没有条件的 switch 同 switch true
一样,此时相当于是if-then-else
语句。
defer 语句
defer 语句会将函数推迟到外层函数返回之后执行。
推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。
1func main() {
2 fmt.Println("counting")
3
4 for i := 0; i < 3; i++ {
5 defer fmt.Println(i)
6 fmt.Println(i)
7 }
8
9 fmt.Println("done")
10}
11/*
12 counting
13 0
14 1
15 2
16 done
17 2
18 1
19 0
20*/