Skip to content

GO 语言的基础组成分别为:包声明、引入包、函数、变量、语句 & 表达式、注释

案例

go
package main

import "fmt"

func main() {
	fmt.Println("hello world")
}

以上为简单GO程序。GO 是编译型语言,需要执行命令运行

bash
# 运行
go run <文件路>

# 编译(会把语言编译为可直接运行的二进制文件)
go build <文件路>

注意:在 GO 语言中 { 是不能单独放到一行中,会产生语法错误。错误案例:

go
func main() 
{
	fmt.Println("hello world")
}

主函数

main() 函数是程序的“入口函数”,整个程序只有一个。除此之外还有 init() 函数,他的执行时机是在 main() 函数之前的,一个包可以有多个 init() 函数

go
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 中,一行则代表着一个语句的结束,如:

go
func main() {
	println("我是第一个行")
	println("我是第二个行")
}

如果是想要把多个语句写在同一行,则需要使用 ; 分割。如:

go
func main() {
	println("我是第一个行"); println("我是第二个行")
}

注释

单行注释(以 // 开头):

go
// 我是单行注释

多行注释(以 /* 开头,以 */ 结尾):

go
/*
我是
多行注释
*/

标识符

标识符用来命令变量和类型等程序实体。通常是由字母(A ~ Z | a ~ z)、数字(0 ~ 9)、下划线(_)组成的一个或多个字符组成的序列。

注意:对于以数字为开头或者 GO 语言关键字为标识符的为无效标识符

判断

在 GO 语言中没有“三目运算符([bool] ? [true] : [false])”

if

go
if [条件] {
    [逻辑]
} else if [条件二] {
    [逻辑二]
} else {
    [逻辑三]
}

从上到下依次匹配,匹配到之后进入逻辑块,执行完成后也不再进行后续逻辑判断

switch

go
switch [条件] {
    case [匹配条件一]:
        [逻辑一]
        fallthrough
    case [匹配条件二]:
        [逻辑二]
    case [匹配条件三], [匹配条件四]:
        [逻辑三,四]
    default:
        [默认逻辑]
}

switch 的执行过程是从上到下,直至找到匹配项。它可以在一个 case 中声明多个匹配项,只需要使用逗号分隔即可

跟 Java 不同的是,case 的逻辑块后面不需要跟 break,它仅会执行匹配上的 case 的逻辑块,不会顺序向下执行

如果需要在执行完某一个 case 的逻辑块后继续向下执行则只需要在 case 的逻辑块末尾的位置加入 fallthrough

循环

GO 语言只有 for 循环这一种语法,但是可以通过特定的方式来模拟例如 while 等循环;在循环过程中可以使用如下关键字来控制循环

关键字描述
break跳出这个循环
continue跳过本次循环
goto跳转语句

普通循环

go
for i := 0; i < 5; i++ {
    println("普通循环:", i)
}

替 while

go
i := 0
for i < 5 {
    println("while 循环:", i)
    i++
}

无限循环

go
i := 0
// 什么都不用写或者写一个 true 都行
for {
    println("无限循环:", i)
    i++
    if i >= 5 {
        break
    }
}

for-range

go 语言独有的为了遍历数组、切片、字符串、map、通道设计的专用写法

go
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、数组、字符串等进行迭代循环,格式:

go
for key, value := range 遍历数据 {
    // 循环体
}

GO 规定了变量声明后必须使用。对于不想使用但是有必须写变量的可以使用下划线占位

for _, v := range 遍历数据 {
    println(v)
}

下划线占位不仅限于循环语法

跳转语句

在正常的编程语句执行的时候是从上到下一行一行顺序执行的,而跳转语句就是打破了这种顺序执行的特殊语句。它能让程序直接跳到指定的位置开始继续执行(可以是之前也可以是之后)

语法

go
// 定义标签:标签名 + 冒号(标签名需符合标识符规则,且在当前函数内唯一)
标签名:
    代码块

// 跳转到标签处
goto 标签名

GO 语言为了保证代码的可维护性对 goto 进行了专门的限制:

  1. 只能在同一个函数内跳转:不能跨函数、跨作用域(如从主函数跳到子函数的标签)
  2. 不能跳转到变量声明语句之后:避免变量未声明就被使用
  3. 不能跳转到循环 /switch 内部:只能在同一层级跳转,避免破坏流程结构

一般建议使用在 统一资源释放或错误处理,例:

go
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
}

函数

把重复的操作或者功能抽取出来封装成为一个函数(函数声明告诉了编译器函数的名称、返回类型和参数),然后再重复的地方调用这个函数即可

定义方式

go
func 函数名(参数列表) (返回值列表) {
    // 函数体:要执行的逻辑
    return 返回值 // 无返回值则省略
}
  1. func:定义函数的关键字
  2. 参数列表:参数名 类型(多个参数用逗号分隔)
  3. 返回值列表:类型(单个返回值)或返回值名 + 类型(多个返回值)
  4. 函数名:符合标识符规则,首字母大写的话能够被别的包调用反之小写的话则只能被本包调用

多返回值

go
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 的时候就不需要进行命名了

go
func fun(b bool) (result bool, message string) {
	result = b
	message = "success"
	if b {
		message = "success"
	} else {
		message = "error"
	}
	return
}

可变参数

顾名思义就是参数的数量不固定,使用 形参 ...类型 进行定义可变参(可变参数必须是形参列表的最后一个),函数内会当成切片处理

go
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
}

闭包

go
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 语言中函数是可以作为另一个函数的参数来传递的

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
}

匿名函数

无函数名,临时使用

go
func main() {
	// 定义并立即调用匿名函数
	func(msg string) {
		println(msg)
	}("我是匿名函数!")

	// 匿名函数赋值给变量,后续调用
	add := func(a, b int) int {
		return a + b
	}
	println(add(3, 4))
}