上期介绍了如下内容:
- Go的前世今生
- Go的特性
- Windows环境变量配置
万物之始:Hello World
//文件名:helloworld.go
package main
import "fmt"
func main() {
fmt.Println("HELLO, WORLD!")
}
复制代码
使用命令行操作.go文件
- 运行Go文件
命令:go run helloworld.go
输出:HELLO, WORLD!
- 编译成exe程序
命令:go build helloworld.go
会创建一个helloworld.exe文件,可以在命令行直接运行这个程序,输出同上。
Go的包概念
Go使用包(package)来组织代码。一个包由一个或多个Go文件组成,这个包存放在一个文件夹中,这个文件的名字描述了包的作用,一般来说包名和文件夹名相同。
每一个源文件的开始都会用package声明这个Go文件属于哪个包。
当我们看到fmt.println('Hello')
这样的代码时,会想当然的认为源文件开头的import fmt
中的fmt就是包名,但是事实并非如此。
import后面的最后一个元素是路径,也就是文件夹目录,并非包名
以Go提供的fmt包为例。
目录名和包名相同只是Go的一个惯例,并非永远如此。
import "fmt"
fmt.Println("xxx")
复制代码
这里虽然两行都是fmt
,但含义不同,一个是路径,即$GOROOT/src.fmt
这个路径。而第二行中的fmt
则是包名。gc会在$GOROOT/src/fmt
路径下找到fmt
包的源文件。
可以给包起别名
import m "fmt"
func main() {
m.Pringln("Hello")
}
复制代码
使用一个容易辨识的单词作为别名,可以用这个别名来调用包中函数。
上面说过"fmt"
是路径,那么用m
作为别名,m
是路径还是包名呢?
m
其实是包名,因为下方可以使用m
来调用函数
用点作为包的别名
import . "fmt"
func main() {
Pringln("Hello")
}
复制代码
使用.
作为包的别名后,调用该包中的函数不需要使用前缀
用_
导入包
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
复制代码
按照网上的说法,使用_
导包,会调用包中的init()
函数,让导入的包做初始化,但是却不用包中其他的功能。这段话让人费解,既然不用为何要导入做初始化呢?要理解这个问题首先要了解Go的import原理。
- 递归导包
当一个包被导入时,如果该包还导入了其它的包,那么会将其它的包先导进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init()
函数,以此类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init()
函数,最后执行main函数。
- 多处引用,一次导入
程序的初始化和执行都起始于main包,如果main包还导入了其它包,那么就会在编译时将它们一次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(比如很多包都会导入"fmt"
包,但它只会被导入一次,因为没有必要导入多次)
- 使用
_
导包的意义
第一,用_
导如包,会执行该包中的init()
函数,并初始化里面的变量。往往这些init()
函数中是注册自己包里面的引擎(比如连接池注册等等)。这样导入一次后,别的包就不用再次初始这个包了,方便外部调用。
第二,Go的语法检查是非常严格的,如果导入一个包却不使用,编译时就会报错。使用_
导包,不使用包中任何函数,是可以编译通过的。要注意的是用_
导入的包是无法用它调用函数的
import _ "fmt"
//会报错
func main() {
_.Pringln("Hello")
}
复制代码
Go和Java包含义的区别
- 颗粒度不同
Java导包最小粒度是class
// 1.引用一个具体的类
import java.util.list;
// 2. 引用package下的所有类
import com.lark.demo.*
复制代码
Go导包最小粒度是package
//只能导入一个package,不能导入一个Struct(类)
import "fmt"
复制代码
- package含义不同
Java的package只是class的目录,package本身不能定义方法和变量
Go的package是一组相关变量、方法、结构的集合。可以直接在package上定义变量和方法。