go的语法
概述
有接触go语言,工作中用的比较少,偶尔做个小项目,最近看到的一个项目也从go迁移到java。
但是对go还是恋恋不忘,它语法比较简洁,库也比较多,编译速度快,等等优点,让我忘不了。
对go的语法做个简单的总结,内容主要来源官网的pdf,学习go语言
语法
安装和运行
安装go,上官网学学,我的系统是mac 直接brew安装
通过go build编译成二进制,然后直接运行,go提供了众多工具,说来惭愧用的比较少,build,test用的比较多吧
至于包管理以前用govender,现在有官方的mod
基础语法
支持各种类型:bool,int,float,double,complex,int32,byte,int64,string
注意go里面int和int32不是同一种类型,赋值需要转一次
go的变量定义比较奇怪,据说是方便编译器解析比如,定义一个变量
var a int = 12或者是自动推导 a:=12
可以通过const xxx = aaa 定义常量
可以使用iota定义枚举,它会自增
也提供了丰富的string操作,这个是利器,其实程序员很大一部分工作都是在和字符串搏斗。
控制结构和python很像,又不同,没有while,都通过for提供能够,也支持goto和switch
switch里面不必要写break了,可以通过fallthrough进入下一个判断
语法的例子
var a int
var b bool
a = 1
b = true
fmt.Println(a, b)
//多个变量赋值和丢弃
a,b = 2,true
a,_ = 12,false
fmt.Println(const1, const2)
//string
str1 := "hi world!"
csts := []rune(str1)
csts[0] = 'a'
str2 := string(csts)
fmt.Printf("str2=%s\n",str2)
var comp complex64 = 5+5i;
fmt.Printf("val %v\n", comp)
//var e error
if a > 2{
fmt.Printf("> a=%d\n",a)
}else{
fmt.Printf("< a=%d\n",a)
}
if err := os.Chmod("/data1/applogs",os.ModePerm); err != nil{
fmt.Println(err)
}else{
fmt.Println("chmod ok!")
}
aFunc()
for i:=0;i < 10;i++{
fmt.Print(i)
}
//
list1 := []string{"a","b","c","d"}
for k,v := range list1{
fmt.Print(k,":",v)
}
var c byte = 'e'
switch {
case '0' <=c:
fmt.Println("aa")
fallthrough
case 'a' <= c:
fmt.Println("bb")
}
i :=0 Here: fmt.Print(i) i++ if i < 10{ goto Here }else{ return }
go提供了几种基础类型 数组,slice列表 map字典 chan
除了chan用于管道通信,从效果上和其他语言没啥差别,语法有点不同,多用几次,熟练就好了
slice和map的例子
var arr1[10]int arr1[0] = 1 fmt.Printf("%d",arr1[0]) arr2 := [3]int{1,2,3} arr3 := [...]int{1,2,3} fmt.Println(arr2,arr3) arr4 := [3][2]int{{1,2},{3,4},{5,6}} fmt.Println(arr4) //slice slice1 := make([]int, 10) fmt.Println(len(slice1),cap(slice1)) slice2 := arr2[1:2] slice2[0] = 123 fmt.Println(arr2) slice2 = append(slice2,1,2,3,4,5,6) fmt.Println(slice2) slice3 := make([]int,20) n1:=copy(slice3,slice2) fmt.Println(n1,slice3) //map map1 := make(map[string]int) map1["a0"] = 123 map1["a1"] = 123 map1["a2"] = 123 map1["a3"] = 123 fmt.Println(map1) delete(map1,"a0") fmt.Println(map1)
作用域的问题
对于我这种新手来说go的作用域是最容易引起混乱的。因为参数可以随便传啊。一堆goroutine共享变量,很容易出错。
注意全局变量和局部变量,也需注意 :=不仅仅是声明和创建,
函数
go的函数支持几个特别的特性,多个返回值,函数做参数,命名返回,变参,panic,recover
支持defer函数退出时执行,有点像try{}finally{}但是封装的更加方便,采用栈的方式去收集defer函数
panic 有点像java的exception ,没有捕获一层层往上抛出。结合defer和recover我们可以用来处理特殊情况
func testfunc(){ //打印 rec(1) //作用域 p() fmt.Println(a1234) //多值返回 //命名返回值 ret1,ret2 := p12() fmt.Println(ret1, ret2) //延迟执行 readWrite() //延迟的顺序 deferSeq() //变参 myFunc(1,2,3,4,5) //原样传递参数 myFunc2(1,2,3,4,5) //函数作为值 hello := func() { fmt.Println("hello world") } hello() //函数回调 aCallback(10, func88) //使用panic bol := throwPanic(aPanic) fmt.Println("bol:",bol) } func throwPanic(f func())(b bool){ defer func(){ if x:=recover(); x!= nil{ b = true } }() f() return false } func aPanic(){ panic("throw a panic") } func aCallback(y int, f func(int)){ f(y) } func func88(i int) { fmt.Printf("i=%d",i*i) } func myFunc2(arg ...int){ myFunc(arg...) } func myFunc(arg ...int) { for _,n := range arg{ fmt.Printf("n=%d,",n) } } func deferSeq() { for i:=0; i < 5;i++{ defer func(x int){ fmt.Printf("%d\t",x) }(i) } } func readWrite() bool{ fp,err := os.Open("/data1/applogs/superfans/info-2019-01-02.log") defer fp.Close() if err != nil{ return false } return true } func p12()(a int,b int){ n := 1 a = 1 b = n return } var a1234 int func p(){ a1234 := 5 fmt.Println(a1234) } func rec(i int){ if i == 10{ return } rec(i+1) fmt.Printf("%d ", i) }
包
go里面没有private protected publice关键字,通过大小写去区分公用还是私有,
大写公用,小写属于包私有的
内存分配
make 只针对slice map chan,返回三种类型
new 任意类型,分配内存空间,返回指针
自定义类型
和c一样,使用struct作为自定义类型
go没有继承的概念,但是可以使用匿名字段
也支持方法,但是必须在函数名字前面加上类型,其实是把类型也作为参数传递给函数吧
go 有指针的概念,但是为了防止指针滥用,不支持指针的+和-
这是上面说明的例子
func testMoreFunc(){ //指针 var p *int fmt.Printf("%v",p) var i int p = &i fmt.Printf("%v",p) *p = 8 fmt.Println("%v",i) //内存分配 //new 返回地址 make返回自建类型 psp := new (SynceBuffer) var syncbufer SynceBuffer fmt.Println("%v,%v",psp,&syncbufer) //仅仅适用于 map,slice,channel slice1:= make([]int, 10) map1 := make(map[string]string) chan1 := make(chan int,10) fmt.Println(len(slice1),len(map1),len(chan1)) //直接使用符合声明创建 syncbufer2 := &SynceBuffer{sync.Mutex{},bytes.Buffer{}} fmt.Println("%v",syncbufer2) //自定义类型 testAge() //结构体字段 aNull := struct{}{} fmt.Printf("%v", aNull) //大写不可导出,小写可以导出 str1 := &Str1{name:"hehe"} str1.T12 = 12 str1.showIt() //方法转换: 先找n, 再找&n str11 := Str1{name:"hehe11"} str11.showIt() //转换 convertStr() //迁入的方式实现组合 } func convertStr(){ //byte类型转换 mystr := "aba12" byteslice := []byte(mystr) runeSlice := []rune(mystr) fmt.Printf("%v, %v",byteslice, runeSlice) fmt.Printf("%v, %v",string(byteslice), string(runeSlice)) //自定义类型的转换 var t12 T12 = 123; var t13 int t13 = int(t12) fmt.Printf("%v", t13) } type T12 int type Str1 struct { T12 name string } func (str1 *Str1) showIt(){ fmt.Printf("%v", str1) } func testAge(){ a := new(NameAge) a.name = "peter" a.age = 123 fmt.Println("%v\n",a) } type NameAge struct { name string age int } type SynceBuffer struct{ lock sync.Mutex buffer bytes.Buffer }
接口
go的接口和python有点像,只要实现了某个方法你就可以实现转换
支持类型判断 xx.(type)
也支持反射reflect,提供两类方法,一类是获取字段的type,一类是获取字段的value
func testInterface(){ //测试接口 s1 := S1{1} //finter1(s1)//传递接口有问题,因为是*引用实现了接口 finter1(&s1); //获取类型 .(type) //类型判断 gtype(&s1) //方法的定义只能是本地的 //接口名字,方法名+er //写代码时面向接口编程 //自省和反射 showTag(&Person{}) } func showTag(i interface{}){ t := reflect.TypeOf(i) v := reflect.ValueOf(i) fmt.Printf("%v,%v",t,v) switch t.Kind() { case reflect.Ptr: tag := t.Elem().Field(0).Tag fmt.Printf("tag:%s",tag) default: tag := t.Field(0).Tag fmt.Printf("tag:%s",tag) } } type Person struct { name string "namestr" age int } /* func (i int) assf(){ } */ func gtype(som interface{})int{ return som.(I1).Get(); } func finter1(i1 I1){ switch t := i1.(type) { case *S1: fmt.Println("type is *s1") default: fmt.Println("%v",t) } fmt.Println(i1.Get()) i1.Put(123) fmt.Println(i1.Get()) } type I1 interface { Get()int Put(int) } type S1 struct { i int } func(p *S1) Get() int { return p.i; } func (p *S1)Put(v int){ p.i = v }
通道
通道可以理解成管道,可以定义各种类型的通道,但是通道发送和接收必须成对出现,不然会报错
可以定义任意类型的通道
var a chan int , var b chan func(), var c chan struct{}
通道没有if判断,却可以使用select做选择
func tgoroutine(){ //torungoroutine() torungoroutineChan() } func torungoroutineChan(){ c := make(chan int,4) fmt.Println("i'm waitting") go readyNew("tom",1,c) go readyNew("tim",1,c) i := 0 L:for{ select{ case <-c: i++ if i > 1{ break L } } } close(c) } func readyNew(w string, sec int, c chan int ){ time.Sleep(time.Duration(sec) * time.Second) fmt.Println(w," is ready") c <-1 } func torungoroutine(){ go ready("tom",2) go ready("ted",1) fmt.Println("i'm waitting") time.Sleep(5 * time.Second) } func ready(w string, sec int){ time.Sleep(time.Duration(sec) * time.Second) fmt.Println(w," is ready") }
常用的包
参数获取,文件读取,命令行执行
func netFunc(){ r, err := http.Get("http://baidu.com") if err != nil{ fmt.Printf("%v\n", err) return }else{ b, err := ioutil.ReadAll(r.Body) r.Body.Close() if err == nil{ fmt.Printf("%s", string(b)) } } } func cmdCommon(){ cmd := exec.Command("/bin/ls","-l") buf,err := cmd.Output() if err != nil{ fmt.Printf("err %v\n",err) }else{ fmt.Printf("buf:%s\n",string(buf)) } } func flagParse(){ b := flag.Bool("b",false, "a bool") port := flag.String("port","33","set port") flag.Usage =func(){ fmt.Fprintf(os.Stderr,"%s",os.Args[0]) flag.PrintDefaults() } flag.Parse() fmt.Printf("%v,%v",*b,*port) } func dirOpt(){ //文件操作 fineName := "/data1test/" if f,e := os.Stat(fineName);e != nil{ os.Mkdir(fineName,0755) }else{ fmt.Printf("%v",f) } } func aBufOutput(){ buf := make([]byte, 1024) f,_ := os.Open("/data1test/info-2019-01-02.log") defer f.Close() r := bufio.NewReader(f) w := bufio.NewWriter(os.Stdout) defer w.Flush() for{ n,_ := r.Read(buf) if n == 0{ break } os.Stdout.Write(buf[:n]) } } func noBufOutput(){ buf := make([]byte, 1024) f,_ := os.Open("/data1test/info-2019-01-02.log") defer f.Close() for{ n,_ := f.Read(buf) if n == 0{ break } os.Stdout.Write(buf[:n]) } }
总结
go的语法告一段落,但是如果仅仅只了解这些还不足以写一个好的golang程序,但是调调库啥的是没问题。
我们需要去读更多优秀的开源项目,汲取营养。
后续我会说明一些好用的库,也会阅读一些项目的源码