环境搭建
第一步肯定是要先有个环境可以上手了,我们选择使用WSL(Ubuntu)+VSCode进行
安装参考:https://go.dev/doc/install
WSL(Ubuntu)
# 卸载旧版本
$ sudo apt remove golang
$ sudo apt autoremove
# 安装新版本
# 这里我们使用最新的稳定版
# 以root或sudo运行安装命令
$ sudo rm -rf /usr/local/go
$ sudo tar -C /usr/local -xzf go1.17.6.linux-amd64.tar.gz
# 添加/usr/local/go/bin/到PATH环境变量
$ export PATH=$PATH:/usr/local/go/bin
# 配置代理,国内环境嘛
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
# 配置GOPATH工作目录
$ go env -w GOPATH=~/developer/gowork
$ go env -w GOMODCACHE=~/developer/gowork/pkg/mod
# 将$GOPATH/bin也添加到PATH变量,方便我们安装的程序可以直接使用
$ export PATH=$PATH:/usr/local/go/bin:~/developer/gowork/bin
# 将上述导入变量也添加到~/.profile中
$ source ~/.profile
# 安装完成,查看一下版本信息
$ go version
go version go1.17.6 linux/amd64
配置文档
配置一份速查文档能提高我们的学习效率,养成一份先查文档再搜索的习惯。毕竟一个内置库的使用也去“百度”搜索,难免不被笑话,哈哈
godoc
官方提供的一种离线文档查看方式
$ go install golang.org/x/tools/cmd/godoc@latest
# 运行godoc
$ godoc
# 或指定访问地址
# 浏览器访问localhost:6060查看文档
$ godoc -http localhost:6060
DevDocs
这也是我更喜欢的方式
- 使用Chrome浏览器访问 https://devdocs.io/
- 将应用创建为桌面打开的应用
- Preferences 添加自己需要的语言,这里当然选Go
- Offline Data 安装离线数据
开发工具
开发工具选用VSCode,主要原因是和WSL的联动相当好,且精简。另一个好用的IDE是GoLand
- 下载打开VSCode
- Extensions 搜索安装Go扩展
- Ctrl+Shift+P 调出命令面板,搜索 go: install tools,安装一些必须的go包
- Remote Explorer 打开WSL中我们创建的学习目录(也可以直接在WSL中输入code PATH快速打开)
基础语法
环境和文档已配置完成,第一天的学习正式开始
$ mkdir day1
$ code day1
优势
- 开源,大量的标准库
- 编译性语言,编译为单文件并支持交叉编译
- 自动的内存管理和垃圾回收机制
- 语言简单,更易的并发支持
- 风格优雅统一
- 向后兼容
运行方式
新建一个main.go,尝试写入以下代码
// package 软件包的名称,main为入口软件包
package main
// 导入的软件包
import "fmt"
// 入口函数
func main() {
// 包中的方法调用
fmt.Println("Hello World!")
}
第一个程序我们就写好了,下面运行一下
# 编译并执行应用程序,不生产二进制文件
$ go run main.go
Hello World!
# 生成二进制可执行文件
$ go build main.go
$ ./main
# 完美输出我们向世界的问好
Hello World!
变量
// 函数外我们使用var关键字进行声明
var firstName string
var age int
// 声明多个相同类型的变量
var firstName, lastName string
// 使用块进行变量声明
var (
firstName, lastName string
age int
)
// 声明时进行赋值操作
var (
firstName string = "John"
lastName string = "Doe"
age int = 32
)
// 支持类型推断的方式进行赋值
var (
firstName = "John"
lastName = "Doe"
age = 32
)
var (
firstName, lastName, age = "John", "Doe", 32
)
// 函数内声明
func main() {
// 函数外必须使用var关键字
// 函数内使用:=进行初始类型推断
firstName, lastName := "John", "Doe"
age := 32
fmt.Println(firstName, lastName, age)
}
常量
// 常量使用const关键字,可以使用但不适用
const A = 1
// 可以使用[iota](https://github.com/golang/go/wiki/Iota)关键字标识进行递增操作
const (
// 0
A = iota
// 1
B
// 2
C
)
// 使用_进行值忽略
const (
// 忽略第一个0值
_ = iota
A = iota * 2
// 4
B
// 6
C
)
数据类型
整数
// 提供int8,int16,int32,int64和uint8,uint16,uint32,uint64
// 还有一些类型别名
type rune = int32
// 取值返回可以查看[builtin.go](https://go.dev/src/builtin/builtin.go)
var i1 int8 = 127
var i16 int16
var i32 int32
var i64 int64
// 默认值为0
fmt.Println(i16, i32, i64)
// 还可以用于表示 Unicode 字符码
var i32a int32 = 'G'
var i32b rune = '你'
浮点数
// 最大值可以通过 math.MaxFloat32 查看
var f32 float32 = math.MaxFloat32
var f64 float64
布尔值
var b1 bool
// 默认值为false
fmt.Println(b1)
字符串
var s1 string = "Hello"
var s2 string
// 默认值空
fmt.Println(s2)
类型转换
内置方法
var i16 int16 = 127
var i32 int32 = 127
fmt.Println(int32(i16) + i32)
strconv
// strconv包提供了类型转换的一些方法
import "strconv"
// _用于忽略的变量,不会使用
i, _ := strconv.Atoi("-42")
s := strconv.Itoa(-42)
// 提供了一些字符串转换方法
b, err := strconv.ParseBool("true")
f, err := strconv.ParseFloat("3.1415", 64)
i, err := strconv.ParseInt("-42", 10, 64)
u, err := strconv.ParseUint("42", 10, 64)
// 转换为ASCII字符串
q := strconv.QuoteToASCII("Hello, 世界")
函数
// 语法
func name(parameters) (results) {
body-content
}
// 常规写法
func sum(n1 string, n2 string) int {
int1, _ := strconv.Atoi(n1)
int2, _ := strconv.Atoi(n2)
return int1 + int2
}
// 定义返回的值
func sum(n1 string, n2 string) (result int) {
result = 10
// 无返回会将result返回为当前值
// => 10
return
// 返回为return值
// => 20
return 20
}
// 多个返回值
func sum(n1 string, n2 string) (result1 int, result2 int) {
return 10, 20
}
指针
// 按值传递
// & 运算符使用其后对象的地址。
// * 运算符取消引用指针。
func main() {
firstName := "John"
updateName(&firstName)
fmt.Println(firstName)
}
func updateName(name *string) {
*name = "David"
}
数组
相同元素组成的集合,数组在初始化后无法改变大小
// 定义
var a [3]int
a[0] = 1
// 初始化
a := [5]int{1, 2, 3}
// [1 2 3 0 0] 剩余元素为类型默认值
fmt.Println(a)
// 省略数量的初始化
a := [...]int{1, 2, 3}
// 省略数量,但指定某个位置的初始化
a := [...]int{10: 3, 20: 5}
// len(a) = 21
a := [...]string{10: "A", 20: "C"}
// 多维数组
var a1 [3][3][3]int
var a1 [3][3]int
a1 := [3][3]int{{1}, {2}}
切片
切片可以认为是动态数组
// 切片和数组声明方式类似,但切片可以动态追加元素
a := []int{1, 2, 3, 4, 5, 6, 7}
// s[i:p]
fmt.Println(a[:])
fmt.Println(a[2:3])
fmt.Println(a[:3])
// 追加
// func append(slice []Type, elems ...Type) []Type
a = append(a, 8)
a = append(a, 8, 9)
b := []int{0, 0, 0}
// 追加其他切片
a = append(a, b...)
// 使用len查看长度,使用cap查看容量
// 切片容量不足时会自动翻倍递增
fmt.Println(len(a), cap(a))
// 删除项
// 没有提供删除的方法,但可以通过切片追加进行实现
a = append(a[:3], a[3+1:]...)
// 数组元素为引用类型,更改切片元素会影响基础数组
a := []int{1, 2, 3, 4, 5, 6, 7}
slice1 := a[0:2]
slice2 := a[1:3]
slice1[1] = 9
// 所有关联值都改为了相同值
fmt.Println(a, slice1, slice2)
// 使用make分配了指定容量的切片
// func make(t Type, size ...IntegerType) Type
slice2 := make([]int, 3)
// 使用存在指定容量的切片
slice2 := []int{0, 0, 0}
// 使用内置copy创建副本
// func copy(dst, src []Type) int
copy(slice2, a[1:3])
map
items := map[int]string{1: "A", 2: "B", 3: "C"}
fmt.Println(items)
// 使用make创建一个动态映射
items := make(map[int]string)
items := map[int]string{}
// 方法外使用var创建
var items = make(map[int]string)
items[3] = "C"
// false
fmt.Println(items == nil)
// 以下会创建一个nil的映射关系,无法动态添加,但可以查询和删除
var items = map[int]string
// true
fmt.Println(items == nil)
// panic: assignment to entry in nil map
items[1] = "A"
// 使用delete删除项
// func delete(m map[Type]Type1, key Type)
delete(items, 1)
delete(items, "A")
if
// 简单判断
x := 10
if x-10 == 0 {
}
// 复合判断
if x := 10; x-10 == 0 {
}
// else
if num := somenumber(); num < 0 {
fmt.Println(num)
} else if num < 10 {
fmt.Println(num)
} else {
fmt.Println(num)
}
// 结构中创建的变量在外部无法使用
// undefined: num
fmt.Println(num)
switch
switch i {
case 0:
fmt.Print("zero...")
// 多个case值
case 1, 2:
fmt.Print("one two...")
default:
fmt.Print("no match...")
}
// 可以条件中创建变量
switch x := 11; {
case x > 20:
fmt.Println("x>20")
case x > 10:
fmt.Println("x>10")
// 正常匹配到退出
// 使用fallthrough关键词转入下一个case
fallthrough
case x > 5:
fmt.Println("x>5")
default:
fmt.Println(x)
}
for
// 简单循环
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
// while循环
var num int64
for num != 5 {
num = rand.Int63n(15)
fmt.Println(num)
}
// 死循环
var num int32
for {
fmt.Print("Writting inside the loop...")
if num = rand.Int31n(10); num == 5 {
fmt.Println("finish!")
// break 关键词跳出循环
break
} else if num > 9 {
fmt.Println("num > 9")
// continue 跳出当前迭代
continue
}
fmt.Println(num)
}
// 遍历
for key := range items {
fmt.Println(key)
}
for key, value := range items {
fmt.Println(key, value)
}
参考
- https://go.dev/doc/
- https://docs.microsoft.com/zh-cn/learn/paths/go-first-steps/
- 《Go语言设计与实现》 - 左书祺(@Draven)