包的概念、导入与可见性


概念

包是结构化代码的一种方式:每个程序都由包的概念组成,可以使用自身的包,或者从其他包中导入内容

类似某些编程语言中的类库或者命名空间的概念,每个文件都属于且仅属于一个包。一个包可以由许多以 .go 为扩展名的源文件组成,所以文件名和包名通常是不相同的

在编写go文件时, 必须在非注释的第一行指明这个文件属于哪个包 ,如 package main ,表示一个可独立执行的程序,每个Go应用程序都包含一个 main 包。若想要编译的包名不是 main 的包,如 pack1 ,则会产生 pack1.a 而不是可执行程序

所有的包名都应使用小写字母

标准库

即Go的安装文件中包含的可以直接使用的包。在 Windows 下,标准库的位置在 Go 根目录下的子目录 pkg\windows_386 中;在 Linux 下,标准库在 Go 根目录下的子目录 pkg\linux_amd64 中(如果是安装的是 32 位,则在 linux_386 目录中)。一般情况下,标准包会存放在 $GOROOT/pkg/$GOOS_$GOARCH/ 目录下。

包的编译

如果想要构建一个程序,则包和包内的文件必须以正确的顺讯进行编译。包的依赖关系决定了其构建顺序。属于同一个包的源文件必须全部被遗弃编译,一个包即为编译时的一个单元。因此根据惯例,每个目录都只包含一个包

若对一个包进行更改或者重新编译,所有引用了这个包的客户端程序*都必须全部重新编译 (为啥是客户端)

Go中的包模型采用了显式依赖关系的机制来达到快速编译的目的,编译器会从后缀名为 .o 的对象文件中提取传递依赖类型的信息

以下是编译实例

  1. a.go依赖b.go,而b.go依赖于c.go,则会
  2. - 先编译c.gob.go,最后是a.go
  3. - 为了编译a.go,编译器将读取b.o(没错就是b.o)

这种编译机制可以显著提升大型项目的编译速度

每段代码只会编译一次

导入包

概念

导入包相当于包含了这个包所有的代码对象

除了 _ 以外,所有的代码对象的标识符必须是唯一的,以避免命名冲突。但是相同的标识符可以在不同的包中使用,因为可以使用包名进行区分

如何导入

Go程序通过 import 关键字将包链接在一起,如 import "fmt" 表示导入 fmt 包(该包实现了格式化的IO)。导入多个包,可以有如下几种方法

  • 分别导入 ```go import “fmt” import “os”

//或者 import “fmt”;import “os”

  1. - 优雅的因式分解关键字(同样适用于constvartype的声明或者定义)
  2. ```go
  3. import(
  4. "fmt"
  5. "os"
  6. )
  7. //或者
  8. import(
  9. "fmt";"os"
  10. )

在导入多个包的时候,最好按照字母进行排序

查找包

如果包名不是以 . 或者 / 开头,则Go会在全局文件进行查找;以 ./ 开头,则会在相对目录中进行查找;以 /(Windows中也可以这么用)开头,则会在系统的绝对路径中查找

注:
以相对路径在GOPATH下导入包,会产生报错信息

可见性规则(有意思)

  • 当标识符以一个 大写字母 开头的时候,如 Group ,这种形式的标识符就可以被外部包的代码所使用,这被称为导出(类似public),在外部包可以使用 PackageName.Group进行访问
  • 以小写字母开头的,则对外界是不可见,但在整个包的内部是可见的(类似private)

    大写字母可以使用任何unicode编码的字符,比如希腊文。。。

可以给包添加别名: import fm "fmt" ,就可以通过fm使用fmt包了

包的分级声明和初始化

可以在使用import导入包之后,定义/声明若干const、var、type等,这些对象的作用域都是全局的(本包范围内)

注意事项

若导入了包却不使用,则会导致错误,因为Go所遵循的格言是:没有不必要的代码

函数


函数的格式

  • 整体格式

    1. func functionName(parameter_list) (return_value_list){
    2. ...
    3. }
    • parameter_list的形式为 (param1 type1, param2 type2...)
    • return_value_list的形式 (ret1 type1, ret2 type2...)
      • 函数也可以返回函数哦
  • 语法格式的注意事项
    • 关于大括号
      • 这是所有结构体都需要注意的
      • { 需要放在声明的行尾, } 需要放在紧跟函数体的后一行
      • 因为,Go语言不需要 ; 号标记语句的结束,但其实是由编译器完成的,所以不这么使用 {} 则会引发错误
    • 关于命名
      • 只有在某个函数需要被外部调用的时候,才使用大写字母开头,并遵循Pascal命名法(大写驼峰)。否则使用驼峰命名法
  • 关于使用预定义的方法
    • 如print、println等等,在单纯地打印一个字符串或者变量的时候,可以使用。不过在部署的时候,请替换成 fmt. 的方法
  • 程序正常退出

    • 也就是执行完main函数,正常退出的代码是0,;若是因为异常而终止,则会返回非零值

      特殊的函数

  • main函数

    • main函数是每一个可执行程序所必须包含的
    • 通常都是启动后第一个执行的函数,除非有 init() 函数
    • 若main包的源码中不包含main函数,则会引发构建错误
    • mian函数 含有参数和返回类型,这与C族语言相反

      注释


注释的形式

有两种

  • 单行注释 //
  • 块注释 /* */

    注释的使用

    值得注意的是,go提供了godoc工具,会收集注释并产生技术文档,所以在使用注释的时候有如下建议

  • 每个包都应该有相关注释

  • 几乎所有的全局作用域的,类型、常量、变量、函数和被到处的对象都应有注释
    • 若注释出现在函数之前,则应以函数名开头

两个例子

  1. // Package superman implements methods for saving the world.
  2. //
  3. // Experience has shown that a small number of procedures can prove
  4. // helpful when attempting to save the world.
  5. package superman
  1. // enterOrbit causes Superman to fly into low Earth orbit, a position
  2. // that presents several possibilities for planet salvation.
  3. func enterOrbit() error {
  4. ...
  5. }

类型


概念

  • 类型定义了某个变量的值的集合与其操作的集合
  • 使用var声明的变量的值会自动初始化为 该类型 的零值

    分类

  • 基本类型

    • int
    • float
    • bool
    • string
  • 结构化的

    结构化的类型没有真正的值,所以其零值为nil

    • struct
    • array
    • slice
    • map
    • channel
  • 描述类型的行为的
    • interface
  • 函数也可以是一个确定的类型
    • 即作为返回类型的时候,这种类型的声明要写在函数名和可选的参数列表之后
  • 定义类型的别名
    • 形式
      • type IZ int
      • 因式分解形式

type(
IZ int
FZ float64
STR string
)

  • 注意
    • 这并不是真正意义上的别名,因为这样定义之后的类型,可以拥有更多的特性,且在类型转换的时候必须 显示转换

      Go程序的一般结构


  1. package main
  2. import (
  3. "fmt"
  4. )
  5. //在完成import之后,开始对常量、变量和类型进行定义、声明
  6. const c = "C"
  7. var v int = 5
  8. type T struct{}
  9. //如果存在init函数,则对该函数进行定义
  10. func init() { // initialization of package
  11. }
  12. //如果是main包,则定义main函数
  13. func main() {
  14. var a int
  15. Func1()
  16. // ...
  17. fmt.Println(a)
  18. }
  19. //定义其余函数,首先是类型方法,然后按照main函数中的先后调用顺序来定义相关函数,或者字母序
  20. func (t T) Method1() {
  21. //...
  22. }
  23. func Func1() { // exported function Func1
  24. //...
  25. }

类型转换


Go语言不存在隐式类型转换,所有的转换都必须显示说明,就像调用一个函数一样

  1. valueOfTypeB = typeB(valueOfTypeA)
  2. //这种方法只能在定义正确的时候转换成功,否则会发生精度丢失或者编译错误
  3. a:=int(b)

Go的命名规范


可以使用gofmt来强制实现统一的代码风格,值得学习借鉴