go基础
GO语言的优势
- 在语言层面支持并发,这个是Go最大的特色
- 语法简单、编译速度快、生态丰富、有垃圾回收
- 总结来说就是有接近C的速度,媲美java的生态,还有像python一样简洁的语法
代表产品:docker、kubernetes、etcd
Go环境安装
go安装包下载地址为:https://go.dev/dl/。如果是windows,则在里面找到 .msi 后缀的文件进行安装,一路默认,安装到最后可以选择自动配置环境变量。
最后可以在cmd中用go env
指令查看是否成功安装好环境。
Go代码执行
如果用cmd命令行,则通过go run XXX.go
来执行
如果在集成的IDE中运行,直接点运行即可,本质上也是执行了go run命令
如将如下的helloword程序保存为hello.go,在当前目录执行go run hello.go
既可
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
从上述helloworld程序可以看出go语言中没有类的概念,而只有包,main包中的main函数就是程序的入口。
go语言的潜规则
只要是声明了的变量必须要在某处使用,否则编译不通过
每行尾部不需要加分号;来标识结束
没有类、对象、继承等概念,只有package
基本语法
变量
普通变量
默认按照【var 变量名 类型名】的格式创建变量,但在知道类型的情况下可以不写类型,比如方法二、方法三。
其中第三种方法最简洁也最常用,只要在等号左边加上冒号就有了声明的作用
//法一:标准写法
var a int
//法二:自动推断
var hp = 100
//法三:极简写法,声明并赋值,自动根据赋值判断类型
name:="嘻哈"
支持多变量声明,和 python 很像,不需要显示声明类型,自动推断
var vname1, vname2, vname3 = v1, v2, v3
其它变量/常量
其它变量也遵循上述定义变量的规则,如果要定义常量,则把var换成const即可
结构体
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
type 结构体名称 struct {
字段1 类型
字段2 类型
…
}
常用数据结构
分为两种创建方式,第一种是用make方法创建(类似java中的new,只不过make是方法,new是关键字),第二种是直接通过字面量赋值的方式创建,总体来说类似java中String的两种创建方式。
数组
数组定义时有点奇葩,[]中括号既不像C语言一样跟在变量名后面,也不像java一样放在类型名后面,而是放在类型名称的前面,也就是【var 变量名 [大小]类型 】的结构,如下:
var n [size]int //直接定义数组
var arr = [3]int{1, 2, 3}//数组定义并初始化
arr := make([]int, 5)//使用make定义数组
数组初始化
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} //数组初始化
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} //初始化简写
balance := [5]float32{1:2.0,3:7.0}// 部分初始化,将索引为 1 和 3 的元素初始化
map
/* 使用 make */
m := make(map[string]int)
/* 使用字面量创建 */
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
语句
分支语句
分支结构,特点也是if后面的判断条件不用加括号,然后判断条件后面必须加上大括号{},即使只有一条语句,也得加{}
if 判断条件1 {
执行语句1
}else if 判断条件2 {
执行语句2
...
...
}else {
执行语句N
}
switch case写法
var a = "hello"
switch a {
case "hello":
fmt.Println(1)
case "world":
fmt.Println(2)
default:
fmt.Println(0)
}
循环语句
普通遍历
和java类似,只是for后面不用加括号而已
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
对数据结构的遍历
若是想对一个数组、字符串、map等数据结构进行遍历,则可以配合使用range 关键字进行遍历,格式如下:
//用range关键字遍历数组
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
//用range关键字遍历map
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
解释:对于数组而言,range返回的是【元素下标】和【元素值】两个返回值;对于map而言,range将返回的是【key】和【value】两个返回值。
注意:如果第只需要二个返回值,则要在第二个返回值前写上“_”符号表示接收但不处理。
函数
Go 语言标准库提供了很多内置的函数,不需要引入其它包即可使用。例如,通过len() 函数可以获取各种类型的数据结构的长度。
函数定义
go中函数定义也与其它语言不同,支持多个返回值,定义格式如下:
func 函数名 (参数) (返回type){
函数体
}
如果返回值有多个,则要用()包围起来,并且在返回值有多个的情况下,需要全部按顺序接收,如果不想使用的某些返回值,那这些返回值就用下划线“_”(匿名变量)去接收,代表弃用。
函数异常处理
Go 语言提供了非常简单的错误处理机制。
一般而言,函数通常前几个返回值是正常的需要的返回值,而最后一个返回值通常是用于返回错误信息。(我们自定义函数时也最好参照这个规则)
result, err:= solution()
if err != nil {
fmt.Println(err)//错误处理
}
Go并发编程
Go 语言很容易实现并发,只需在在调用某函数时,在函数前加上 go 关键字就可以开始一个 goroutine 。
goroutine
goroutine 叫做协程,其实本质上就是轻量级的线程,只不过开销比线程小得多,所以go语言支持较大的并发量。
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
如上运行结果是hello和world交替出现,可以得出主线程和子线程也是交替执行的。
channel
channel(管道)是用于实现goroutine 之间通信的数据结构。一般作为各个goroutine 方法的参数传入(传递的是同一个管道的引用),然后在goroutine 之中对这个管道进行操作,其中的操作包括发送和读取,具体如下:
ch := make(chan int) //定义管道ch
ch <- value // 把 value 发送到通道 ch
value := <-ch // 从 ch 接收数据并把值赋给 value
下面是一个通过3线程去计算6个数之和的例子,各个线程把计算结果放入channel中,最后在主线程中从channel接收这些结果。
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 把当前 sum 发送到通道 c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[0:2], c)
go sum(s[2:4], c)
go sum(s[4:6], c)
x := <-c // 从通道 c 中接收第一个结束的结果
y := <-c // 从通道 c 中接收第二个结束的结果
z := <-c // 从通道 c 中接收第三个结束的结果
fmt.Println(x, y, z, x+y+z)
}