go语言实战 - 3、4、5

第三章 打包和工具链

包 package

  • go语言的程序会被组织成若干组文件,每组文件都被称为一个包
  • 不能把多个包放在同一个目录中
  • 不能把同一个包拆分到多个不同目录中
  • 一个目录下的.go文件必须声明同一个包名
  • 包名命名的管理是使用包所在的目录的名字
  • 包的导入使用全路径,允许名字重复
  • 导入后的报名默认使用包的名字,但是是可以修改的
  • main包会被编译成二进制可执行文件
  • main包需要有个main函数作为主函数入口
  • go build 命令用于构建可执行程序

导入 import

  • import语句告诉编译器到磁盘的哪里去寻找导入的包
  • 编译器会使用Go环境变量设置的路径,通过引入的相对路径来查找磁盘上的包
  • 标准库中的包会在安装Go的位置找到
  • Go开发者创建的包会在Gopath的环境变量指定的目录中查找
  • go get命令将获取指定URL的包
  • 包支持命名导入 import nh net/http
  • 导入一个不在代码里使用的包时,会导致编译报错,避免代码变得臃肿
  • _空白标识符用于抛弃,比如导入的包,抛出的异常,返回的函数都可以被抛弃

初始函数 init

  • 每个包可以包含任意多个init函数,这些函数在程序执行开始的时候被调用
  • 所有被编译器发现的init函数都会在main函数之前执行
  • init函数主要用在设置包、初始化变量、或者其他先导设置工作,例如在数据库驱动启动的包中,初始化函数会将自身注册到sql包中,启动之后才可以完成数据库的调用
  • 空白标识符可以保证导入包的初始化函数正常执行,而不会报错

go 工具

  • go build 执行编译
  • go clean 删除编译之后产生的二进制文件
  • go run 先编译,后执行
  • go vet 帮助开发人员检测代码的常见错误
    • 类型匹配错误
    • 方法签名调用错误
    • 错误的结构标签
    • 没有指定字段名的结构字面量
  • go fmt 将代码布局修改成和go源码类似的风格
  • go doc tar可以在命令行打开tar包的相关文档
  • godoc -http=:6060可以在web浏览文档

良好的习惯

  • 包应该在代码库的根目录
  • 包可以非常小
  • 代码需要执行 go fmt
  • 给代码写文档

第四章 数组、切片和映射

数组

  • 数组是一个长度固定的数据类型,用于存储一段具有相同的类型的元素的连续块
  • 内存时连续分配的
  • 数组的类型信息可以提供每次访问一个元素时需要在内存中移动的距离
  • 随机读取速度非常快
  • 声明 var array [5]int,声明之后数组里存储的数据类型和长度都不能改变了
  • 声明之后,数组的值默认为零值
  • 使用数组字面量可以在定义的时候给数组元素赋值 array := [5]int{1,2,3,4,5}
  • 相同类型的数组可以赋值给另一个数组
  • 数组变量的类型时包含长度和每个元素的类型的,只有两部分都相同的数组,才是同类型的数组,才可以互相赋值
  • 指针数组赋值,两个数组会指向同一组的字符串
  • 多维数组 var array [2][2]int
  • 函数之间传递数组的开销比较大,值传递需要复制整个数组,最好只传入指向数组的指针

切片

  • 切片的概念类似于动态数组,可以根据需要自动增长和缩小
  • 切片是一个很小的对象,对底层数组进行了抽象,并提供相关的方法
  • 前片有3个字段的数据,指向底层数组的指针,切片长度,切片容量
  • 切片的创建方法
    • make创建
      • 只指定长度,长度和容量相等 slice := make([]string, 5)
      • 指定了长度和容量 slice := make([]string, 3, 5)
      • 不允许创建容量小于长度的切片
    • 使用切片字面量来创建
      • 长度和容量都是3 slice := []string{"r", "g", "b"}
      • 创建长度和容量是100的切片 slice := []string{99: ""}
    • nil 切片 var slice []int
  • 空切片,在底层数组中包含0个元素,也没有分配内核的存储空间
    • slice := []int{}
    • slice := make([]int, 0)
  • 切片索引的赋值和数组完全一致
  • 切片可以理解为底层数组的一个滑动窗口,类似于视图的概念
  • 底层数组容量是k的切片slice[i:j]其长度为j-i,容量为k-i
  • 切片是可以根据需要来增加切片的容量的,go语言内置的append函数会处理其细节
1
2
3
4
5
6
7
8
9
10
// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
// slice = append(slice, elem1, elem2)
// slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
// slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type
  • 切片添加多个数据用...完成
  • 切片迭代for index, val := range slice
  • 切片也支持多维
  • 函数内传递切片采用值传递,其本身尺寸很小

映射

  • 映射用于存储一组无序的键值对
  • 查询时间复杂度O(1)
  • 映射是可以迭代的,但是不保证顺序
  • 映射的散列表包含一组桶,在存储、删除或者查找键值对的时候,所有操作都需要选择一个桶
  • 散列函数生成的散列值的低位来选择桶
  • 创建和初始化
    • dict := make(map[string]int)
    • dict := map[string]string{"Red":"red"}
    • dict := map[int][]string{}

小结

  • 数组是构造切片和映射的基石
  • go语言里切片经常用于处理数据的集合,映射用来处理具有键值对结构的数据
  • 内置函数make可以创建切片和映射,并制定原始的长度和容量
  • 切片有容量限制,可以使用append来扩展容量
  • 映射的增长没有任何限制
  • 内置函数len可以用来获取切片或者映射的长度
  • 内置函数cap只能用于切片
  • 将切片或者映射传递给函数的成本很小,不会复制底层的数据结构

第五章 Go语言的类型系统

  • go语言是一种静态类型的语言,编译器编译时需要知道每个值的类型
  • 值的类型可以使得编译器知道分配多少内存给这个值,以及这个内存存储什么数据

用户定义的类型

  • 当用户声明一个新的类型时,这个声明就给编译器提供一个框架,告知必要的内存大小和表示信息
  • 自定义类型的方法
1
2
3
4
5
type user struct {
name string
age int
}
var bill user
  • 声明变量时,这个变量对应的值总是会被初始化,未指定初始化的值时,各个字段默认取零值
    • 数字类型零值为0
    • 字符串类型零值为””
    • bool类型零值为false

方法

  • 方法能给用户定义的类型添加新的行为
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
type user struct {
name string
email string
}
func (u user) notify () {
fmt.Printf("Sending User Email To %s<%s> \n", u.name, u.email)
}
func (u user) changeEmail(email string) {
u.email = email
}
func main () {
bill := user {
name: "Bill",
email: "bill@email.com",
}
bill.notify()
lisa := &user{"Lisa", "lisa@email.com"}
lisa.notify()
bill.changeEmail("bill@new.com")
bill.notify()
}
  • 实际上一个函数如果有接收者,这个函数就被称为方法

类型的本质

  • 内置类型

    • 对内置类型进行增加或者删除的时候,会创建一个新值,所以函数中内置类型的传递时值传递
  • 引用类型

    • 引用类型包括:切片,映射,通道,接口,函数类型
    • 创建的变量被称为标头值,函数中使用是指针传递,本质上共享底层数据结构
  • 结构类型

    • 当需要更改类型本身的值时,采用指针传递
    • 当不需要更改类型本身的值时,用值传递

接口

  • 多态是指代码可以根据类型的具体实现采用不同行为的能力
  • 如果一个类型实现了这个接口的所有方法,那么就是实现了这个接口
  • 接口是用来定义行为的类型
Donate comment here