分类 go 下的文章

go语言了解(3) 工作流程

本章将的关键字有 defer,panic,recover,main,init,import

defer

go语言中有一个不错的设计,就是defer。尤其是调用资源时需要打开和关闭两个选项的时候就能凸显出来

它的作用是将指定的语句在函数结束时执行,没明白?咱们举个例子啊

main () {
  for i :=0;i < 5 ; i++{
    defer fmt.Println('输出:',i)
  }
}

输出的结果不是01234而是相反的43210,这样就明白它的功能了吧?再举一个常用的例子

func OpenFile() bool{
  file.Open("file/path")

  if condition1 {
    file.Close()
    return false
  }

  if condition2 {
    file.Close()
    return false
  }

  file.Close()
  return true
}

在别的语言中我们打开文件的逻辑大体是这样的,你会发现打开一个文件需要在每个条件结束的时候去再声明关闭它,
这显得语句很臃肿,而且如果我们哪个地方给忘了很可能就造成内存泄漏,但在go中它变的很简单

func OpenFile() bool{
  file.Open("file/path")
  defer file.Close()
  if condition1 {
    return false
  }

  if condition2 {
    return false
  }
  return true
}

当函数按照从上往下执行的时,执行到最下面准备结束这个函数,go将会再从下往上执行一下标明的defer的命令

panic 和 recover

这两个函数放在一起说,因为他们的作用是相对的

panic会中断控制流程的命令,是go语言的报错机制,一旦执行panic那么当前进程就会终止,
但是我们刚才讲的defer命令还是会执行,等defer执行完了就会结束这个goroutine

recover会恢复panic造成的中断,从而让进程继续进行,而且recover只能在defer中执行,同时在正常进程中执行的recover()将返回false

由于没想到十分合适的场景就先不举例了:p

main 和 init

这两个也能放在一起说

main只能有一个,而且只能在main package当中,而init()每个包里都可以有多个,也可以没有,为了方便维护,建议包里不超过一个init

这两个都是系统自动执行的函数,不用在文件中执行

在平时构建项目的时候我们会导入很多的包(package),多个包中肯定会有重复的,但是不用担心,重复导入的包只会生效一次

main package导入外界的包会优先执行外界包的init和变量初始化,如果外界的包也导入了第三方的包,那么优先执行外界包的第三方包的初始化,再执行外界包的初始化最后执行mian package的初始化

这也很好理解,我们引入包的时候这个包肯定要是完整的形态,为了让他完整就要优先满足它的条件,这也层层向外翻到了最边缘初始化完毕再一层层的向内完成初始化

import

import有几个很方便的特性比如:

import(
  f "fmt"
  . "fmt"
  _ "fmt"
  )

这三种写法

  1. 第一种是引入别名 fmt.Println 就可以简写成 f.Println
  2. 第二种更厉害了,直接就可以把包名给省去 可以当成自己的方法写成 Println
  3. 第三种其实是引入该包,不直接使用包里的函数,而是调用了该包的init函数

go语言了解(2)--切片

切片是一种复合类型,可以是数组也可以是字符串

接下来是一个简单切片使用示例

package main

import "fmt"

func slienceInfo(slience []int, str string) {
    fmt.Println("\n", str, "的长度是:", len(slience))
    fmt.Println(str, "的大小是:", cap(slience), "\n")
    for i, v := range slience {
        fmt.Println(str, "下标", i, "是", v)
    }
}

func main() {
    //定义一个数组
    var arr [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    //打印一下
    for _, v := range arr {
        fmt.Println("arr element is", v)
    }

    //获取数组 arr 从0 到下标为5(不包含下标5) 的一个切片
    mySlice := arr[:5]
    slienceInfo(mySlice, "mySlice")

    //声明一个包含5个元素的数组切片 此时会创建一个匿名数组
    elementSlience := []int{1, 2, 2, 3, 4}
    slienceInfo(elementSlience, "elementSlience")

    //声明一个默认值0,长度5,预留长度10的切片
    emptySlience := make([]int, 5, 10)
    slienceInfo(emptySlience, "emptySlience")

    //向切片中添加元素 这时数组的长度和占用内存是一致的
    emptySlience = append(emptySlience, 1, 2, 3, 4, 5)
    //如果超过分配的内存,会动态分配足够的内存空间
    //  emptySlience = append(emptySlience, 1, 2, 3, 4, 5)
    slienceInfo(emptySlience, "emptySlience2")

    mySlice = append(mySlice, emptySlience...)
    slienceInfo(mySlice, "mySlice2")

  //内容复制  
    slice1 := []int{1, 2, 3, 4, 5}
    slice2 := []int{6, 6, 6}
    //从slice2复制前三个元素到slice1当中,不同长度的按照较小的来
    copy(slice1, slice2)
    slienceInfo(slice2, "slice")

}

从示例中可以看出切片是动态分配内存大小的,我总结了一下几条规律

  1. 当切片源于一个已有的数组时,切片长度取决于截取的长度,切片分配的内存大小和源数组占用的内存大小一样
  2. 当直接生成一个切片数组时,可以定义长度和预留内存
  3. 当切片超出分配内存大小时,系统将会再分配一块足够大的内存

通常如果直到业务中明确的数组极限长度的时候,就分配足够大的内存,以缓解系统不断计算分配内存的压力,典型的空间换时间

go语言了解(1)

有人说go和php有很多相似的地方,用惯了弱语言的php是时候换换口味了

声明变量

go的声明变量的方式四种(茴香的茴有四种写法),开个玩笑,但是以下几种形式还是可以的

  1. var ValueName type //直接声明一个,声明了就要用啊,不然编译的时候就会报错
  2. var name1,name2,name3 type //类型就近原则,直接声明三个相同类型的变量
  3. var ValueName type = value //声明了变量顺便赋值
  4. var name1,name2,name3 type = value1,value2,value3 //批量赋值也可以,注意他们的类型是一样的
  5. name1,name2,name3 := value1,value2,value3 //这才是常用的写法,用 : 来自动判断类型,单个的也是这个用法
    还有个特殊的变量名, _ 这代表占个位置但是赋给他的值会被丢弃,比如 _,a := 1,2 a的值为2 但是1就给丢了 这时的使用情景碰到再说

声明常量

常量的关键词就不是 var 了 而是 const,和变量的区别是一旦定义就不能改了

  1. const ValueName = value //直接赋值
  2. const Pi float32 = 3.1415926 //也可以声明常量的类型

###分组赋值
在多个包里用到再赋值有的时候不利于debug,可以直接把要赋值的都放到一起 比如:

const{
      ENV := true
      LOCAL := 'dev'
      DATE := 20170307
}

var{
    i int
    pi float32
    name string
}

##boolean类型
true or false 默认为false

##数值类型
go支持 int,uint可以定义的类型有rune,int8,int16,int32,int64,byte,uint8,uint16,uint16,uint32,uint64

其中 rune是int32的别称,byte是uint8的别称

不同类型的数不能相互赋值和操作 二进制的数不能和八进制的相加减

浮点型也有float32,float64两种,没有 float这个类型,默认 float64

##字符串
字符串的赋值方式遵循着上面的 声明变量 那一块说的。

Value1 := 'string' 和 Value1 := "string" 一个效果 ""代表的是一个空字符串也是占着内存的

字符串之间的拼接用 + 比如

a,b :="hello","world"  

c := a + b

Printf("%s\n",c)  //输出 helloworld

字符串的修改不能直接修改,可以用切片的方式实现例如:

a := 'hello'
b := 'w' + a[1:]
//b 的值为 wello

什么是切片我们一会儿就会说到