一小时捋完GO语法
go语言也就是golang,是谷歌发明的一种编程语言,主要特点在于能够快速开发出适应高并发的代码,主要应用在一些云原生环境,比如k8s,gitlab-runner。
本文适用于有点编程语言基础的。可以快速入个门,然后就可以愉快得阅读k8s源代码了。
开发环境
可以参考 centos下VSCode进行GO开发
基础
粗略的扫一遍基本数据结构、运算符、条件、循环、函数、类。有些GO语言特有的先不管,后面慢慢理解。能达到基本的阅读代码水平即可。
- 关键字:
package
、import
、func
、return
、var
、const
- 数据类型:
bool
、int
、string
- 运算符:
+
、-
、*
、/
、%
、++
、--
- 关系运算符:
==
、!=
、>
、<
、>=
、<=
- 逻辑运算符:
&&
、||
、!
- 位运算
a = 0011 1100``````b = 0000 1101
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
异或,相异时为1a << 2 = 1111 0000
左移a >> 2 = 0000 1111
右移
- 赋值运算,后面几个是位运算的赋值
=
、+=
、-=
、*=
、/=
、%=
、&=
、|=
、^=
、<<=
、>>=
- 条件语句:
if else
、switch
、select
- 循环:
for + break + continue + goto
- 函数格式:
func function_name( [parameter_list] ) ( [return_types_list] ) {}
- 注意可以多返回值
- 值传递、引用传递
- 结构体(相当于类)
type struct_name struct {}
- 结构体并不会把函数包含在他的定义中,只需要定义函数时,指定他的结构体就可以。
func (struct_var struct_name) function_name( [parameter_list] ) ( [return_types_list] ) {}
- 接口
type interface_name interface {}
- 如果一个结构体实现了interface中全部的方法,我们认为他实现了这个interface。
interface_name val1 = new(struct_name);val1.method1();
- 变量作用域
- 全局变量局部变量重名时,优先取局部变量
- 数组定义
var array_name = [size] type_name { var1,var2,var3 }
var array_name = [...] type_name { var1,var2,var3 }
自动判断大小
- 切片
- 类似数组,但是长度可以自由变化
slice_name := array_name[x:y]
arr从下标x
到y-1
下的元素创建为一个新的切片make([]T, length, capacity)
创建一个T类型的切片,初始长度length,最大容量capacityappend
、copy
- 集合
var map_name map[key_type_name]value_type_name
make(map[key_type_name]value_type_name)
- 范围
range
用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。for k,v := range kvs {}
range返回key value,或者index value
- 指针
var var_name2 type_name = var2
var var_name1 *type_name = &var_name2
- 递归
- 错误处理,error是一个接口,可以自己定义错误。
type error interface{ Error() string }
func ErrorTest()(int,error){
return 0,errors.New("error test")
}
- 并发
go func_name(param_list)
即开启了一个 goroutine。- 同一个程序中的所有 goroutine 共享同一个地址空间。
特点
变量定义
go的变量定义相当的灵活,可以多个变量一起定义,也可以由编译器自己去分析变量的数据类型。
//注意变量定义有点另类,比如:
var bbsname string
bbsname = "csdn"
//也可以不加类型,系统自己推断
var username = "laowu"
//多变量定义
var key1, key2, key3 = "value1", "value2", "value3"
// := 的赋值,代表变量第一次出现
age, name, chinese := 20, "laowu", true
而go语言的函数也比较有特点,可以定义多个返回值,带来了极大的方便,这里不多说。
异常处理
涉及三个关键字:panic
、defer
、recover
。
panic
类似异常抛出,不遇到recover
就进程出错了。recover
如上所示,解决panic
问题- 函数返回前会按照先进后出的顺序调用栈内
defer
释放资源。
go语言不像java,有try...catch......
来处理异常。但是也有一套机制panic...defer...recover...
,可以捕捉系统中出现的错误。
但是两者在流程上有不同:
try...catch...
在try的过程中遇到问题,跳出try代码块,转到catch继续执行。panic...recover...
遇到panic后,回溯整个goroutine的调用栈,挨个执行defer,直到遇到defer中的recover或退出。
下例中,app运行过程中,进行了异常处理。
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
calc(i)
}
}
func calc(n int) {
defer func() {
recover() }()
if n == 5 {
panic("panic")
}
fmt.Println(" n = ", n)
}
select
尽管select和switch看上去挺像,其实他们的运行机制完全不同。
select
是针对通信的一个关键词。- 每个
case
代表了一个channel
的操作,select
能对接多个channel
,进行无阻塞的收发操作。 - 如果同时满足多个
case
,那么随即执行一个 - 如果都不满足,则执行
default
,若没定义default
,则一直阻塞,等待。
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
ch3 := make(chan int)
go func() {
time.Sleep(2 * time.Second); close(ch1) }() //改变时间可以改变事件的发生顺序
go func() {
time.Sleep(3 * time.Second); close(ch2) }()
go func() {
time.Sleep(4 * time.Second); close(ch3) }()
time.Sleep(4 * time.Second) //通过自己休息的时间,可以控制上面三个事件的发生状态。
for i := 0; i < 1000; i++ {
select {
case <-ch1:
fmt.Printf("close ch1")
case <-ch2:
fmt.Printf("close ch2")
case <-ch3:
fmt.Printf("close ch3")
}
}
}
完毕。