ninetyhe
格式
程序统⼀使⽤ ”gofmt“ 进行格式化;如果使用的IDE是Goland可以按照如下进行配置:PrePerences -> Tools -> FileWatcher(如果没有在plugins安装) :添加 go fmt;
推荐一并添加:golangci-lint 和 goimports。这样就可以进⾏语法检查和⾃动处理导入和删除包注释
// ⾏注释⽅式,⽤于简单的方法和字段注释
/* */ 块注释方式,本书推荐在每一个包前面注释,说明该包的主要作用
/*
function: do xxx
*/
package regexp
名字
Go语⾔言的命名⼤小写,决定了该方法或者该类是否公有还是私有,本书推荐如果⽅法不对外调用,首字⺟建议小写,如果需要跨包调⽤则⾸字⺟需要大写;
包的命名统一使⽤用小写,并且不应该使⽤下划线或者驼峰。(这点表示有点别扭,如果名字真的过长的话,可读性会很差) Get/Set:Go不提供对Get和Set支持,但是推荐⾃己实现,但是对于Get建议直接⽤⾸字母代替,例如:如果你有一个域叫做owner(⼩写,不被导出),则Get方法应该叫做 Owner (大写,被导出),⽽不是 GetOwner (个人觉得有点反人类,⽽且protobuf generate 的pb文件的字段也是是 Getxx )。这里联想到⽬前我们⾃⼰用的gRPC,看到很多同学的字段命名都是大驼峰的,开始还以为gRPC有特殊不同的规范,后来查阅了官网更加肯定,对象的属性要⼩驼峰,字段的访问需要通过Get和Set来访问!⽅法命名:单个方法的接⼝口使⽤方法名加上“er”后缀来命名,或者类似的修改来构造⼀个施动者名词
分号
Go语⾔省略分号; {} 中的左 { 不能换行;条件语句省略 ()
控制结构
Go中的循环结构只用 for 语句,主要分为以下三种:
// 传统的for写法 for init; condition; post { }
// 对应其他语言中 while for condition { }
//⼀直循环 for { }
函数
⽀持多返回值,Go语⾔支持函数返回多个返回值,本书推荐也给返回值命名,这样对于以下int值对应哪
个就值就⼀⽬了然 例例如:
func (file *File) Write(b []byte) (n int, m int, err error)
延时执行defer;这是一种不不寻常但又很有效的方法,⽤于处理类似于不管函数通过哪个执行路径返回,
资源都必须要被释放的情况。经常⽤用于链接断开,释放锁或者关闭IO如果需要将⼀些值初始化,或者在代码执行前调⽤,可以在 init 函数实现,该函数会优先被执⾏;
数据
Go语⾔言使⽤new和make分配原语; new ⽤于给T类型的分配内存,但是并不会初始化内存,只是将其置为0; make 不同于 new ,它用于创建 silence , map 和 channel ,并返回一个初始化(⽽不是置0),类型给T的值。如果需要获得一个显式的指针,就必须只⽤用 new 分配。
构造器 :本书推荐结构体的属性值尽量不要有空或者空,通过构造器对其初始化;
数组 :在Go中:数组是值。将⼀个数组赋值给另一个,会拷⻉所有的元素;特别是,如果你给函数传递⼀个数组,其将收到一个数组的拷贝,⽽不是它的指针;组的大小是其类型的一部分。类型
[10]int 和 [20]int 是不同的。
切片 :切⽚持有对底层数组的引用,如果你将⼀个切⽚赋值给另⼀个,⼆者都将引用同⼀个数组,或你将切片的值进行修改,则原数组的内容也将发⽣改变
字典 :同切片一样,map持有对底层数据结构的引用。如果将map传递给函数,其对map的内容做了改变,则这些改变对于调⽤者是可⻅的。如果像判断Map释放包含该元素,需要使用 :
//感觉好蠢,并没有像Java那样有contains key或者contains value,//如果像⽅便使⽤且避免代码写成一坨,建议⾃⼰实现一个⽅法收敛该逻辑if value,ok := map[key];ok{
// if exist do some
}
//⽽不建议使⽤以下⽅式:value, ret := map1["key"]
if ret == true{
fmt.Print("map1[\"key\"]存在,值为:", i)
} else {
fmt.Print("map1[\"key\"],不存在\n")
}
初始化
常量 在编译的时候就被创建,并且只能是数字,字符(附文),字符串或者布尔类型。由于编译限制,
表达式必须为能被编译器求值的常量表达式, 例如:// 常量表达式
1<<3
// ⾮常量表达式,在const不可使用math.Sin(math.Pi/4)
init函数 如果需要将一些值初始化,或者需要预先调⽤⽅法函数,可以在 int 函数中实现;例如:
func init() {
//需要初始化调用方法
initFunction(a interface{},b interface{})
// 初始化变量
initParam
}
接⼝
通过法与接口,Go语⾔定义了一种与java/C++等OOP语言截然不同的“继承”的形态。通过实现接口定
义的方法,便可将reciever的类型变量赋值给接⼝类型变量,通过接⼝类型变量来调用到reciever类型的
⽅法 例如:
//定义了一个接口geometry表示几何类型type thisIsInterface interface {
funcA() int32
funcB(bool)
}
//实现类实现两个方法type IntImpl struct {
A, B float64
}
//在Go中,实现接⼝,只需要实现该接口定义的所有方法即可
//A接口方法实现
func (r * IntImpl) funcA(res float64) {
A *= res
B *= res
}
func (r * IntImpl) funcB() float64 {
return r.A * r.B
}
//可以把rect和circle类型的变量量作为实参
//传递给geometry接口类型的变量
func measure (i thisIsInterface){
fmt.Print("i 's area:",i.funcA(),"\n")
i.funcB(2)
fmt.Print("after funcB , funcA :",i.funcA(),"\n")
}
func main() {
i := IntImpl{A: 10, B: 5}
measure(&i)
}
并发
Gorutine 轻量级并发的函数执行线索,创建开销初始化栈空间⽐较⼩,会根据实际需要堆的空间分配或
者释放额外空间Gorutine与操作系统线程间采用“多对多”的映射⽅方式,其他Gorutines不会因为其中⼀一个Gorutines阻塞后
⽽阻塞
在函数前加 go 关键字就可以创建⼀个Gorutine并调⽤该函数⽅法。当该函数执行完成之后隐式推出(类似于Java中的 Thread )
Channel:于map类似,也是通过 make 进⾏行行分配,如果创建的时候指定⼤小,如果⾮零则即创建一个
缓冲区。如果是零,则是⽆无缓冲的channel或同步channel
例例如:
stringBuffer := make(chan string,10)
value := <- stringBuffer
并行实现方式:可以创建多个go func 并行执⾏多个函数,然后通过sync.WaitGroup来实现阻塞等并⾏的结果(类似于Java中的 CountDownlunch ),但是功能相当简陋陋,功能相差更更远
互斥: 通过sync.Mutex可以创建锁进行互斥,例例如:
import (
"sync"
"runtime"
"fmt"
)
var {
//共享变量
num int
wg sync.WaitGroup
//锁
lock sync.Mutex
}
func Add(){
defer wg.Done()
for count := 0;ciunt < 2; count++{
// lock
}
}
lock.Lock()
{
value := num
value++
num = value
}
// release lock
lock.Unlock()
func main{
wg.Add(2)
go Add()
go Add()
wg.Wait()
fmt.Printf("result is : %d\n", num)
}
结果如下:
result is : 4
类型断⾔
Go⾥面对类型判断有两种: value,ok := element.(T) 当然也可以直接这样用 (不推
荐) value:=element.(T) 这样的话,⼀旦出错就会产⽣运⾏错误,不推荐使用;