GO 语言的基础组成分别为:包声明、引入包、函数、变量、语句 & 表达式、注释
案例
package main
import "fmt"
func main() {
fmt.Println("hello world")
}以上为简单GO程序。GO 是编译型语言,需要执行命令运行
# 运行
go run <文件路径>
# 编译(会把语言编译为可直接运行的二进制文件)
go build <文件路径>注意:在 GO 语言中
{是不能单独放到一行中,会产生语法错误。错误案例:gofunc main() { fmt.Println("hello world") }
主函数
main() 函数是程序的“入口函数”,整个程序只有一个。除此之外还有 init() 函数,他的执行时机是在 main() 函数之前的,一个包可以有多个 init() 函数
package main
func init() {
println("init 函数被调用")
}
func main() {
println("main 函数被调用")
}执行规则
包级变量初始化:程序启动时,首先初始化所有被导入包的包级变量(全局变量)
init 函数执行:包级变量初始化完成后,执行该包的 init 函数(一个包可以有多个 init 函数,按声明顺序执行)
依赖包优先:如果包 A 导入了包 B,会先完成包 B 的变量初始化和 init 函数执行,再处理包 A
main 函数执行:所有依赖包的初始化和 init 函数执行完毕后,才会执行 main 包的 init 函数,最后执行 main 函数
特殊点:
init函数不能被手动调用,也没有参数和返回值,程序自动触发执行
分隔符
在 GO 中,一行则代表着一个语句的结束,如:
func main() {
println("我是第一个行")
println("我是第二个行")
}如果是想要把多个语句写在同一行,则需要使用 ; 分割。如:
func main() {
println("我是第一个行"); println("我是第二个行")
}注释
单行注释(以 // 开头):
// 我是单行注释多行注释(以 /* 开头,以 */ 结尾):
/*
我是
多行注释
*/标识符
标识符用来命令变量和类型等程序实体。通常是由字母(A ~ Z | a ~ z)、数字(0 ~ 9)、下划线(_)组成的一个或多个字符组成的序列。
注意:对于以数字为开头或者 GO 语言关键字为标识符的为无效标识符
判断
在 GO 语言中没有“三目运算符(
[bool] ? [true] : [false])”
if
if [条件] {
[逻辑]
} else if [条件二] {
[逻辑二]
} else {
[逻辑三]
}从上到下依次匹配,匹配到之后进入逻辑块,执行完成后也不再进行后续逻辑判断
switch
switch [条件] {
case [匹配条件一]:
[逻辑一]
fallthrough
case [匹配条件二]:
[逻辑二]
case [匹配条件三], [匹配条件四]:
[逻辑三,四]
default:
[默认逻辑]
}switch 的执行过程是从上到下,直至找到匹配项。它可以在一个 case 中声明多个匹配项,只需要使用逗号分隔即可
跟 Java 不同的是,case 的逻辑块后面不需要跟 break,它仅会执行匹配上的 case 的逻辑块,不会顺序向下执行
如果需要在执行完某一个 case 的逻辑块后继续向下执行则只需要在 case 的逻辑块末尾的位置加入 fallthrough
循环
GO 语言只有 for 循环这一种语法,但是可以通过特定的方式来模拟例如 while 等循环;在循环过程中可以使用如下关键字来控制循环
| 关键字 | 描述 |
|---|---|
break | 跳出这个循环 |
continue | 跳过本次循环 |
goto | 跳转语句 |
普通循环
for i := 0; i < 5; i++ {
println("普通循环:", i)
}替 while
i := 0
for i < 5 {
println("while 循环:", i)
i++
}无限循环
i := 0
// 什么都不用写或者写一个 true 都行
for {
println("无限循环:", i)
i++
if i >= 5 {
break
}
}for-range
go 语言独有的为了遍历数组、切片、字符串、map、通道设计的专用写法
import "fmt"
func main() {
// 遍历切片
nums := []int{10, 20, 30}
for idx, val := range nums {
fmt.Printf("索引:%d,值:%d\n", idx, val)
}
// 遍历字符串(按字符,而非字节)
str := "hello wordl"
for idx, char := range str {
fmt.Printf("索引:%d,字符:%c\n", idx, char)
}
}Range
range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引(key)和索引对应的值(Value)
for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环,格式:
for key, value := range 遍历数据 {
// 循环体
}GO 规定了变量声明后必须使用。对于不想使用但是有必须写变量的可以使用下划线占位
for _, v := range 遍历数据 {
println(v)
}下划线占位不仅限于循环语法
跳转语句
在正常的编程语句执行的时候是从上到下一行一行顺序执行的,而跳转语句就是打破了这种顺序执行的特殊语句。它能让程序直接跳到指定的位置开始继续执行(可以是之前也可以是之后)
语法
// 定义标签:标签名 + 冒号(标签名需符合标识符规则,且在当前函数内唯一)
标签名:
代码块
// 跳转到标签处
goto 标签名GO 语言为了保证代码的可维护性对
goto进行了专门的限制:
- 只能在同一个函数内跳转:不能跨函数、跨作用域(如从主函数跳到子函数的标签)
- 不能跳转到变量声明语句之后:避免变量未声明就被使用
- 不能跳转到循环 /switch 内部:只能在同一层级跳转,避免破坏流程结构
一般建议使用在 统一资源释放或错误处理,例:
func readFile(path string) error {
// 打开文件
file, err := os.Open(path)
if err != nil {
// 错误:跳转到清理逻辑(此处无资源需释放,直接返回)
goto cleanup
}
// 模拟读取操作出错
err = fmt.Errorf("读取文件失败")
if err != nil {
// 错误:跳转到清理逻辑(关闭文件)
goto cleanup
}
cleanup: // 统一的清理标签
if file != nil {
file.Close() // 确保文件被关闭
fmt.Println("文件已关闭")
}
return err
}函数
把重复的操作或者功能抽取出来封装成为一个函数(函数声明告诉了编译器函数的名称、返回类型和参数),然后再重复的地方调用这个函数即可
定义方式
func 函数名(参数列表) (返回值列表) {
// 函数体:要执行的逻辑
return 返回值 // 无返回值则省略
}func:定义函数的关键字- 参数列表:
参数名 类型(多个参数用逗号分隔) - 返回值列表:类型(单个返回值)或返回值名 + 类型(多个返回值)
- 函数名:符合标识符规则,首字母大写的话能够被别的包调用反之小写的话则只能被本包调用
多返回值
func main() {
b, s := fun(true)
println(b, s)
b, s = fun(false)
println(b, s)
}
// 返回多值
func fun(b bool) (bool, string) {
if b {
return true, "success"
} else {
return false, "error"
}
}还有一种简化写法,在返回类型中定义返回值的名称,然后在 return 的时候就不需要进行命名了
func fun(b bool) (result bool, message string) {
result = b
message = "success"
if b {
message = "success"
} else {
message = "error"
}
return
}可变参数
顾名思义就是参数的数量不固定,使用 形参 ...类型 进行定义可变参(可变参数必须是形参列表的最后一个),函数内会当成切片处理
func main() {
println(add(1, 2, 3, 4))
}
func add(params ...int) int {
count := 0
// 遍历可变参数(params是[]int类型)
for _, num := range params {
count += num
}
return count
}闭包
func main() {
f := add()
println(f())
println(f())
println(f())
}
func add() func() int {
var a int
// 闭包函数
return func() int {
a++
return a
}
}再以上的情况中变量 a 的声明周期就不再是 add 这个函数执行完就结束了,而是跟随着返回的闭包函数是否还有引用。等到闭包函数没有引用的时候 GC 才会回收
函数式编程
在 GO 语言中函数是可以作为另一个函数的参数来传递的
package main
// 定义一个函数类型(接收int,返回int)
type CalcFunc func(int) int
// 定义两个具体函数
func double(n int) int {
return n * 2
}
func square(n int) int {
return n * n
}
// 接收函数作为参数
func process(num int, f CalcFunc) int {
return f(num)
}
func main() {
// 传入double函数
println(process(5, double)) // 输出 10
// 传入square函数
println(process(5, square)) // 输出 25
}匿名函数
无函数名,临时使用
func main() {
// 定义并立即调用匿名函数
func(msg string) {
println(msg)
}("我是匿名函数!")
// 匿名函数赋值给变量,后续调用
add := func(a, b int) int {
return a + b
}
println(add(3, 4))
}