Go程序基础(1)
Go程序基础
程序
写一个可以输出 hello world 的程序。
package main
import "fmt"
func main() {
fmt.Printf("Hello, world. \n")
}
来讨论下程序本身。Go语言的代码通过包(package
)组织,包类似于其它语言里的库(libraries
)或者模块(modules
)。
一个包由位于单个目录下的一个或多个.go
源代码文件组成, 目录定义包的作用。
每个源文件都以一条 package
声明语句开始,这个例子里就是 package main
, 表示该文件属于哪个包,紧跟着一系列导入( import
)的包,之后是存储在这个文件里的程序语句。
Go的标准库提供了100多个包,以支持常见功能,如输入、输出、排序以及文本处理。比如 fmt
包,就含有格式化输出、接收输入的函数。 Println
是其中一个基础函数,可以打印以空格间隔的一个或多个值,并在最后添加一个换行符,从而输出一整行。
main
包比较特殊。它定义了一个独立可执行的程序,而不是一个库。在 main
里的 main
函数 也很特
殊,它是整个程序执行时的入口。
main
函数所做的事情就是程序做的。当然了, main
函数一般调用其它包里的函数完成很多工作, 比如fmt.Println
。必须告诉编译器源文件需要哪些包,这就是 import
声明以及随后的 package
声明扮演的角色。
包的概念
每个目录下面可以有多个.go
文件, 这些文件只能属于同一个包, 否则编译会报错。同一个包下的不 .go
文件相互之间可以直接引用变量和函数,所以在这些文件中定义的全局变量和函数不能重名。
go语言的可执行程序必须有main
包,而且在main
包中必须且只能有一个main()
函数,main()
函数是应用程序的入口。在main
包中也可以使用init()
函数。
go语言不强制要求包的名称和文件所在目录名称相同,但两者最好保持一致,否则很容易引起歧义。
因为导入包的时候,会使用文件名作为包的路径,而代码中使用时,却要用package
定义的包名称。
包的初始化
可执行应用程序的初始化和执行都起始于main
包。如果main
包的源代码中没有包含main()
函数,则会引发构建错误undefined:main.main
。
main()
函数即没有参数,也没有返回类型,init()
函数和main()
函数在这一点上一样。
如果main
包还导入了其他的包,那么在编译是会一次导入。如果一个包被多个包同时导入,则只会导入一次(例如很多包都导入了fmt
包,但它只会被导入一次,因为没有必要导入多次)。
当某个包被导入时,如果该包还导入了其他的包,那么会先将其他的包导入进来,在对这些包中的包级常量和变量进行初始化,接着执行init()
函数(如果有的话),以此类推。
当所有被导入的包都加载完毕后,就会对main
包中的包级变量和常量进行初始化,然后执行main
包中的init()
函数,最后执行main()
函数。
init 函数详解
go语言中init()
函数常用于包的初始化,该函数是go语言的一个重要特性,有下面的特征:
init
函数用于程序执行前进行包的初始化函数,例如初始化包里的变量等。- 每个包可以拥有多个
init
函数。 - 包的每个源文件也可以拥有多个
init
函数。 - 同一个包的多个
init
函数的执行顺序不定。 - 不同包的
init
函数按照包导入的依赖关系决定该函数的执行顺序。 init
函数不能被其他函数调用,在main
函数执行前自动调用。
init()
执行时机
在Go中初始化包后,会自动执行init()函数,并且优先级是比主函数执行优先级高。每个包可以包含任意多个 init 函数,这些函数都会在程序执行开始的时候被调用。
所有被编译器发现的 init 函数都会安排在 main 函数之前执行。 init
函数用在设置包、初始化变量或其他要在程序运行前优先完成的引导工作。
Go 基础-变量
变量定义
// var 是关键字, 用来定义变量, type 说明变量类型, Value表示具体值
var variableName type = value
// 定义三个类型都是 “type” 的变量
var vname1, vname2, vname3 type
// 同时初始化多个变量
var vname1, vname2, vname3 type= v1, v2, v3
// 定义三个变量,它们分别初始化为相应的值。 编译器会根据初始化的值自动推导出相应的类型
vname1, vname2, vname3 := v1, v2, v3
Go
对于已声明但未使用的变量会在编译阶段报错,比如下面的代码就会产生一个错误。
常量
所谓常量,也就是在程序编译阶段就确定下来的值,而程序在运行时无法改变该值。
// 常量定义方式
const Pi float32 = 3.1415926
内置基础类型
Boolean
布尔值的类型为 bool
,值是 true
或 false
,默认为 false
。
数值类型
整数类型有无符号和带符号两种。Go 同时支持 int
和 uint
,这两种类型的长度相同,但具体长度取决于不同编译器的实现。
Go
里面也有直接定义好位数的类型:rune
, int8
, int16
, int32
, int64
和 byte
, uint8
, uint16
, uint32
, uint64
。
其中 rune
是 int32
的别称,byte
是 uint8
的别称。
浮点数的类型有 float32
和 float64
两种(没有 float
类型),默认是 float64
。
字符串
我们在上一节中讲过,Go
中的字符串都是采用 UTF-8
字符集编码。字符串是用一对双引号(""
) 或 反引号(` `)括起来定义,它的类型是 string
。
在 Go 中字符串是不可变的, 例如下面的操作:
func main() {
var str string = "hello"
str[0] = 'c'
}
// 会出现 Cannot assign to str[0] 的错误。
如果真的需要修改,可以将 将字符串 s
转换为 []byte
类型, 再将其转换回 string
类型。
Go
中可以使用 +
操作符来连接两个字符串。
如果要声明一个多行的字符串,可以通过反引号形式来定义。` 括起的字符串为 Raw
字符串,即字符串在代码中的形式就是打印时的形式,它没有字符转义,换行也将原样输出。
错误类型
Go
内置有一个 error
类型,专门用来处理错误信息,Go
的 package
里面还专门有一个包 errors
来处理错误:
err := errors.New("custom errors.")
if err != nil {
fmt.Print(err)
}
iota 枚举
Go
里面有一个关键字 iota
,这个关键字用来声明 enum
的时候采用,它默认开始值是 0
,const
中每增加一行加 1
array、slice、map
三者的区别与联系,后边一个章节我们单独介绍。
零值
关于 “零值”,所指并非是空值,而是一种 “变量未填充前” 的默认值,通常为 0。
Go 程序设计的一些规则
Go 之所以会那么简洁,是因为它有一些默认的行为:
-
大写字母开头的变量是可导出的,也就是其它包可以读取的,是公有变量;
-
小写字母开头的就是不可导出的,是私有变量。
-
大写字母开头的函数也是一样,相当于
class
中的带public
关键词的公有函数; -
小写字母开头的就是有
private
关键词的私有函数。