我们一起学猫叫 一起喵喵喵喵喵~
 
 
[[TOC]]
常量 常量的定义格式:const identifier [type] = value
显式类型定义: const b string = "abc" 
隐式类型定义: const b = "abc" 
 
变量 使用 var 关键字:var identifier type
整数的类型 
浮点型 
通常情况下应该使用 float64, 因为它比 float32 更精确
 
字符 
golang 中字符串是不可变的 
 
1 2 s1 := "hello killy"  s1[0 ] = "y"  
 
字符串的表示
双引号 
反引号: 以字符串的形式原生输出(python 中的 r'hello kitty') 
 
基本数据类型默认值 
基本数据类型的转换 Go 在不同的类型之间赋值时需要显示的转换. 即 Golang 中数据类型不能自动转换.
1 2 var  i int32  = 100 var  n1 float32  = float32 (i)
 
数据转换中若结果溢出, 编译时不会报错, 只是转换的结果会按照溢出处理.
1 2 3 var  num1 int64  = 9999999 var  num2 int8  = int8 (num1)
 
命名规范 
保持 package 名字和目录一致. 
变量名, 函数名, 常量名采用驼峰法命名 
若首字母大写, 贼可以被其它的包访问, 若小写则只能在本包中使用. 
 
指针 1 2 3 4 5 6 var  intP *int var  i int  = 10 var  ptr *int  = &i
 
源码, 反码, 补码 
控制结构 if-else 1 2 3 4 5 6 7 if  condition1 {      } else  if  condition2 {      } else  {      }
 
常用例子 判断一个字符串是否为空:
if str == "" { ... } 
if len(str) == 0 {...} 
 
函数 Abs() 用于返回一个整型数字的绝对值:
1 2 3 4 5 6 func  Abs (x int )   int  {     if  x < 0  {         return  -x     }     return  x     }
 
测试多返回值函数的错误 参考教程 
swich 1 2 3 4 5 6 7 8 switch  var1 {     case  val1:         ...     case  val2:         ...     default :         ... }
 
for结构 1 for  初始化语句; 条件语句; 修饰语句 {}
 
Break 与 continue Break 与 continue 
标签与 goto 标签与 goto 
函数 传递变长参数 1 func  myFunc (a, b, arg ...int )   {}
 
示例函数和调用:
1 2 func  Greeting (prefix string , who ...string )  Greeting("hello:" , "Joe" , "Anna" , "Eileen" )
 
在 Greeting 函数中,变量 who 的值为 []string{"Joe", "Anna", "Eileen"}
函数作为参数 1 2 3 4 5 6 7 8 9 10 11 func  main ()   {     callback(1 , Add) }func  Add (a, b int )   {     fmt.Printf("The sum of %d and %d is: %d\n" , a, b, a+b) }func  callback (y int , f func (int , int )  ) {     f(y, 2 )  }
 
使用闭包调试 1 2 3 4 5 6 7 where := func ()   {     _, file, line, _ := runtime.Caller(1 )     log.Printf("%s:%d" , file, line) } where() where()
 
通过内存缓存提升性能 牺牲空间换时间, 示例  fibonacci_memoization.go
数组和切片 声明 一个切片在未初始化之前默认为 nil,长度为 0。
切片的初始化格式是:var slice1 []type = arr1[start:end]。
使用 make() 创建去切片 func make([]T, len, cap),其中 cap 是可选参数。
所以下面两种方法可以生成相同的切片:
1 2 make ([]int , 50 , 100 )new ([100 ]int )[0 :50 ]
 
new() 和 make() 的区别
new(T) 为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 * T 的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针 ,它适用于值类型如数组和结构体(参见第 10 章);它相当于 &T{}。 
make(T) 返回一个类型为 T 的初始值 ,它只适用于 3 种内建的引用类型:切片、map 和 channel(参见第 8 章,第 13 章)。 
 
bytes 包For-range 结构这种构建方法可以应用于数组和切片
1 2 3 for  ix, value := range  slice1 {     ... }
 
切片重组(reslice) 改变切片长度的过程称之为切片重组 reslicing,做法如下:slice1 = slice1[0:end],其中 end 是新的末尾索引(即长度)
参考示例 
Map 声明、初始化和 make map 是引用类型
1 2 3 var  map1 map [keytype]valuetypevar  map1 map [string ]int 
 
未初始化的 map 的值是 nil
var map1 = make(map[keytype]valuetype)
不要用 new 构造 Map
测试键值是否存在及删除元素 判断 key 是否存在  
或者和 if 混合使用:
1 2 3 if  _, ok := map1[key1]; ok {      }
 
删除 key delete(map1, key1)
key 不存在, 不会产生错误.
For-range 1 2 3 for  key, value := range  map1 {     ... }
 
map 类型的切片 想获取一个 map 类型的切片,  必须使用两次 make(), 第一次分配切片, 第二次分配切片中每个 map 元素, 示例 
排序 代码示例 
键值对调 代码示例 
包 
GFW 牛逼 … 
有些方法得研究研究
 
init 函数 init 是一类非常特殊的函数, 不能够被人为调用, 会在包初始化后自动执行, 优先级高于 main.
结构和方法 定义 1 2 3 4 5 type  identifier struct  {     field1 type1     field2 type2     ... }
 
new() t := new(T),变量 t 是一个指向 T的指针,此时结构体字段的值是它们所属类型的零值
工厂方法创建结构体实例 按惯例,工厂的名字以 new 或 New 开头.
1 2 3 4 type  File struct  {     fd      int           name    string    }
 
这个结构体类型对应的工厂方法,它返回一个指向结构体实例的指针:
1 2 3 4 5 6 7 func  NewFile (fd int , name string )   *File {     if  fd < 0  {         return  nil      }     return  &File{fd, name} }
 
然后这样调用它:
1 f := NewFile(10 , "./test.txt" )
 
强制使用工厂方法 1 2 3 4 5 6 7 8 type  matrix truct {     ... }func  NewMatrix (params)   *matrix {     m := new (matrix)      return  m }
 
在其他包里使用工厂方法:
1 2 3 4 5 package  mainimport  "matrix"  ... wrong := new (matrix.matrix)      right := matrix.NewMatrix(...)  
 
带标签的结构体 
略
 
匿名字段和内嵌结构体 结构体可以包含一个或多个 匿名(或内嵌)字段 ,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字。匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体 
方法 定义方法的一般格式如下:
1 func  (recv receiver_type)   methodName(parameter_list) (return_value_list) { ... }
 
类型和作用在它上面定义的方法必须在同一个包里定义,这就是为什么不能在 int、float 或类似这些的类型上定义方法
函数和方法的区别 函数将变量作为参数:Function1(recv) 
方法在变量上被调用:recv.Method1() 
指针或值作为接收者  pointer_value
方法和未导出字段 提供 getter 和 setter 方法
person2.go
略
类型的 String() 方法和格式化描述符 如果类型定义了 String() 方法,它会被用在 fmt.Printf() 中生成默认的输出:等同于使用格式化描述符 %v 产生的输出。还有 fmt.Print() 和 fmt.Println() 也会自动使用 String() 方法。
接口 通过如下格式定义接口:
1 2 3 4 5 type  Namer interface  {     Method1(param_list) return_type     Method2(param_list) return_type     ... }
 
上面的 Namer 是一个 接口类型 。
(按照约定,只包含一个方法的)接口的名字由方法名加 [e]r 后缀组成,例如 Printer、Reader、Writer、Logger、Converter 等等。还有一些不常用的方式(当后缀 er 不合适时),比如 Recoverable,此时接口名以 able 结尾,或者以 I 开头(像 .NET 或 Java 中那样)
Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法。
demo demo1 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 package  mainimport  "fmt" type  Usb interface  {        Start()    Stop() }type  Phone struct  { }func  (p Phone)   Start() {    fmt.Println("手机开始工作" ) }func  (p Phone)   Stop() {    fmt.Println("手机停止工作" ) }type  Camera struct  { }func  (c Camera)   Start() {    fmt.Println("Camera 开始工作" ) }func  (c Camera)   Stop() {    fmt.Println("Camera 停止工作" ) }type  Cumputer struct  { }func  (c Cumputer)  Working(usb Usb)  {        usb.Start()    usb.Stop() }func  main ()   {            computer := new (Cumputer)    phone := new (Phone)    camera := new (Camera)        computer.Working(phone)    computer.Working(camera) }
 
1 2 3 4 手机开始工作 手机停止工作 Camera 开始工作 Camera 停止工作
 
demo2 使用接口对结构体排序
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 package  mainimport  ( 	"fmt"  	"math/rand"  	"sort"  )type  Hero struct  { 	Name string  	Age  int  }type  HeroSlice []Herofunc  (hs HeroSlice)   Len() int  { 	return  len (hs) }func  (hs HeroSlice)   Less(i, j int ) bool  { 	return  hs[i].Age < hs[j].Age }func  (hs HeroSlice)   Swap(i, j int ) { 	hs[i], hs[j] = hs[j], hs[j] 	 	 	 }func  main ()   { 	 	intSlice := []int {0 , -1 , 10 , 7 , 90 } 	 	 	sort.Ints(intSlice) 	fmt.Println(intSlice) 	 	var  heroes HeroSlice 	for  i := 0 ; i < 10 ; i++ { 		hero := Hero{ 			Name: fmt.Sprintf("英雄_%d" , rand.Intn(100 )), 			Age:  rand.Intn(100 ), 		} 		 		heroes = append (heroes, hero) 	} 	 	for  _, v := range  heroes { 		fmt.Println(v) 	} 	fmt.Println("------ 正在排序 ---------" ) 	 	sort.Sort(heroes) 	 	for  _, v := range  heroes { 		fmt.Println(v) 	} }
 
output
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [-1 0 7 10 90] {英雄_81 87} {英雄_47 59} {英雄_81 18} {英雄_25 40} {英雄_56 0} {英雄_94 11} {英雄_62 89} {英雄_28 74} {英雄_11 45} {英雄_37 6} ------ 正在排序 --------- {英雄_81 87} {英雄_81 87} {英雄_81 87} {英雄_81 87} {英雄_81 87} {英雄_81 87} {英雄_62 89} {英雄_62 89} {英雄_62 89} {英雄_62 89} 
 
类型断言 使用以下形式来进行类型断言:
1 2 3 4 5 if  v, ok := varI.(T); ok {       Process(v)     return  }
 
如果转换合法,v 是 varI 转换到类型 T 的值,ok 会是 true;否则 v 是类型 T 的零值,ok 是 false,也没有运行时错误发生。
由于接口是一般类型, 不知道具体类型. 如果要转换成具体类型, 就需要使用类型断言.
1 2 3 4 5 6 7 8 9 10 func  main  ()   { 	var  x interface {} 	b := 1.1  	 	x = b  	b_type := reflect.TypeOf(b) 	fmt.Println(b_type) 	y := x.(float64 )     fmt.Printf("y 的类型是 %T, 值是%v" , y, y) }
 
输出:
1 2 3 4 map [1 :1 ][{} {} {}] float64 y  的类型是 float64, 值是1 .1 
 
类型判断:type-switch 1 2 3 4 5 6 7 8 9 10 switch  t := areaIntf.(type ) {case  *Square:     fmt.Printf("Type Square %T with value %v\n" , t, t)case  *Circle:     fmt.Printf("Type Circle %T with value %v\n" , t, t)case  nil :     fmt.Printf("nil value: nothing to check?\n" )default :     fmt.Printf("Unexpected type %T\n" , t) }
 
测试值是否实现了某个接口 1 2 3 4 5 6 7 type  Stringer interface  {     String() string  }if  sv, ok := v.(Stringer); ok {     fmt.Printf("v implements String(): %s\n" , sv.String())  }
 
空接口 空接口或者最小接口  不包含任何方法,它对实现不做任何要求:
 
任何其他类型都实现了空接口(它不仅仅像 Java/C# 中 Object 引用类型),any 或 Any 是空接口一个很好的别名或缩写。
反射包 gorouttine Go 协程的特点:
- 有独立的栈空间
- 共享程序堆空间
- 调度由用户控制
 
demo 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  mainimport  ( 	"fmt"  	"strconv"  	"time"  )func  loop_print (c string )   { 	for  i := 1 ; i <= 10 ; i++ { 		fmt.Println(c + strconv.Itoa(i)) 		time.Sleep(time.Second) 	} }func  hello_world ()   { 	loop_print("hello world" ) }func  main ()   { 	go  hello_world() 	loop_print("hello golang" ) }
 
channel channel 本质是一个队列
线程安全, 多 goroutine 访问时, 不需要加锁.
channel 是有类型的.
声明 var <变量名> chan <数据类型> 
ch := make(chan int)
channel 是引用类型, 必须初始化, 即 make 后才能使用.
在没有使用协程的情况下, 如果管道的数据被全部取出, 继续读取数据会死锁, 报 deadlock 错误
创建空接口类型的 channel, 可以存放任意类型.
1 allChan := make (chan  interface {}, 10 )
 
使用 channel 进行 goroutine 通信demo 
使用通信共享内存, 而不是共享内存来通信
 
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 package  mainimport  ( 	"fmt"  )func  writeData (intchan chan  int )   { 	for  i := 0 ; i < 50 ; i++ { 		intchan <- i 		fmt.Printf("writeDate =%v\n" , i) 	} 	close (intchan) }func  readData (intchan chan  int , exitchan chan  bool )   { 	for  { 		v, ok := <-intchan 		if  ok { 			fmt.Printf("readDate =%v\n" , v) 		} else  { 			break  		} 	} 	 	exitchan <- true  	close (exitchan) }func  main ()   { 	 	intchan := make (chan  int , 50 ) 	exitchan := make (chan  bool , 1 ) 	go  writeData(intchan)    	go  readData(intchan, exitchan) 	 	for  true  { 		_, ok := <-exitchan 		if  !ok { 			break  		} 	} }
 
使用 select 读取管道数据 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" func  main ()   { 	 	intChan := make (chan  int , 10 ) 	for  i := 1 ; i <= 10 ; i++ { 		intChan <- i 	} 	strChan := make (chan  string , 5 ) 	for  i := 0 ; i < 5 ; i++ { 		strChan <- "hello"  + fmt.Sprintf("%d" , i) 	} 	 	 	for  { 		select  { 		case  v := <-intChan:  			fmt.Printf(" read data from intChan, %d\n" , v) 		case  v := <-strChan: 			fmt.Printf("read data from strChan, %s\n" , v) 		default : 			fmt.Printf("read data failed, alsdkjfl;kasjdlkfjklajskldfj\n" ) 			return  		} 	} }
 
goroutine 中捕获错误 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 package  mainimport  ( 	"fmt"  	"time"  )func  sayHello ()   { 	for  i := 0 ; i < 10 ; i++ { 		time.Sleep(time.Second) 		fmt.Println("hello world" ) 	} }func  test ()   { 	 	 	defer  func ()   { 		err:=recover () 		if  err !=nil  { 			fmt.Println("test() panic, err:" , err) 		} 		 		 		 	}() 	 	 	 	 	var  mymap map [int ]string  	mymap[0 ] = "golang"  	 }func  main ()   { 	go  sayHello() 	go  test() 	for  i, _ := range  "1234567890"  { 		fmt.Println("main  ok=" , i) 		time.Sleep(time.Second) 	} }
 
反射 
反射可以在运行时动态的获取变量的各种信息 
如果是结构体变量, 还可以获取到结构体本身的信息. 
通过反射, 可以修改变量的值, 可以调用相关的方法 
使用反射, 需要 import (“reflect”) 
变量 interface() 和 reflect.Value 是可以相互转换的,  
 
demo1 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  mainimport  ( 	"fmt"  	"reflect"  )func  reflectTest1 (b interface {})   { 	 	rType := reflect.TypeOf(b) 	fmt.Println("rType: " , rType) 	 	rValue := reflect.ValueOf(b) 	fmt.Printf("rValue: %v,\nrValueType: %T\n" , rValue, rValue) 	 	rValue1 := reflect.ValueOf(b).Int() 	fmt.Printf("rValue: %v,\nrValueType: %T\n" , rValue1, rValue1) 	 	iV := rValue.Interface() 	num2 := iV.(int ) 	fmt.Println("num2: " , num2) }func  reflectTest2 (b interface {})   { 	 	rType := reflect.TypeOf(b) 	fmt.Println("rType: " , rType) 	 	rValue := reflect.ValueOf(b) 	fmt.Printf("rValue: %v,\nrValueType: %T\n" , rValue, rValue) 	 	iV := rValue.Interface() 	fmt.Printf("iv: %v, type: %T\n" , iV, iV) 	 	stu, ok:=iV.(*Student)  	if  ok { 		fmt.Printf("stu.Name=%v \n" , stu.Name) 	} }type  Student struct  { 	Name string  	Age  int  }func  main ()   { 	num := 10  	reflectTest1(num) 	fmt.Println("\n" ) 	stu := new (Student) 	stu.Name = "tom"  	stu.Age = 10  	reflectTest2(stu) }
 
demo2 通过反射修改值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package  mainimport  ( 	"fmt"  	"reflect"  )func  reflectTest (b interface {})   { 	rVal:=reflect.ValueOf(b) 	 	rVal.Elem().SetInt(2 ) }func  main ()   { 	num:=2  	reflectTest(&num) 	fmt.Println("num: " ,num)	 }
 
demo3 通过反射遍历结构体, 获取方法和标签值 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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 package  mainimport  ( 	"fmt"  	"github.com/bradfitz/iter"  	"reflect"  )type  Monster struct  { 	Name  string  `json:"name"`  	Age   int     `json:"monster_age"`  	Score float64  	Sex   string  }func  (s Monster)   Print() { 	fmt.Println("===== start =====" ) 	fmt.Println(s) 	fmt.Println("===== end =====" ) }func  (s Monster)   GetSum(n1, n2 int ) int  { 	return  n1 + n2 }func  (s Monster)   Set(name string , age int , score float64 , sex string ) { 	s.Name = name 	s.Age = age 	s.Score = score 	s.Sex = sex }func  TestStruct (a interface {})   { 	typ := reflect.TypeOf(a) 	val := reflect.ValueOf(a) 	kd := val.Kind() 	if  kd != reflect.Struct { 		fmt.Println("except struct" ) 		return  	} 	 	num := val.NumField() 	fmt.Printf("struct has %d fields \n" , num) 	for  i, _ := range  iter.N(num) { 		fmt.Printf("field %d: 值: %v\n " , i, val.Field(i)) 		 		tagVal := typ.Field(i).Tag.Get("json" ) 		if  tagVal != ""  { 			fmt.Printf("field %d: tag: %v\n" , i, tagVal) 		} 	} 	numOfMethod := val.NumMethod() 	fmt.Printf("struct has %d methods\n" , numOfMethod) 	 	 	val.Method(1 ).Call(nil ) 	 	 	var  params []reflect.Value 	params = append (params, reflect.ValueOf(10 )) 	params = append (params, reflect.ValueOf(40 )) 	 	res := val.Method(0 ).Call(params) 	 	fmt.Println("red: " , res[0 ].Int()) }func  main ()   { 	m := new (Monster) 	m.Name = "test"  	m.Age = 100  	m.Score = 40.0  	m.Sex = "nan"  	TestStruct(*m) }