Go 语言学习笔记
Go 笔记 基础 常量、变量 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 50 51 52 53 54 55 56 57 package mainconst pi = 3.1415 const version = 0.1 const n10 = 42 const n8 = 0600 const n16 = 0x80 const ff = 07.1 const ne = 1e4 const ( ok = 1 msg = "OK" ) const ( running = false finished success ) const ( number0 = iota number1 _ = "Hello" number2 = iota ) const ( a, b, c = iota , iota , iota ) var ( gba int = 1 gbb float64 = 2.1 gbc string = "OK" ) func main () { const loa = 1 const b = "1" var c int = 2 println (c) var _ string = "_匿名变量不会被处理,也不占用内存" }
基本数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func main () { var ( a int = 1 b int32 = 2 c float64 = 3.14 d uint = 5 e string = "Hello\"" e2 rune = 'B' f bool = false g byte = 65 h complex64 = 2 + 3i ) fmt.Println(a, b, c, d, e, e2, f, g, h) }
注:
字符串是常量,可以用索引访问,但不可修改,基于字符串创建的切片同样不可修改
字符串转 []bytes 切片慎用
字符串结尾不包含空字符
复合数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func main () { a := 0 var b *int = &a var c [2 ]int = [2 ]int {1 , 2 } var d []int = []int {1 , 2 , 3 } var e map [int ]string var f chan int g := struct { id int name string }{1 ,"张三" } var h interface { func1(int ) string func2(string ) (int , string ) } fmt.Println(a, b, c, d, e, f, g, h) }
结构体访问字段使用 .
标识符,没有 ->
标识符
不支持指针运算
函数允许返回局部变量地址以实现“栈逃逸”
数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var a [2 ]int b := [...]int {1 , 2 , 3 , 4 , 5 } c := [3 ]int {2 , 3 , 4 } d := [10 ]int {1 : 4 , 5 : 8 } e := [...]int {1 : 5 , 5 : 6 } _ = a[0 ] for index, value := range e {println (index, ":" , value)} _ = len (e)
数据不可追加元素
数组赋值或作为参数均为值的拷贝
不同数组长度是不同数据类型
切片(变长数组) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 a := []int {1 , 2 , 3 , 4 , 5 , 6 , 7 } b := a[0 :4 ] c := make ([]int , 5 ) d := make ([]int , 10 , 20 ) _ = a[0 ] _ = len (a) _ = append (a, 3 ) _ = cap (a) e := make ([]int , 10 ) copy (e, b) s := "Hello,World!" s2 := []byte (s) s3 := []rune (s)
字典 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 a := map [int ]string {1 : "张三" , 4 : "李四" } b := make (map [string ]int , 10 ) b["A" ] = 1 _ = a[1 ] a[3 ] = "王五" delete (a, 3 ) _ = len (a) type User struct { name string age int } c := make (map [int ]User) c[1 ] = User{name: "张三" , age: 20 } c[1 ] = User{name: "张三" , age: 21 } println (c[1 ].age)a[3 ] = "1" for i, v := range a { println (i, ":" , v) }
结构体 结构体中可存放任意类型,结构体在存储空间上是连续的,按声明的顺序存放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type Student struct { id int name string done bool } a := Student{ id: 1 , name: "张三" , done: true , } a.name = "李四" println (a.name)
判断 if
语句进行判断,if 后的条件不需要小括号,{必须和 if 或 if else 放在一行
1 2 3 4 5 6 7 if h < 12 { println ("早上好" ) } else if h < 16 { println ("中午好" ) } else { println ("晚上好" ) }
Switch 略
循环 for 初始化;循环判断条件;每次循环执行{}
或
1 2 for key,value := range map {}for index,value := range arry{}
跳转 goto 只能在函数内部跳转,不能跳转到内部作用域
break 用于跳出 for swtch 和 select 语句,默认跳出最内层,可配合标签使用跳出多层
continue 用于跳出当前 for 循环并开始步进执行和下一次循环,可配合标签使用
return 用于函数和方法退出,也会触发跳转
Stdin/out/err 可用操作文件的方式对基本输入输出进行读写
1 2 3 4 5 6 func main () { os.Stdout.Write([]byte ("Hello,World!" )) bf := make ([]byte , 10 ) os.Stdin.Read(bf) println (bf) }
函数 特点:
支持多值返回
支持闭包
支持可变参数
不支持默认参数
不支持重载
不支持命名函数嵌套定义
函数定义 函数名首字母大写可供其他包访问,小写仅能在相同包内访问(共有,私有函数)
func 函数名(输入)(返回值){ 函数体 }
当返回值非命名且只有一个是可以省略返回值的括号
若命名返回值则 return 可不带返回参数
函数的调用是值的拷贝,意味若不使用指针则函数不会对输入进行修改
1 2 3 4 5 6 7 8 func main () { println (add(1 , 2 )) } func add (a, b int ) (sum int ) { sum = a + b return }
不定参数 所有不定参数的类型必须相同,不定参必须是最后一个参数,在函数体内相当于切片(若将切片传入函数,则需要在切片名后面加...
,如add(a...)
)
1 2 3 4 5 6 7 8 9 10 func main() { println(add(1, 2, 3, 4, 5)) } func add(vs ...int) (sum int) { for _, v := range vs { sum += v } return }
函数签名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 type Calc func (int , int ) int func add (a, b int ) int { return a + b } func sub (a, b int ) int { return a - b } func doCalc (f Calc, a, b int ) int { return f(a, b) } func main () { println (doCalc(add, 1 , 2 )) println (doCalc(sub, 19 , 2 )) }
有名函数和匿名函数 1 2 3 4 5 6 7 8 9 func add (a, b int ) int { return a + b } func main () { f := add println (f(1 ,2 )) }
匿名函数:返回函数的函数所返回的函数为匿名函数
1 2 3 4 5 6 7 8 9 10 11 12 func add () func (int , int ) int { f := func (a, b int ) int { return a + b } return f } func main () { f := add() println (f(1 , 2 )) }
延迟调用 1 2 3 4 5 6 7 8 func main () { defer println ("Hello" ) println ("World" ) }
defer 后必须是函数或者方法的调用,不能是语句,注册时使用的是值的拷贝,意味着若不使用指针,则执行时输入的值为调用是的值的拷贝,不会受到后续影响,也不会影响输入内容
位于 return 后的 defer 不会被执行
非必要应避免在循环中使用 defer
当主动调用 os.Exit
时,已注册的 defer 不会被执行
函数闭包 闭包最初的目的是减少全局变量,但隐式的共享变量不清晰,一般不建议使用闭包
异常处理 略
方法 方法和函数类似,但又特殊,可为除接口外的命名类增加方法。
方法的定义必须和类型的定义在同一个包中(所以不能为 int 增加方法),可见范围取决于方法名第一位大小写,使用 type 定义的新类型不能继承原类型除底层类型支持的运算外的方法
1 2 3 4 5 6 7 8 9 10 type sint int func (a sint) next() sint { return a + 1 } func main () { var a sint = 1 println (a.next()) }
接口 接口是自定义类型,是对其他类型行为的抽象,不包含数据
接口的定义、赋值、使用、断言 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 50 51 52 package maintype Student interface { Login(uname, password string ) (res bool , token string ) Logout(token string ) } type StudentInfo struct { id int name string password string token string } func (st *StudentInfo) Login(uname, password string ) (suc bool , token string ) { return true , "123456" } func (st *StudentInfo) Logout(token string ) { return } func (st *StudentInfo) UpdateName(n string ) { st.name = n return } func main () { var st Student admin := &StudentInfo{ id: 0 , name: "admin" , password: "123456" , token: "" , } st = admin println (st.(*StudentInfo).name) _, token := st.Login("admin" , "123456" ) st.Logout(token) }
空接口 1 2 3 4 5 6 7 8 9 10 func main () { var ei interface {} ei = 1 ei = true ei = "Hello" ei = [3 ]int {1 , 2 , 3 } ei = map [int ]string {1 : "a" } obj, _ := ei.(map [int ]string ) println (obj[1 ]) }
反射 反射,即在程序运行时动态创建、访问、修改任意类型的结构和成员
测试 testing
单元测试 通过 func TestGetLevel(t *testing.T) {}
定义的测试方法对功能进行测试,Xxx 不能以小写字母开头。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainfunc GetLevel (score int ) string { switch { case score >= 90 : return "优秀" case score >= 80 : return "良好" case score >= 60 : return "及格" default : return "不及格" } } func main () {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "testing" func TestGetLevel (t *testing.T) { if GetLevel(99 ) != "优秀" { t.Error("99 未正确返回 \"优秀\"" ) } if GetLevel(87 ) != "良好" { t.Error("87 未正确返回 \"良好\"" ) } if GetLevel(60 ) != "及格" { t.Error("60 未正确返回 \"及格\"" ) } }
运行测试
go test p1
# ok p1 0.209s
1 2 3 4 # 查看测试覆盖率 go test -coverprofile='test.log' # html 显示测试覆盖 go tool cover -html .\test.log
打开的网页通过不同颜色显示是否被测试覆盖
基准测试 通过 func BenchmarkFact(b *testing.B) {}
进行基准测试,若测试过程中需要重置计时器,可使用 b.ResetTimer()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainfunc Fact (n int ) int { if n > 1 { return n * Fact(n-1 ) } return n } func main () {}
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport ( "math/rand" "testing" ) func BenchmarkFact (b *testing.B) { Fact(rand.Intn(100 )) }
运行测试
1 2 3 4 5 6 7 8 go test p1 -bench . -benchmem # goos: windows # goarch: amd64 # pkg: p1 # BenchmarkFact-12 1000000000 0 B/op 0 allocs/op # PASS # ok p1 0.032s
文件、bufio 、ioutil 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 50 51 52 53 54 55 56 57 58 59 60 61 62 func Write2File (tpath, wdata string ) { f, err := os.OpenFile(tpath, os.O_APPEND, os.ModePerm) if err != nil { fmt.Printf("%s\n" , err) return } else { defer func (f *os.File) { _ = f.Close() }(f) } f.WriteString(wdata) } func ReadFromFile (tpath string ) { f, err := os.Open(tpath) if err != nil { os.Create(tpath) f, _ = os.Open(tpath) } else { defer func (f *os.File) { _ = f.Close() }(f) } var buf = make ([]byte , 200 ) for { l, err := f.Read(buf) if l == 0 || err == io.EOF { println ("\n文件读取完毕\n" ) break } else { if err != nil { fmt.Println(err) } else { println (string (buf[:l])) } } } f.Seek(-1 ,1 ) l, err := f.Read(buf) println (string (buf[:l])) } func main () { f, err := os.OpenFile("test.log" , os.O_APPEND, os.ModePerm) defer f.Close() if err == nil { log.SetOutput(f) log.SetFlags(log.Flags() | log.Lshortfile) } log.Println("日志文件测试" ) }
ioutil
1 2 3 4 5 6 7 8 9 10 11 func main () { f, _ := os.OpenFile("test.log" , os.O_APPEND, os.ModePerm) defer f.Close() d, _ := ioutil.ReadAll(f) println (string (d)) ioutil.WriteFile("test.txt" , []byte ("Hello,World" ), os.ModePerm) d2, _ := ioutil.ReadFile("test.txt" ) println (string (d2)) }
bufio
1 2 3 4 5 6 7 8 9 10 func main () { f, _ := os.OpenFile("test.log" , os.O_RDONLY, os.ModePerm) defer f.Close() sc := bufio.NewScanner(f) for sc.Scan() { println (sc.Text()) } }
删除、重命名、信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func main () { os.Rename("test.log" , "test2.log" ) os.Remove("test.log" ) os.Mkdir("a" , 0644 ) os.MkdirAll("b/c/d" , 0644 ) os.RemoveAll("b" ) f, _ := os.Stat("test.txt" ) fmt.Printf("%#v\n" , f) }
常用库 命令行解析 1 2 3 4 5 6 7 8 9 10 var a int var help bool flag.IntVar(&a, "n" , 1 , "input number" ) flag.BoolVar(&help, "h" , false , "Help" ) flag.Parse() if help { flag.Usage() return } println (Fact(a))
时间、延时 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 func main () { now := time.Now() println (now.Format("2006 年 01 月 02 日 03 时 04 分 05 秒" )) t, err := time.Parse("20060102 030405" , "20080808 100100" ) if err == nil { fmt.Println(t) } else { println (err) } fmt.Println(t.Unix()) fmt.Println(t.UnixNano()) t = time.Unix(1618189660 , 0 ) fmt.Println(t) d := now.Sub(t) println (d.Seconds()) time.Sleep(2 * time.Second) now = now.Add(10 * time.Hour) dd, err := time.ParseDuration("4h3m2s" ) now = now.Add(dd) fmt.Println(now) }
加密、哈希 MD5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { a := md5.Sum([]byte ("Hello,World!" )) m := fmt.Sprintf("% 2x" , a) b := md5.New() b.Write([]byte ("Hello," )) b.Write([]byte ("World!" )) m2 := hex.EncodeToString(b.Sum(nil )[:]) fmt.Printf(m) print ("\n" ) fmt.Printf(m2) }
Base64 1 2 3 4 5 6 7 8 9 10 11 12 13 func main () { a := base64.StdEncoding.EncodeToString([]byte ("Hello,World!" )) println (a) b, _ := base64.StdEncoding.DecodeString(a) print (string (b)) }