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 ",str2)

var comp complex64 = 5+5i;
fmt.Printf("val %v ", comp)

//var e error

if a > 2{
fmt.Printf("> a=%d ",a)
}else{
fmt.Printf("< a=%d ",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	",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
",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
", 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
",err)
    }else{
        fmt.Printf("buf:%s
",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程序,但是调调库啥的是没问题。

  我们需要去读更多优秀的开源项目,汲取营养。

  后续我会说明一些好用的库,也会阅读一些项目的源码

原文地址:https://www.cnblogs.com/beckbi/p/10542318.html