本文的内容基于 Go 语言简明教程 , 目的是快速学习 Go 的基本用法。
Variables and Built-in Data Types Variable Go 作为静态类型的语言,变量声明的时候必须要明确变量的类型。 Go 语言的类型写在变量后面。
1 2 3 4 5 var a int var a int = 1 var a = 1 a := 1 msg := "Hello World!"
简单类型 1 2 3 4 5 6 nil var a int8 = 10 var c1 byte = 'a' var b float32 = 12.2 var msg = "Hellow World!" ok := false
字符串 golang 字符串的底层是一个 byte 数组,unint8 的数组
utf8 进行存储,一个英文字母占 1 个 byte,一个中文字符占 3 个 byte。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport ( "fmt" "reflect" ) func main () { str1 := "Golang" str2 := "Go语言" fmt.Println(reflect.TypeOf(str2[2 ]).Kind()) fmt.Println(str1[2 ], string (str1[2 ])) fmt.Printf("%d %c\n" , str2[2 ], str2[2 ]) fmt.Println("len(str2):" , len (str2)) }
从以上代码中可以看到,由于 go 中一个中文字符占 3 个 byte,导致使用 string 数组不能正确切片,所以正确的处理方式是将 string 转为 rune 数组。rune 是 int32 的别名。使用[]int32 进行类型转换也是一样的效果。
1 2 3 4 runeArr := []rune (str2) fmt.Println(reflect.TypeOf(runeArr[2 ]).Kind()) fmt.Println(runeArr[2 ], string (runeArr[2 ])) fmt.Println("len(runeArr):" , len (runeArr))
Array and Slice Array 1 2 3 4 5 6 7 8 9 array and slice var arr [5 ]int var arr2 [5 ][5 ]int arr := [5 ]int {1 , 2 , 3 , 4 , 5 } for i := 0 ; i < len (arr); i++ {arr[i] += 100 } fmt.Println(arr)
数组的长度不能改变,如若想要拼接或者获取子数组,需要使用切片。切片是使用数组作为底层结构的抽象。切片包含三个组件:容量,长度和指向底层数组的指针,切片可以随时进行扩展。
Slice 声明切片
1 2 3 slice1 := make ([]float32 , 0 ) slice2 := make ([]float32 , 3 , 5 ) fmt.Println(len (slice1), cap (slice2))
使用切片
1 2 3 4 5 6 7 8 9 10 11 12 slice2 = append (slice2, 1 , 2 , 3 , 4 ) fmt.Println(len (slice2), cap (slice2)) sub1 := slice2[3 :] sub2 := slice2[:3 ] combined := append (sub1, sub2...) fmt.Println(combined)
申明切片的时候会需要设置容量大小,为切片预分配空间,实际使用过程中,如果容量不够,切片容量会自动扩展。
Map 1 2 3 4 5 6 7 8 9 10 11 m1 := make (map [string ]int ) m2 := map [string ]string { "Sam" : "Male" ,"Alice" : "Female" ,} m1["Tom" ] = 18 fmt.Println(m1) fmt.Println(m2)
Pointer 使用 * 定义指针,使用 & 获取某个变量的地址
1 2 3 4 5 6 var n int = 1 add(n) fmt.Println(n) realAdd(&n) fmt.Println(n)
Control Flow (if, for, switch) if else 1 2 3 4 5 6 7 8 9 10 11 12 13 age := 17 if age < 18 {fmt.Printf("Kid\n" ) } else { fmt.Printf("Adult" ) } if age := 18 ; age < 18 {fmt.Printf("Kid" ) } else { fmt.Printf("Adult\n" ) }
switch 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 type Gender int8 const ( MALE Gender = 1 FEMALE Gender = 2 ) gender := MALE switch gender { case FEMALE: fmt.Println("female" ) case MALE: fmt.Println("male" ) default : fmt.Println("unkonwn" ) } switch gender { case FEMALE: fmt.Println("female" ) fallthrough case MALE: fmt.Println("male" ) fallthrough default : fmt.Println("unkonwn" ) }
for loop for loop,c++ 写法和 python 写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 sum := 0 for i := 0 ; i < 10 ; i++ { if sum > 20 { break } sum += i fmt.Println(sum) } nums := []int {1 , 2 , 3 , 4 , 5 } for i, num := range nums { fmt.Println(i, num) } m2 := map [string ]string { "Jhon" : "MALE" , "Alice" : "FEMALE" , } for key, value := range m2 { fmt.Println(key, value) }
Functions Parameter and Return Values 对于一个 go 中的典型函数,参数可以有多个,返回值也可以有多个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func div2 (num1 int , num2 int ) ( quo int , rem int ) { quo = num1 / num2 rem = num1 % num2 return } func main () { quo, rem := div(100 , 17 ) fmt.Println(quo, rem) quo1, rem1 := div(100 , 17 ) fmt.Println(quo1, rem1) }
Error Handling 1 2 3 4 5 6 7 8 9 10 11 import ( "fmt" "os" ) func main () { _, err := os.Open("noexist.txt" ) if err != nil { fmt.Println(err) } }
使用 errorw.New 返回自定义的错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import ( "fmt" "os" "error" ) func hello (name string ) error { if len (name) == 0 { return errors.New("error: name is null" ) } fmt.Println("Hello" , name) return nil } func main () { if err := hello("" ); err != nil { fmt.Println(err) } }
出现一些不可预知的错误,比如数组越界,这种错误可能会导致程序非正常退出,在 Go 语言中称之为 panic
1 2 3 4 5 6 7 8 9 func get (index int ) int { arr := [3 ]int {2 , 3 , 4 } return arr[index] } func main () { fmt.Println(get(5 )) fmt.Println("finished" ) }
报错
1 2 3 4 5 6 7 8 anic: runtime error: index out of range [5] with length 3 goroutine 1 [running]: main.get(...) /home/yiwei/go_test/main.go:38 main.main() /home/yiwei/go_test/main.go:203 +0x1d exit status 2
在 Python 和 Java 等语言中,有 try … catch 机制, Go 中类似的机制是 defer,recover
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func get (index int ) (ret int ) { defer func () { if r := recover (); r != nil { fmt.Println("Some Error !" , r) ret = -1 } }() arr := [3 ]int {2 , 3 , 4 } return arr[index] } func main () { fmt.Println(get(5 )) fmt.Println("finished" ) }
Struct, Method and Interface Struct and Method 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 type Student struct { name string age int } func (student *Student) hello (person string ) string { return fmt.Sprintf("hello %s, I am %s" , person, student.name) } func main () { stu := &Student{ name: "Tom" , } msg := stu.hello("Jack" ) fmt.Println(msg) stu1 := new (Student) fmt.Println(stu1.hello("Alice" )) }
Interfaces 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package mainimport ( "fmt" ) type Person interface { getName() string getAge() int } type Student struct { name string age int } func (stu *Student) getName () string { return stu.name } func (stu *Student) getAge () int { return stu.age } type Worker struct { name string gender string } func (w *Worker) getName () string { return w.name } func main () { var p Person = &Student{ name: "Tom" , age: 18 , } fmt.Println(p.getName()) stu := p.(*Student) fmt.Println(stu.getAge()) }
测试某个类型实现了某个接口的全部方法
1 2 var _ Person = (*Student)(nil )var _ Person = (*Worker)(nil )
如果一个没有任何方法的空接口,可以表示任意类型
1 2 3 4 5 6 7 8 func main () { m := make (map [string ]interface {}) m["name" ] = "Tom" m["age" ] = 18 m["scores" ] = [3 ]int {98 , 99 , 85 } fmt.Println(m) }
Goroutines Goroutines are functions or methods that run concurrently with other functions and method. Goroutines can be thought as light weight threads.
Sync Go 提供了 sync 和 channel 两种方式支持 Goroutine 的并发
如果我们希望并发下载 N 个资源,且多个并发写成之间不需要通信,就可以使用 sync.WaitGroup, 等待所有并发协程结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup func download(url string) { fmt.Println("start to download" , url) time.Sleep(time.Second) //模拟耗时操作 wg.Done() //减少一个计数器 } func main() { for i := 0 ; i < 3 ; i++ { wg.Add(1 ) // 增加一个计数器 go download("a.com/" + string(i+'0' )) //启动新的写成并发执行 download 函数 } wg.Wait() // 等待所有的协程执行结束 fmt.Println("Done!" ) }
Channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport ( "fmt" "time" ) var ch = make (chan string , 10 ) func download (url string ) { fmt.Println("start to download" , url) time.Sleep(time.Second) ch <- url } func main () { for i := 0 ; i < 3 ; i++ { go download("a.com/" + string (i+'0' )) } for i := 0 ; i < 3 ; i++ { msg := <-ch fmt.Println("finish" , msg) } fmt.Println("Done!" ) }
1 2 3 4 5 6 7 start to download a.com/2 start to download a.com/1 start to download a.com/0 finish a.com/1 finish a.com/2 finish a.com/0 Done!
Unit Test calc.go
1 2 3 4 5 6 7 8 9 10 package mainimport "testing" func TestAdd (t *testing.T) { if ans := add(1 , 2 ); ans != 3 { t.Error("add(1,2) should be equal to 3" ) } }
calc_test.go
1 2 3 4 5 package mainfunc add (num1 int , num2 int ) int { return num1 + num2 }
go test calc_test.go calc.go -v
运行命令行,将两个文件连起来并进行单元测试
1 2 3 4 === RUN TestAdd --- PASS: TestAdd (0.00s) PASS ok command-line-arguments 0.002s
Package and Module Package 一般来说,一个文件夹可以作为一个 package ,同一个 package 对的内部变量、类型、方法等定义可以相互看到。
Module Go Mdules 是 Go 1.11 版本之后引入的。Go Modules 是较为完善的包管理工具。Go Modules 在 1.13 版本仍然是可选使用的, GO111MODULE 的默认值为 AUTO,如果需要强制使用 Go Modules 进行以来管理,可以将其设置成 ON。
文件及其相关内容如下所示:
1 2 3 4 demo/ |--calc/ |--calc.go |--main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "example/calc" "fmt" "rsc.io/quote" ) func main () { fmt.Println(calc.Add(1 , 2 )) fmt.Println(quote.Hello()) }
1 2 3 4 5 package calcfunc Add (num1 int , num2 int ) int { return num1 + num2 }
1 2 3 4 5 6 7 8 9 10 $ go mod init example go: creating new go.mod: module example go: to add module requirements and sums: go mod tidy $ go mod tidy go: finding module for package rsc.io/quote go: found rsc.io/quote in rsc.io/quote v1.5.2 $ go run . 3 Ahoy, world!
Author:
Yiwei
Permalink:
https://hangzhi.github.io/2021/04/01/go-tut/
License:
Copyright (c) 2019 CC-BY-NC-4.0 LICENSE