变量
Go语言变量源于数学中变量的概念。变量表示一块内存区域,用来存放数据。变量有一个名字,称作变量名。通过变量名可以访问变量,读写变量的内存数据。
举例:
var a = 1 // 变量a,存放数字1
var b = 2 // 变量b,存放数字2
var c = "hello" // 变量c,存放字符串"hello"
数据类型
Go语言的变量中存放的数据是有类型的。数据类型是对变量中存放的数据的某种解释方式,即程序把内存中的数据看做是什么样的值。
Go语言的数据类型可分为:基本数据类型和派生数据类型。
常见的基本数据类型有:布尔型(bool)、数值型(int,int32,int64,uint,uint32,uint64,…)、字符串型(string)。
函数
函数代表一系列先后执行的操作。函数有一个名字,称作函数名。通过函数名可以调用函数,执行其所代表的操作。
举例:
package main
import "fmt"
// 函数sumOfSquares返回两个数的平方和
func sumOfSquares(a int, b int) int {
var c = a*a + b*b
return c
}
func main() {
var sum int
sum = sumOfSquares(3, 5) // 调用函数sumOfSquares
fmt.Printf("sum = %d\n", sum)
sum = sumOfSquares(5, 6)
fmt.Printf("sum = %d\n", sum) // 调用函数sumOfSquares
}
注释
/*
这是多行注释。
这是它的第二行。
这是它的第三行。
*/
// 这是单行注释
包
包由一个或多个go源文件聚合而成。这些go源文件存放在同一目录下,这个目录称作包目录。
包是对这些go源文件中声明的数据类型、变量、函数等的封装。
可以设置包中的一些名称是导出的,以便外部包使用;非导出的名称,仅可在包内部使用。
包给包中的代码提供了独立的命名空间,不同包中的相同名称不会冲突。
模块
模块由一个或多个包聚合而成。这些包的包目录放在同一顶层目录下,这个顶层目录称作模块目录。
Go语言是以模块为单位定义编译时需要的上下文的,包括:使用的Go版本、依赖的其他模块以及版本。
// 当前程序的包声明,说明当前程序所属的包
package main
// 导入其他包
import "fmt"
// 常量定义
const PI = 3.14
// 全局变量的声明和赋值
var name = "gopher"
// 一般类型声明
type newType int
// 结构的声明
type gopher struct{}
// 接口的声明
type golang interface{}
// 函数的声明
func circleArea(r float32) float32 {
return PI*r*r;
}
// 由main函数作为程序入口点启动
func main() {
var text = "Hello, World!"
fmt.Println(text)
var area = circleArea(5)
fmt.Println(area)
}
Go项目的文件组织结构一般如下:
+ 项目目录
+ 模块1目录 (可以含有go文件,此时也作为一个包目录)
+ uvw.go
+ xyz.go
+ 包a目录
+ a.go
+ 包b目录
+ b.go
+ b2.go
+ 包c目录
+ c.go
+ 子包d目录
+ d.go
+ 模块2目录
+ ...
Go项目的组织结构:
创建一个项目目录prj1.my.com,在其下再创建一个模块目录greetings:
$ mkdir prj1.my.com
$ cd prj1.my.com
$ mkdir greetings
$ cd greetings
使用go mod init初始化greetings模块:
$ go mod init prj1.my.com/greetings
go: creating new go.mod: module prj1.my.com/greetings
上面的go mod init
命令指定了prj1.my.com/greetings作为模块路径。当模块发布以后,别人的项目将通过这个URL来下载模块。
go mod init
命令会在当前目录生成一个go.mod文件。如果一个目录中有go.mod文件,Go就把该目录当作是一个模块目录。新生成的go.mod文件中仅定义了模块路径和使用的Go版本,如下:
module prj1.my.com/greetings
go 1.13
以后,当你需要在你的模块中使用其他模块时,这个文件中还会增加对其他模块的依赖,并且可以指定具体依赖的版本。
在greetings目录下新建代码文件greetings.go:
package greetings
import "fmt"
// Hello returns a greeting for the named person.
func Hello(name string) string {
// Return a greeting that embeds the name in a message.
message := fmt.Sprintf("Hi, %v. Welcome!", name)
return message
}
回到prj1.my.com目录,在其下创建另一个模块目录hello:
cd ..
mkdir hello
cd hello
在hello目录下新建代码文件hello.go:
package main
import (
"fmt"
"prj1.my.com/greetings"
)
func main() {
// Get a greeting message and print it.
message := greetings.Hello("Gladys")
fmt.Println(message)
}
把hello目录初始化为模块:
$ go mod init hello
go: creating new go.mod: module hello
设置hello模块使用本地的greetings模块:
如果greetings模块是作为一个产品发布的话,通常会有一个URL,该URL可以是互联网的地址,也可以是公司内部服务器的地址,Go会自动从该URL下载模块代码。但是现在,我们的greetings项目并不是作为产品发布的,而是在本地文件系统中。因此,需要在hello模块中将URL替换为本地路径。
在hello项目的go.mod中添加replace指令,
module hello
go 1.13
replace prj1.my.com/greetings => ../greetings
replace指令告诉Go,查找模块时候使用后面本地路径替换前面的模块路径。
在hello目录执行go build。go build编译代码的时候检测到代码中依赖了"prj1.my.com/greetings"包,然后定位到本地的greetings模块,并把它作为依赖项添加到go.mod中。
执行完go build之后,go.mod变为这样:
module hello
go 1.13
replace prj1.my.com/greetings => ../greetings
require prj1.my.com/greetings v0.0.0-00010101000000-000000000000
将来如果greetings作为产品发布了,想让hello模块切换为使用已发布的greetings,只需要移除hello模块的go.mod文件中的replace指令,并且修改require指令使其指定一个具体的模块版本号。
module hello
go 1.13
require prj1.my.com/greetings v1.1.0
在hello目录执行刚刚生成的二进制文件
$ ./hello
Hi, Gladys. Welcome!
思考
下面的代码执行后输出的内容:
package main
import "fmt"
var g = globalVar()
func main() {
fmt.Println("main")
fmt.Printf("g = %d\n", g)
}
func init() {
fmt.Println("init")
}
func globalVar() int {
fmt.Println("globalVar")
return 3
}
实测结果:
globalVar
init
main
g = 3
从以上结果可以得出:
init()函数:
在Go语言中,init()函数是一个特殊的函数。每个go源文件中都可以定义一个init()函数,当然,go源文件中也可以不定义init()函数。
init()函数不需要显式调用,它会在包初始化的时候被自动调用。
如果某个包中的多个go源文件都定义了init()函数,并不会导致命名冲突,但这些init()函数的执行先后顺序是不确定的。
单个包的初始化过程:
(当在代码中import一个包时,会执行这个包的初始化。无论一个包在其他地方被import了多少次,都只会初始化一次。)
如果一个包导入了其他包,被导入的包又导入了其他包,层层导入。那么按照依赖优先原则,最里层被导入的包要首先进行初始化,然后返回外层包,层层向外返回。每单个包的初始化都遵循上面的过程。
main包的初始化:
(main包的初始化也遵照上面的过程)
小练习,测试Go程序的初始化过程:
创建一个目录seeinit,进入该目录,执行go mod init初始化模块:
$ mkdir seeinit
$ cd seeinit
$ go mod init myexample.com/seeinit
go: creating new go.mod: module myexample.com/seeinit
在seeinit目录下创建子目录apple,该目录用来存放包apple的源码,在apple目录下创建源码文件apple.go、apple2.go。
apple.go:
package apple
import "fmt"
var Count = valueOfCount()
func valueOfCount() int {
fmt.Println("apple.valueOfCount()")
return 1
}
func init() {
fmt.Println("apple.init() 111")
}
apple2.go:
package apple
import "fmt"
var count2 = valueOfCount2()
func valueOfCount2() int {
fmt.Println("apple.valueOfCount2()")
return 2
}
func init() {
fmt.Println("apple.init() 222")
}
在seeinit目录下创建子目录orange,该子目录用来存放包orange的源码。在orange目录下创建源码文件orange.go、orange2.go。
orange.go:
package orange
import "fmt"
var Count = valueOfCount()
func valueOfCount() int {
fmt.Println("orange.valueOfCount()")
return 2
}
func init() {
fmt.Println("orange.init() 111")
}
orange2.go:
package orange
import "fmt"
var count2 = valueOfCount2()
func valueOfCount2() int {
fmt.Println("orange.valueOfCount2()")
return 3
}
func init() {
fmt.Println("orange.init() 222")
}
在seeinit目录下创建子目录banana,该子目录下存放包banana的代码,在banana目录下创建源码文件banana.go:
package banana
import "fmt"
var Count = valueOfCount()
func valueOfCount() int {
fmt.Println("banana.valueOfCount")
return 5
}
func init() {
fmt.Println("banana init")
}
在seeinit目录下创建main包文件seeit.go、seeinit2.go:
seeinit.go:
package main
import (
"fmt"
"myexample.com/seeinit/apple"
"myexample.com/seeinit/orange"
)
var base = valueOfBase()
func valueOfBase() int {
fmt.Println("main.valueOfBase()")
return 5
}
var sum int
func init() {
fmt.Println("main.init() 111")
sum = base + apple.Count + orange.Count
}
func main() {
fmt.Println("main()")
fmt.Printf("sum = %d\n", sum)
}
seeinit2.go:
package main
import (
"fmt"
_ "myexample.com/seeinit/banana"
)
var base2 = valueOfBase2()
func valueOfBase2() int {
fmt.Println("main.valueOfBase2()")
return 0
}
func init() {
fmt.Println("main.init() 222")
}
以上程序编写完毕后,在seeinit目录执行go run seeit.go
,将看到如下结果:
从结果中可以印证Go程序的初始化顺序。
$ go run seeit.go
apple.valueOfCount()
apple.valueOfCount2()
apple.init() 111
apple.init() 222
orange.valueOfCount()
orange.valueOfCount2()
orange.init() 111
orange.init() 222
banana.valueOfCount
banana init
main.valueOfBase()
main.valueOfBase2()
main.init() 111
main.init() 222
main()
sum = 8