基础语法

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println("Hello Go")
  5. }

运行:$ go run hello.go

运算符

算术运算符

Go速查表 - 图1

&^ 即是 AND NOT(x, y) = AND(x, NOT(Y)),如:

  1. package main
  2. import "fmt"
  3. func main() {
  4. x := 0xDC // 11011100
  5. y := 0xF0 // 11110000
  6. z := x &^ y // 00001100 // y 中为 1 的位全部被清除为 0
  7. fmt.Printf("%08b", z)
  8. }

比较运算符

Go速查表 - 图2

逻辑运算符

Go速查表 - 图3

其他

Go速查表 - 图4

声明

与C不同,类型放在标识符后面:

  1. var foo int // 无初值的声明
  2. var foo int = 42 // 带初值的声明
  3. var foo, bar int = 42, 1302 // 一次性声明并初始化多个变量
  4. var foo = 42 // 类型推断,由使用的上下文决定
  5. foo := 42 // 简短声明,只能用在函数内部
  6. const constant = "This is a constant"

函数

  1. // 最简单的函数
  2. func functionName() {}
  3. // 带参数的函数(注意类型也是放在标识符之后的)
  4. func functionName(param1 string, param2 int) {}
  5. // 类型相同的多个参数
  6. func functionName(param1, param2 int) {}
  7. // 声明返回值的类型
  8. func functionName() int {
  9. return 42
  10. }
  11. // 一次返回多个值
  12. func returnMulti() (int, string) {
  13. return 42, "foobar"
  14. }
  15. var x, str = returnMulti()
  16. // 只使用 return 返回多个命名返回值
  17. func returnMulti2() (n int, s string) {
  18. n = 42
  19. s = "foobar"
  20. // n 和 s 会被返回
  21. return
  22. }
  23. var x, str = returnMulti2()

函数作为值和回调使用

  1. func main() {
  2. // 将函数作为值,赋给变量
  3. add := func(a, b int) int {
  4. return a + b
  5. }
  6. // 使用变量直接调用函数
  7. fmt.Println(add(3, 4))
  8. }
  9. // 回调函数作用域:在定义回调函数时能访问外部函数的值
  10. func scope() func() int{
  11. outer_var := 2
  12. foo := func() int { return outer_var}
  13. return foo
  14. }
  15. func another_scope() func() int{
  16. // 编译错误,两个变量不在此函数作用域内
  17. // undefined: outer_var
  18. outer_var = 444
  19. return foo
  20. }
  21. // 回调函数不会修改外部作用域的数据
  22. func outer() (func() int, int) {
  23. outer_var := 2
  24. inner := func() int {
  25. outer_var += 99 // 试着使用外部作用域的 outer_var 变量
  26. return outer_var // 返回值是 101,但只在 inner() 内部有效
  27. }
  28. return inner, outer_var // 返回值是 inner, 2 (outer_var 仍是 2)
  29. }
  30. inner, outer_var := outer(); // inner, 2
  31. inner(); // 返回 101
  32. inner(); // 返回 200 // 回调函数的特性

可变参数函数

  1. func main() {
  2. fmt.Println(adder(1, 2, 3)) // 6
  3. fmt.Println(adder(9, 9)) // 18
  4. nums := []int{10, 20, 30}
  5. fmt.Println(adder(nums...)) // 60
  6. }
  7. // 在函数的最后一个参数类型前,使用 ... 可表明函数还能接收 0 到多个此种类型的参数
  8. // 下边的函数在调用时传多少个参数都可以
  9. func adder(args ...int) int {
  10. total := 0
  11. for _, v := range args { // 使用迭代器逐个访问参数
  12. total += v
  13. }
  14. return total
  15. }

内置类型

  1. bool
  2. string
  3. int int8 int16 int32 int64
  4. uint uint8 uint16 uint32 uint64 uintptr
  5. byte // uint8 类型的别名 // 存储 raw data
  6. rune // int32 类型的别名 // 一个 Unicode code point 字符
  7. float32 float64
  8. complex64 complex128

类型转换

  1. ar i int = 42
  2. var f float64 = float64(i)
  3. var u uint = uint(f)
  4. // 简化语法
  5. i := 42
  6. f := float64(i)
  7. u := uint(f)

Package

1.package 在源文件开头声明
2.main package 才是可执行文件
3.约定:package 名字与 import 路径的最后一个单词一致(如导入 math/rand 则 package 叫 rand)
4.大写开头的标识符(变量名、函数名…):对其他 package 是可访问的
5.小写开头的标识符:对其他 package 是不可见的

流程控制结构

if

  1. func main() {
  2. // 一般的条件判断
  3. if x > 0 {
  4. return x
  5. } else {
  6. return -x
  7. }
  8. // 在条件判断语句前可塞一条语句,使代码更简洁
  9. if a := b + c; a < 42 {
  10. return a
  11. } else {
  12. return a - 42
  13. }
  14. // 使用 if 做类型断言
  15. var val interface{}
  16. val = "foo"
  17. if str, ok := val.(string); ok {
  18. fmt.Println(str)
  19. }
  20. }

Loops

  1. // Go 语言中循环结构只有 for,没有 do、while、until、foreach 等等
  2. for i := 1; i < 10; i++ {
  3. }
  4. for ; i < 10; { // 等效于 while 循环
  5. }
  6. for i < 10 { // 只有一个判断条件时可省去分号
  7. }
  8. for { // 无条件循环时,等效于 while(true)
  9. }

switch

  1. // switch 分支语句
  2. switch operatingSystem {
  3. case "darwin":
  4. fmt.Println("Mac OS Hipster")
  5. // case 语句自带 break,想执行所有 case 需要手动 fallthrough
  6. case "linux":
  7. fmt.Println("Linux Geek")
  8. default:
  9. // Windows, BSD, ...
  10. fmt.Println("Other")
  11. }
  12. // 和 if、for 语句一样,可在判断变量之前加入一条赋值语句
  13. switch os := runtime.GOOS; os {
  14. case "darwin": ...
  15. }
  16. // 在 switch 中还能做比较,相当于 switch (true) {...}
  17. number := 42
  18. switch {
  19. case number < 42:
  20. fmt.Println("Smaller")
  21. case number == 42:
  22. fmt.Println("Equal")
  23. case number > 42:
  24. fmt.Println("Greater")
  25. }
  26. // 多个 case 可使用逗号分隔统一处理
  27. var char byte = '?'
  28. switch char {
  29. case ' ', '?', '&', '=', '#', '+', '%':
  30. fmt.Println("Should escape")
  31. }

Arrays

  1. var a [10]int // 声明长度为 10 的 int 型数组,注意数组类型 = (元素类型 int,元素个数 10)
  2. a[3] = 42 // 设置元素值
  3. i := a[3] // 读取元素值
  4. // 声明并初始化数组
  5. var a = [2]int{1, 2}
  6. a := [2]int{1, 2} // 简短声明
  7. a := [...]int{1, 2} // 数组长度使用 ... 代替,编译器会自动计算元素个数

slices

  1. var a []int // 声明 slice,相当于声明未指定长度的数组
  2. var a = []int {1, 2, 3, 4} // 声明并初始化 slice (基于 {} 中给出的底层数组)
  3. a := []int{1, 2, 3, 4} // 简短声明
  4. chars := []string{0:"a", 2:"c", 1: "b"} // ["a", "b", "c"]
  5. var b = a[lo:hi] // 创建从 lo 到 hi-1 的 slice
  6. var b = a[1:4] // 创建从 1 到 3 的 slice
  7. var b = a[:3] // 缺省 start index 则默认为 0
  8. var b = a[3:] // 缺省 end index 则默认为 len(a)
  9. a = append(a,17,3) // 向 slice a 中追加 17 和 3
  10. c := append(a,b...) // 合并两个 slice
  11. // 使用 make 创建 slice
  12. a = make([]byte, 5, 5) // 第一个参数是长度,第二个参数是容量
  13. a = make([]byte, 5) // 容量参数是可选的
  14. // 从数组创建 slice
  15. x := [3]string{"Лайка", "Белка", "Стрелка"}
  16. s := x[:] // slice s 指向底层数组 x

数组和slice的操作函数

  1. // 迭代数组或 slice
  2. for i, e := range a {
  3. // i 是索引
  4. // e 是元素值
  5. }
  6. // 如果你只要值,可用 _ 来丢弃返回的索引
  7. for _, e := range a {
  8. }
  9. // 如果你只要索引
  10. for i := range a {
  11. }
  12. // 在 Go 1.4 以前的版本,如果 i 和 e 你都不用,直接 range 编译器会报错
  13. for range time.Tick(time.Second) {
  14. // 每隔 1s 执行一次
  15. }

map

  1. var m map[string]int
  2. m = make(map[string]int)
  3. m["key"] = 42
  4. fmt.Println(m["key"])
  5. delete(m, "key")
  6. elem, ok := m["key"] // 检查 m 中是否键为 key 的元素,如果有 ok 才为 true
  7. // 使用键值对的形式来初始化 map
  8. var m = map[string]Vertex{
  9. "Bell Labs": {40.68433, -74.39967},
  10. "Google": {37.42202, -122.08408},
  11. }

结构体

Go 语言中没有 class 类的概念,取而代之的是 struct,struct 的方法对应到类的成员函数。

  1. // struct 是一种类型,也是字段成员的集合体
  2. // 声明 struct
  3. type Vertex struct {
  4. X, Y int
  5. }
  6. // 初始化 struct
  7. var v = Vertex{1, 2} // 字段名有序对应值
  8. var v = Vertex{X: 1, Y: 2} // 字段名对应值
  9. var v = []Vertex{{1,2},{5,2},{5,5}} // 初始化多个 struct 组成的 slice
  10. // 访问成员
  11. v.X = 4
  12. // 在 func 关键字和函数名之间,声明接收者是 struct
  13. // 在方法内部,struct 实例被复制,传值引用
  14. func (v Vertex) Abs() float64 {
  15. return math.Sqrt(v.X*v.X + v.Y*v.Y)
  16. }
  17. // 调用方法(有接收者的函数)
  18. v.Abs()
  19. // 有的方法接收者是指向 struct 的指针
  20. // 此时在方法内调用实例,将是传址引用
  21. func (v *Vertex) add(n float64) {
  22. v.X += n
  23. v.Y += n
  24. }

匿名结构体

使用 map[string]interface{} 开销更小且更为安全。

  1. point := struct {
  2. X, Y int
  3. }{1, 2}

指针

  1. p := Vertex{1, 2} // p 是一个 Vertex
  2. q := &p // q 是指向 Vertex 的指针
  3. r := &Vertex{1, 2} // r 也是指向 Vertex 的指针
  4. var s *Vertex = new(Vertex) // new 返回的指向该实例指针

接口

  1. // 声明接口
  2. type Awesomizer interface {
  3. Awesomize() string
  4. }
  5. // 无需手动声明 implement 接口
  6. type Foo struct {}
  7. // 自定义类型如果实现了接口的所有方法,那它就自动实现了该接口
  8. func (foo Foo) Awesomize() string {
  9. return "Awesome!"
  10. }

结构体和接口的组合嵌入

  1. // 实现 ReadWriter 的类型要同时实现了 Reader 和 Writer 两个接口
  2. type ReadWriter interface {
  3. Reader
  4. Writer
  5. }
  6. // Server 暴露出 Logger 所有开放的方法
  7. type Server struct {
  8. Host string
  9. Port int
  10. *log.Logger
  11. }
  12. // 初始化自定义的组合类型
  13. server := &Server{"localhost", 80, log.New(...)}
  14. // 组合的结构体能直接跨节点调用方法
  15. server.Log(...) // 等同于调用 server.Logger.Log(...)
  16. // 字段同理
  17. var logger *log.Logger = server.Logger

Errors

Go 中没有异常处理机制,函数在调用时在有可能会产生错误,可返回一个 Error 类型的值,Error 接口:

  1. type error interface {
  2. Error() string
  3. }

一个可能产生错误的函数:

  1. func doStuff() (int, error) {
  2. }
  3. func main() {
  4. result, err := doStuff()
  5. if err != nil {
  6. // 错误处理
  7. }
  8. // 使用 result 处理正常逻辑
  9. }

并发

goroutine

goroutine(协程)是轻量级的线程(Go runtime 自行管理,而不是操作系统),代码 go f(a, b) 就开了一个运行 f 函数的协程。

  1. func doStuff(s string) {
  2. }
  3. func main() {
  4. // 在协程中执行函数
  5. go doStuff("foobar")
  6. // 在协程中执行匿名函数
  7. go func (x int) {
  8. // 函数实现
  9. }(42)
  10. }

Channels

  1. ch := make(chan int) // 创建类型为 int 的 channel
  2. ch <- 42 // 向 channel ch 写数据 42
  3. v := <-ch // 从 channel ch 读数据,此时 v 的值为 42
  4. // 无缓冲的 channel 此时会阻塞
  5. // 如果 channel 中无数据,则读操作会被阻塞,直到有数据可读
  6. // 创建带缓冲的 channel
  7. // 向带缓冲的 channel 写数据不会被阻塞,除非该缓冲区已满
  8. ch := make(chan int, 100)
  9. close(ch) // 发送者主动关闭 channel
  10. // 在从 channel 读数据的同时检测其是否已关闭
  11. // 如果 ok 为 false,则 ch 已被关闭
  12. v, ok := <-ch
  13. // 从 channel 中读数据直到它被关闭
  14. for i := range ch {
  15. fmt.Println(i)
  16. }
  17. // select 语句中 任一 channel 不阻塞则自动执行对应的 case
  18. func doStuff(channelOut, channelIn chan int) {
  19. select {
  20. case channelOut <- 42:
  21. fmt.Println("We could write to channelOut!")
  22. case x := <- channelIn:
  23. fmt.Println("We could read from channelIn")
  24. case <-time.After(time.Second * 1):
  25. fmt.Println("timeout")
  26. }
  27. }

channel开发原则
1.向 nil channel 写数据将卡死,一直阻塞 ;
2.从 nil channel 读数据将卡死,一直阻塞;
3.向已关闭的 channel 写数据将造成 panic

  1. package main
  2. func main() {
  3. var c = make(chan string, 1)
  4. c <- "Hello, World!"
  5. close(c)
  6. c <- "Hello, Panic!"
  7. }

4.从已关闭的 channel 读数据将返回零值

  1. package main
  2. func main() {
  3. var c = make(chan int, 2)
  4. c <- 1
  5. c <- 2
  6. close(c)
  7. for i := 0; i < 3; i++ {
  8. println(<-c)
  9. }
  10. }

输出

  1. fmt.Println("Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ") // 最基本的输出,会自动加一个换行
  2. p := struct { X, Y int }{ 17, 2 }
  3. fmt.Println( "My point:", p, "x coord=", p.X ) // 输出结构体字段等
  4. s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // 组合字符串并返回
  5. fmt.Printf("%d hex:%x bin:%b fp:%f sci:%e",17,17,17,17.0,17.0) // 类 C 的格式化输出
  6. s2 := fmt.Sprintf( "%d %f", 17, 17.0 ) // 格式化字符串并返回
  7. hellomsg := `
  8. "Hello" in Chinese is 你好 ('Ni Hao')
  9. "Hello" in Hindi is नमस्ते ('Namaste')
  10. `
  11. // 声明多行字符串,在前后均使用反引号 `

代码片段

HTTP Server

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. // 定义响应的数据结构
  7. type Hello struct{}
  8. // Hello 实现 http.Handler 中定义的 ServeHTTP 方法
  9. func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  10. fmt.Fprint(w, "Hello!")
  11. }
  12. func main() {
  13. var h Hello
  14. http.ListenAndServe("localhost:4000", h)
  15. }
  16. // http.ServeHTTP 在接口内的定义如下:
  17. // type Handler interface {
  18. // ServeHTTP(w http.ResponseWriter, r *http.Request)
  19. // }

Go速查表 - 图5