Go语言学习笔记(四)——函数、方法、接口与并发编程入门
Go 的强大之处不仅在于语法简洁,更在于它把很多实用能力设计得非常自然。函数、方法、接口和并发,是 Go 从“会写”走向“会用”的关键阶段。
一、函数 Function
函数是 Go 代码组织的基本单位。
1. 函数定义
func add(a int, b int) int {
return a + b
}
调用:
result := add(3, 5)
fmt.Println(result)
2. 参数类型简写
如果多个参数类型相同,可以简写:
func add(a, b int) int {
return a + b
}
3. 多返回值
Go 非常有特色的一点就是支持多返回值。
func calc(a, b int) (int, int) {
return a + b, a - b
}
调用:
sum, diff := calc(10, 3)
fmt.Println(sum, diff)
4. 命名返回值
func getInfo() (name string, age int) {
name = "Tom"
age = 20
return
}
虽然命名返回值可以用,但项目中通常不建议滥用,否则会降低代码可读性。
二、错误处理
Go 没有 Java 那种异常机制作为主要错误处理方式,而是强调显式返回 error。
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为0")
}
return a / b, nil
}
调用:
result, err := divide(10, 0)
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Println(result)
这种写法虽然看起来比 try-catch 更“啰嗦”,但优点是逻辑明确,错误路径清晰。
三、方法 Method
Go 没有传统面向对象里的类,但可以给结构体绑定方法。
1. 定义方法
type Person struct {
Name string
Age int
}
func (p Person) SayHello() {
fmt.Println("你好,我是", p.Name)
}
调用:
p := Person{Name: "小明", Age: 20}
p.SayHello()
2. 值接收者与指针接收者
func (p *Person) GrowUp() {
p.Age++
}
如果方法需要修改结构体内容,通常使用指针接收者。
四、接口 Interface
接口是 Go 中非常重要的抽象能力。接口定义的是行为,而不是具体实现。
1. 定义接口
type Speaker interface {
Speak()
}
2. 实现接口
type Dog struct {}
func (d Dog) Speak() {
fmt.Println("汪汪汪")
}
3. 使用接口
func makeSpeak(s Speaker) {
s.Speak()
}
func main() {
d := Dog{}
makeSpeak(d)
}
在 Go 中,一个类型只要实现了接口中的所有方法,就算实现了该接口,不需要显式声明。这种设计非常灵活。
五、并发编程:goroutine
并发是 Go 的最大亮点之一。
1. 启动 goroutine
package main
import (
"fmt"
"time"
)
func task() {
fmt.Println("goroutine 执行中")
}
func main() {
go task()
time.Sleep(time.Second)
}
go task() 表示开启一个新的 goroutine 并发执行任务。
goroutine 非常轻量,可以很方便地创建大量并发任务。
六、channel 通道
如果 goroutine 之间需要通信,可以使用 channel。
1. 创建通道
ch := make(chan int)
2. 发送和接收数据
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 100
}()
num := <-ch
fmt.Println(num)
}
这里子 goroutine 向通道发送数据,主 goroutine 从通道接收数据。
3. 带缓冲通道
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
带缓冲通道允许在缓冲区未满时直接写入,不会立刻阻塞。
七、select 语句
select 用于同时监听多个 channel。
select {
case msg1 := <-ch1:
fmt.Println("收到:", msg1)
case msg2 := <-ch2:
fmt.Println("收到:", msg2)
default:
fmt.Println("暂无数据")
}
它非常适合做超时控制、多路通信处理等。
八、示例:并发打印数字
package main
import (
"fmt"
"time"
)
func printNums() {
for i := 1; i <= 5; i++ {
fmt.Println("子协程:", i)
time.Sleep(200 * time.Millisecond)
}
}
func main() {
go printNums()
for i := 1; i <= 5; i++ {
fmt.Println("主协程:", i)
time.Sleep(200 * time.Millisecond)
}
}
运行后可以看到主协程和子协程交替输出,这就是并发执行的表现。
九、学习并发时要注意的问题
初学 Go 并发时,常见问题有:
一是主协程退出太快,导致子 goroutine 来不及执行。
二是通道读写不匹配,容易造成死锁。
三是多个 goroutine 同时操作共享资源时,可能产生竞态问题。
四是看到并发就乱开 goroutine,结果逻辑难以维护。
所以学习并发不能只会写 go 关键字,更重要的是理解协程调度、通道通信和同步控制。
十、总结
函数让代码可复用,方法让结构体具备行为,接口让程序具有更好的扩展性,而 goroutine 和 channel 则让 Go 在并发场景中表现出色。
Go 的很多设计理念都很务实:不用过度复杂的语法,却能把实际开发中最需要的能力做得很顺手。这也是 Go 受到大量后端开发者青睐的重要原因。

浙公网安备 33010602011771号