Go 语法笔记

  • Go 是静态、强类型语言;
1
2
3
4
5
6
7
8
9
10
11
12
package main // 定义包名

import "fmt" // 引入包

// 没有 init() 时的程序入口,可执行程序必须包含
func main() { // 注意大括号不能换行
/*
当标识符以一个大写字母开头,就可以被外部包的代码所使用(public),
否则对包外不可见(protected)
*/
fmt.Println("Hello, World!") // 不需要分号,除非一行多条语句
}
  • 运行
    • 使用 go run main.go 直接执行,或使用 go build main.go 生成可执行文件 ;
  • 数据类型
    • boolintuintfloat32float64complex64complex128string (UTF-8)等;
    • 指针、数组、 structChannel 、函数、切片、接口、 Map
    • 类似 Java,基础类型为值类型,高级类型为引用;
    • 交换值: a, b = b, a
  • 变量声明
    • var identifier1, identifier2 [type] [= value1, value2] (不赋初值自动赋零值),可以根据赋初值自动判定类型;
    • 可以用 a := 50 直接声明(只能在函数体内);
  • 表达式
    • 基本类似于 C 语言;自增自减运算只能单独作为表达式,不能用于赋值语句;
  • 常量定义
    • const identifier [type] = value
  • 条件、循环语句
    • 基本相当于 C 语言删去条件括号;
    • 范围(range): for-each 型循环: for key, value := range oldMap
  • 函数声明
    • func myFunction(a, b int, s *string) (int, string) {} ,无返回值则省略;
    • 传参均为传值(包括数组等,切片为传引用),想传引用只能 s *string ,调用 *s
  • 匿名函数作闭包
    • func add(x int) func(x int)(string, int) { return func(x int)(string, int) { ... } }
  • 方法
    • 包含了接受者的函数;
    • func (c Circle) getArea() float64 { ... } ,调用 c.getArea()
    • 没有 this 指针,直接使用参数名 c
  • 数组
    • var value = [3][2]int{{1, 2}, {3, 4}, {5, 6}}
    • 可以用 [...] 自动获取初始化列表个数;
  • 指针
    • 类似 C,输出为一个整数,空值为 nil
    • 类型声明从左往右读,右侧是左侧的定语(与 C 相反);
  • struct 类型
    • 不包含方法,可以转换为 JSON;
    • 指向 struct 的指针获取成员变量可以不带星号: ptr.a ,但这个自动解引用只做了一级,多级需要手动解引用到一级;
  • 切片(slice)
    • var identifier []type ,可以使用 make([]T, length, [capacity]) ,length 为实际长度(即 r-l),capacity 为最大容量(即数组长度-l) ,对应有 len()cap() 函数;
    • 使用 s := arr[:] 创建数组引用,左闭右开类似 Python Numpy;
    • 未初始化时为 nil
    • 直接修改切片会影响它相关联的其余切片或原数组,但 append 会让切片和与它相关的切片和数组脱钩,而地址不变;
    • 切片的 capacity 增长大致为每次两倍;
1
2
3
slices = append(slices, 1, 2, 3); 
slices1 = make([]int, len(slices), cap(slices) * 2)
copy(slices1, slices) // slices 拷贝进 slices1
  • 集合(Map):无序键值对集合,
1
2
3
4
5
6
v1 := m["apple"]
v2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
m["apple"] = 5 // 插入/修改
for key, value := range m {
delete(m, key) // 删除
}
  • 接口
    • 一个类型实现了一个接口包含的所有函数,它就实现了这个接口,从而可以实现多态;
    • 本质是一个指针,可以指向任何实现了它的类对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type interface_name interface {
   method_name1 [return_type]
   ...
}

func (struct_name_variable struct_name) method_name1() [return_type] { ... }

type Phone interface { call() }

type NokiaPhone struct { }
func (nokiaPhone NokiaPhone) call() { ... }

type IPhone struct {}
func (iPhone *IPhone) call() { ... } // 修改 iphone 内属性

var phone Phone

phone = new(NokiaPhone)
phone.call()

phone = new(IPhone)
phone.call()
  • 异常
    • 是一个接口, type error interface { Error() string } ,实现了 Error() 的类即可抛出异常;
    • 可通过 return errors.New("...") 生成错误信息;
    • panic-recover 机制:类似 try-catch,发生异常时立即向上逐层返回并执行 deferdefer 中也可以抛出 panic),直到被 recover 捕获或退出到最外层;
    • defer 可以多次声明(需要在 panic 之前声明),在发生异常或正常结束时执行,按先进后出(后声明的先执行);
1
2
3
4
5
6
7
8
9
10
11
// 以下代码执行输出 three
func main(){
defer func(){
if err := recover() ; err != nil {
fmt.Println(err)
}
}()
defer func(){ panic("three") }()
defer func(){ panic("two") }()
panic("one")
}
  • 并发(Goroutine)
    • go func(params) 开启轻量级线程;
    • 通道(Channel)ch <- vv 送给通道 chv[, ok] := <- ch 从通道接收数据给 v
    • 通道声明: ch := make(chan int[, 100]) ,默认无缓冲区;发送方在缓冲区满时会阻塞,接收方在缓冲区为空时会阻塞;
    • close(c) 关闭通道(缓冲区仍然存在)后接收方的 ok 会得到 false ,否则会一直阻塞,据此可使用 for i := range c