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)) }