Go 语言从零开始:25 个示例
Go(Golang)是 Google 开发的语言。它语法简洁、编译快,并内置并发(concurrency)支持,因此广泛用于服务器、工具和云系统。本文有 25 个代码示例,全部使用 Go 1.27 运行,并展示每个示例的真实输出。内容从基础讲到并发、Web 处理函数和泛型。
需要准备的东西
从 go.dev/dl 安装 Go,并用 `go version` 检查。创建 `.go` 文件,用 `go run 文件名.go` 运行。
示意图
示例 1: 你好,世界
最简单的 Go 程序。每个程序都从 main 包和 main 函数开始。
package main
import "fmt"
func main() {
fmt.Println("สวัสดี Go")
}
**实际输出**
สวัสดี Go
示例 2: 变量与常量
`:=` 同时声明并赋值,`var` 明确声明,`const` 用于永不改变的值。
package main
import "fmt"
const schoolName = "โรงเรียนตัวอย่าง"
func main() {
name := "สมชาย"
var age int = 20
fmt.Println(schoolName, name, age)
}
**实际输出**
โรงเรียนตัวอย่าง สมชาย 20
示例 3: 零值
声明但未赋值的变量会得到其类型的零值,例如 0、空字符串或 false。
package main
import "fmt"
func main() {
var n int
var s string
var b bool
var f float64
fmt.Printf("int=%d string=%q bool=%t float=%.1f\n", n, s, b, f)
}
**实际输出**
int=0 string="" bool=false float=0.0
示例 4: if 与 else
使用 if 和 else if 做判断。条件外不需要括号。
package main
import "fmt"
func main() {
score := 72
if score >= 80 {
fmt.Println("A")
} else if score >= 70 {
fmt.Println("B")
} else {
fmt.Println("C")
}
}
**实际输出**
B
示例 5: for 循环
Go 只有一种循环关键字 for,既可以计数,也可以像 while 一样使用。
package main
import "fmt"
func main() {
for i := 1; i <= 3; i++ {
fmt.Println("นับ", i)
}
n := 0
for n < 2 {
n++
}
fmt.Println("n =", n)
}
**实际输出**
นับ 1
นับ 2
นับ 3
n = 2
示例 6: switch
switch 比较多个分支,除非明确要求,否则不会落入下一个 case,因此不需要 break。
package main
import "fmt"
func main() {
day := "เสาร์"
switch day {
case "เสาร์", "อาทิตย์":
fmt.Println("วันหยุด")
default:
fmt.Println("วันทำงาน")
}
}
**实际输出**
วันหยุด
示例 7: 数组与切片
数组大小固定。切片可以用 append 增长,并可用 s[a:b] 截取一段。
package main
import "fmt"
func main() {
arr := [3]int{1, 2, 3}
s := []int{10, 20}
s = append(s, 30, 40)
fmt.Println("array:", arr, "len:", len(arr))
fmt.Println("slice:", s, "len:", len(s), "cap:", cap(s) >= 4)
fmt.Println("ตัดช่วง:", s[1:3])
}
**实际输出**
array: [1 2 3] len: 3
slice: [10 20 30 40] len: 4 cap: true
ตัดช่วง: [20 30]
示例 8: 映射(map)
map 保存键值对。使用 `v, ok :=` 形式可以检查键是否存在。
package main
import "fmt"
func main() {
scores := map[string]int{"สมชาย": 85, "สมหญิง": 72}
scores["สมศักดิ์"] = 45
if v, ok := scores["สมชาย"]; ok {
fmt.Println("คะแนน:", v)
}
delete(scores, "สมหญิง")
fmt.Println("จำนวน:", len(scores))
}
**实际输出**
คะแนน: 85
จำนวน: 2
示例 9: 多返回值函数
函数可以返回多个值,通常把 error 作为最后一个返回值。
package main
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("หารด้วยศูนย์ไม่ได้")
}
return a / b, nil
}
func main() {
q, err := divide(10, 2)
fmt.Println("ผล:", q, "error:", err)
_, err = divide(1, 0)
fmt.Println("error:", err)
}
**实际输出**
ผล: 5 error: <nil>
error: หารด้วยศูนย์ไม่ได้
示例 10: 闭包
能记住外层作用域变量的函数,适合做计数器或私有状态。
package main
import "fmt"
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
next := counter()
fmt.Println(next(), next(), next())
}
**实际输出**
1 2 3
示例 11: 结构体与方法
struct 把多个数据组合在一起,方法则为该类型添加行为。
package main
import "fmt"
type Student struct {
Name string
Score int
}
func (s Student) Grade() string {
if s.Score >= 80 {
return "A"
}
return "B"
}
func main() {
s := Student{Name: "สมชาย", Score: 85}
fmt.Println(s.Name, s.Grade())
}
**实际输出**
สมชาย A
示例 12: 接口
接口定义类型必须具备的方法集合。拥有这些方法的类型会自动满足该接口。
package main
import "fmt"
type Shape interface {
Area() float64
}
type Rect struct{ W, H float64 }
func (r Rect) Area() float64 { return r.W * r.H }
func main() {
var s Shape = Rect{W: 3, H: 4}
fmt.Printf("พื้นที่ = %.1f\n", s.Area())
}
**实际输出**
พื้นที่ = 12.0
示例 13: 指针
&x 得到变量的地址,*p 读写该地址上的值,使函数能修改调用者的数据。
package main
import "fmt"
func increment(n *int) {
*n++
}
func main() {
x := 5
increment(&x)
fmt.Println("x =", x)
}
**实际输出**
x = 6
示例 14: 错误处理
用 fmt.Errorf 和 %w 创建错误,再用 errors.Is 判断它是否属于某个已知错误。
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("ไม่พบข้อมูล")
func find(id int) error {
if id != 1 {
return fmt.Errorf("รหัส %d: %w", id, ErrNotFound)
}
return nil
}
func main() {
err := find(2)
fmt.Println(err)
fmt.Println("ตรงกับ ErrNotFound:", errors.Is(err, ErrNotFound))
}
**实际输出**
รหัส 2: ไม่พบข้อมูล
ตรงกับ ErrNotFound: true
示例 15: defer
defer 会把调用推迟到函数返回时执行,适合关闭文件和释放资源。
package main
import "fmt"
func main() {
defer fmt.Println("ทำงานสุดท้าย")
fmt.Println("เริ่มต้น")
fmt.Println("ประมวลผล")
}
**实际输出**
เริ่มต้น
ประมวลผล
ทำงานสุดท้าย
示例 16: panic 与 recover
panic 会中断正常执行,recover 只能在 defer 的函数中捕获它,从而避免整个程序崩溃。
package main
import "fmt"
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("กู้คืนจาก panic: %v", r)
}
}()
return a / b, nil
}
func main() {
fmt.Println(safeDivide(10, 2))
fmt.Println(safeDivide(1, 0))
}
**实际输出**
5 <nil>
0 กู้คืนจาก panic: runtime error: integer divide by zero
示例 17: 协程(goroutine)
go 关键字启动并发执行的任务,sync.WaitGroup 等待所有任务完成。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
_ = n * n
}(i)
}
wg.Wait()
fmt.Println("ทุกงานเสร็จแล้ว")
}
**实际输出**
ทุกงานเสร็จแล้ว
示例 18: 通道(channel)
通道在协程之间传递值。本例把值从协程发送到 main,然后关闭通道。
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
for i := 1; i <= 3; i++ {
ch <- i * 10
}
close(ch)
}()
for v := range ch {
fmt.Println("ได้รับ", v)
}
}
**实际输出**
ได้รับ 10
ได้รับ 20
ได้รับ 30
示例 19: select 与超时
select 可以同时等待多个通道。time.After 用来设置最长等待时间。
package main
import (
"fmt"
"time"
)
func main() {
result := make(chan string)
go func() {
time.Sleep(200 * time.Millisecond)
result <- "เสร็จ"
}()
select {
case msg := <-result:
fmt.Println(msg)
case <-time.After(50 * time.Millisecond):
fmt.Println("หมดเวลา")
}
}
**实际输出**
หมดเวลา
示例 20: 用 Mutex 保护共享数据
sync.Mutex 让多个协程安全地修改同一个变量,最终的总和始终正确。
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
var wg sync.WaitGroup
total := 0
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
total++
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("รวม =", total)
}
**实际输出**
รวม = 100
示例 21: strings 包与 rune
strings 提供字符串处理函数。len 统计的是字节数,统计泰语字符需要先转换为 []rune。
package main
import (
"fmt"
"strings"
)
func main() {
s := "Go เขียนง่าย"
fmt.Println(strings.ToUpper("go"), strings.Contains(s, "เขียน"))
fmt.Println(strings.Split("a,b,c", ","), len("สวัสดี"))
fmt.Println([]rune("สวัสดี")[0] == 'ส')
}
**实际输出**
GO true
[a b c] 18
true
示例 22: 读写文件
os.WriteFile 与 os.ReadFile 可以用一次调用读写整个文件。
package main
import (
"fmt"
"os"
)
func main() {
if err := os.WriteFile("note.txt", []byte("บันทึกจาก Go\n"), 0644); err != nil {
fmt.Println("เขียนไม่ได้:", err)
return
}
data, err := os.ReadFile("note.txt")
if err != nil {
fmt.Println("อ่านไม่ได้:", err)
return
}
fmt.Print("อ่านได้: ", string(data))
}
**实际输出**
อ่านได้: บันทึกจาก Go
示例 23: JSON
encoding/json 可以在 struct 与 JSON 之间互相转换,`json:"..."` 标签用于指定字段名。
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string `json:"name"`
Score int `json:"score"`
}
func main() {
b, _ := json.Marshal(Student{Name: "สมชาย", Score: 85})
fmt.Println(string(b))
var s Student
_ = json.Unmarshal([]byte(`{"name":"สมหญิง","score":72}`), &s)
fmt.Println(s.Name, s.Score)
}
**实际输出**
{"name":"สมชาย","score":85}
สมหญิง 72
示例 24: 测试 HTTP 处理函数
httptest 可以在不启动真实服务器的情况下测试处理函数。
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "สวัสดีจาก Go HTTP")
}
func main() {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
hello(rec, req)
body, _ := io.ReadAll(rec.Result().Body)
fmt.Println(rec.Code, string(body))
}
**实际输出**
200 สวัสดีจาก Go HTTP
示例 25: 泛型
使用类型参数 T 编写可用于多种类型的函数,并用接口约束类型。
package main
import "fmt"
type Number interface {
~int | ~float64
}
func Sum[T Number](values []T) T {
var total T
for _, v := range values {
total += v
}
return total
}
func main() {
fmt.Println(Sum([]int{1, 2, 3}))
fmt.Println(Sum([]float64{1.5, 2.5}))
}
**实际输出**
6
4
下一步
尝试编写一个接收 JSON 并保存到文件的 Web 服务器。然后学习 net/http 与 context 包,用于超时和取消操作。
总结
Go 语法简洁,类型在编译时检查,错误作为值处理,并通过协程和通道支持并发,因此很适合服务器和需要处理大量任务的场景。