包的概念、导入与可见性
概念
包是结构化代码的一种方式:每个程序都由包的概念组成,可以使用自身的包,或者从其他包中导入内容
类似某些编程语言中的类库或者命名空间的概念,每个文件都属于且仅属于一个包。一个包可以由许多以 .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
的对象文件中提取传递依赖类型的信息
以下是编译实例
若a.go依赖b.go,而b.go依赖于c.go,则会
- 先编译c.go,b.go,最后是a.go
- 为了编译a.go,编译器将读取b.o(没错就是b.o)
这种编译机制可以显著提升大型项目的编译速度
导入包
概念
导入包相当于包含了这个包所有的代码对象
除了 _
以外,所有的代码对象的标识符必须是唯一的,以避免命名冲突。但是相同的标识符可以在不同的包中使用,因为可以使用包名进行区分
如何导入
Go程序通过 import
关键字将包链接在一起,如 import "fmt"
表示导入 fmt
包(该包实现了格式化的IO)。导入多个包,可以有如下几种方法
- 分别导入 ```go import “fmt” import “os”
//或者 import “fmt”;import “os”
- 优雅的因式分解关键字(同样适用于const、var和type的声明或者定义)
```go
import(
"fmt"
"os"
)
//或者
import(
"fmt";"os"
)
查找包
如果包名不是以 .
或者 /
开头,则Go会在全局文件进行查找;以 ./
开头,则会在相对目录中进行查找;以 /
(Windows中也可以这么用)开头,则会在系统的绝对路径中查找
可见性规则(有意思)
- 当标识符以一个 大写字母 开头的时候,如
Group
,这种形式的标识符就可以被外部包的代码所使用,这被称为导出(类似public),在外部包可以使用PackageName.Group
进行访问 - 以小写字母开头的,则对外界是不可见,但在整个包的内部是可见的(类似private)
大写字母可以使用任何unicode编码的字符,比如希腊文。。。
可以给包添加别名: import fm "fmt"
,就可以通过fm使用fmt包了
包的分级声明和初始化
可以在使用import导入包之后,定义/声明若干const、var、type等,这些对象的作用域都是全局的(本包范围内)
注意事项
若导入了包却不使用,则会导致错误,因为Go所遵循的格言是:没有不必要的代码
函数
函数的格式
整体格式
func functionName(parameter_list) (return_value_list){
...
}
- parameter_list的形式为
(param1 type1, param2 type2...)
- return_value_list的形式
(ret1 type1, ret2 type2...)
- 函数也可以返回函数哦
- 语法格式的注意事项
- 关于大括号
- 这是所有结构体都需要注意的
{
需要放在声明的行尾,}
需要放在紧跟函数体的后一行- 因为,Go语言不需要
;
号标记语句的结束,但其实是由编译器完成的,所以不这么使用{}
则会引发错误
- 关于命名
- 只有在某个函数需要被外部调用的时候,才使用大写字母开头,并遵循Pascal命名法(大写驼峰)。否则使用驼峰命名法
- 关于大括号
- 关于使用预定义的方法
- 如print、println等等,在单纯地打印一个字符串或者变量的时候,可以使用。不过在部署的时候,请替换成
fmt.
的方法
- 如print、println等等,在单纯地打印一个字符串或者变量的时候,可以使用。不过在部署的时候,请替换成
程序正常退出
main函数
注释的形式
有两种
- 单行注释
//
-
注释的使用
值得注意的是,go提供了godoc工具,会收集注释并产生技术文档,所以在使用注释的时候有如下建议
每个包都应该有相关注释
- 几乎所有的全局作用域的,类型、常量、变量、函数和被到处的对象都应有注释
- 若注释出现在函数之前,则应以函数名开头
两个例子
// Package superman implements methods for saving the world.
//
// Experience has shown that a small number of procedures can prove
// helpful when attempting to save the world.
package superman
// enterOrbit causes Superman to fly into low Earth orbit, a position
// that presents several possibilities for planet salvation.
func enterOrbit() error {
...
}
类型
概念
- 类型定义了某个变量的值的集合与其操作的集合
-
分类
基本类型
- int
- float
- bool
- string
结构化的
结构化的类型没有真正的值,所以其零值为nil
- struct
- array
- slice
- map
- channel
- 描述类型的行为的
- interface
- 函数也可以是一个确定的类型
- 即作为返回类型的时候,这种类型的声明要写在函数名和可选的参数列表之后
- 定义类型的别名
- 形式
- type IZ int
- 因式分解形式
- 形式
type(
IZ int
FZ float64
STR string
)
package main
import (
"fmt"
)
//在完成import之后,开始对常量、变量和类型进行定义、声明
const c = "C"
var v int = 5
type T struct{}
//如果存在init函数,则对该函数进行定义
func init() { // initialization of package
}
//如果是main包,则定义main函数
func main() {
var a int
Func1()
// ...
fmt.Println(a)
}
//定义其余函数,首先是类型方法,然后按照main函数中的先后调用顺序来定义相关函数,或者字母序
func (t T) Method1() {
//...
}
func Func1() { // exported function Func1
//...
}
类型转换
Go语言不存在隐式类型转换,所有的转换都必须显示说明,就像调用一个函数一样
valueOfTypeB = typeB(valueOfTypeA)
//这种方法只能在定义正确的时候转换成功,否则会发生精度丢失或者编译错误
a:=int(b)
Go的命名规范
可以使用gofmt来强制实现统一的代码风格,值得学习借鉴