golang文档
1. Go 是什么
Go 是一种强类型、垃圾回收、原生支持并发的编程语言。代码由 package 组成;用 module 管理依赖;可执行程序通常从 package main 的 main() 函数开始运行。Go 官方一直强调兼容性,Go 1.26 也继续保持 Go 1 的兼容承诺。
最小可运行程序:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
这里你已经看到了 Go 最核心的 3 个语法点:
package 声明包、import 导入包、func 声明函数。main 包里的 main() 是程序入口。
2. 先记住 Go 的代码长什么样
一个典型 Go 文件:
package main
import (
"fmt"
"time"
)
const AppName = "demo"
var globalCount int = 0
type User struct {
Name string
Age int
}
func (u User) SayHi() {
fmt.Println("Hi,", u.Name)
}
func main() {
u := User{Name: "Hua", Age: 18}
u.SayHi()
fmt.Println(AppName, time.Now())
}
这已经包含了很多 Go 语法:
常量、变量、结构体、方法、短变量声明、包导入。Go 倾向于代码结构清晰而不是语法花哨。
3. 关键字与标识符
Go 有一组保留关键字,例如:
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
这些不能拿来当变量名、函数名、类型名。标识符命名时,首字母大写表示可导出(public),小写表示包内私有。比如 User 可被别的包访问,user 不行。
例子:
package demo
var PublicName = "可以被外部包访问"
var privateName = "只能在当前包内访问"
4. 注释
Go 支持两种注释:
// 单行注释
/*
多行注释
*/
日常开发里最常用的是 //。放在顶层声明前的注释还会被当作文档注释。
5. 变量:var与**:=**
5.1 基本声明
var name string
var age int
var isAdmin bool
Go 有零值概念:
string 默认是 “”,int 默认是 0,bool 默认是 false,指针 / slice / map / chan / func / interface 默认是 nil。
5.2 声明并初始化
var name string = "Hua"
var age = 18
如果右边有值,很多时候可以省类型,让编译器推断。
5.3 短变量声明**:=**
name := "Hua"
age := 18
这是 Go 里最常用的写法,但只能在函数内部使用,不能用于包级变量。
5.4 多变量声明
var a, b int = 1, 2
x, y := "hello", true
5.5 分组声明
var (
name string = "Hua"
age int = 18
)
**6. 常量:**const
常量在编译期确定,常用来放固定配置、枚举值。
const Pi = 3.14
const AppName = "myapp"
也支持分组:
const (
StatusPending = 0
StatusDone = 1
StatusFail = 2
)
7.iota:Go 里的枚举神器
Go 没有传统语言那种 enum 关键字,通常用 const + iota 实现枚举。
const (
Red = iota
Green
Blue
)
值依次是 0 1 2。
更常见的业务写法:
type Status int
const (
StatusPending Status = iota
StatusPaid
StatusShipped
StatusDone
)
作用:
让状态值有明确类型,不是到处乱飞的裸数字。
8. 基本数据类型
Go 常见内建类型包括:
布尔、整数、浮点、复数、字符串。数值类型还有不同位宽版本。规范定义了这些预声明类型与常量表达式、类型推断规则。
8.1 布尔
var ok bool = true
8.2 整数
var a int = 10
var b int8 = 127
var c uint = 20
var d byte = 255 // byte 是 uint8 的别名
var e rune = '中' // rune 是 int32 的别名,常表示 Unicode 码点
8.3 浮点
var f1 float32 = 3.14
var f2 float64 = 3.1415926
8.4 复数
var c1 complex64 = 1 + 2i
var c2 complex128 = 3 + 4i
8.5 字符串
var s string = "hello"
字符串是只读字节序列。中文按 UTF-8 存储,一个汉字可能占多个字节,所以“按下标取字符串”很容易踩坑。
例子:
s := "你好"
fmt.Println(len(s)) // 字节长度,不是字符个数
fmt.Println([]rune(s)) // 转成 rune 切片再按字符处理
9. 类型转换
Go 不做很多隐式转换,你要手动转:
var a int = 10
var b int64 = int64(a)
var f float64 = 3.14
var n int = int(f) // 小数部分会被截断
这个设计是为了让类型更明确,减少隐式转换带来的 bug。
10. 运算符
10.1 算术运算
a + b
a - b
a * b
a / b
a % b
10.2 比较运算
a == b
a != b
a > b
a < b
a >= b
a <= b
10.3 逻辑运算
a && b
a || b
!a
10.4 位运算
a & b
a | b
a ^ b
a &^ b
a << 1
a >> 1
&^ 是 Go 比较有特色的“按位清除”。
10.5 自增自减
i++
i--
注意:Go 的 ++、-- 是语句,不是表达式,不能写成:
// 错误示例
x = i++
11. 流程控制
Go 主要流程控制就 3 个:if、for、switch。Go 没有 while,但 for 可以胜任。控制语句语法里不需要圆括号。
11.1 if
if age >= 18 {
fmt.Println("成年")
} else {
fmt.Println("未成年")
}
带初始化语句的****if
if score := 90; score >= 60 {
fmt.Println("及格")
}
这个变量只在 if / else 作用域内有效。
11.2****for
普通 for
for i := 0; i < 5; i++ {
fmt.Println(i)
}
类 while
sum := 0
for sum < 100 {
sum += 10
}
无限循环
for {
// do something
}
range
nums := []int{10, 20, 30}
for index, value := range nums {
fmt.Println(index, value)
}
也可以只取一个:
for _, value := range nums {
fmt.Println(value)
}
_ 是空白标识符,表示“这个值我不要”。
11.3 switch
day := 2
switch day {
case 1:
fmt.Println("Mon")
case 2:
fmt.Println("Tue")
default:
fmt.Println("Other")
}
Go 的 switch 默认不会自动向下贯穿。如果你真要继续执行下一个 case,用 fallthrough。
switch n := 1; n {
case 1:
fmt.Println("one")
fallthrough
case 2:
fmt.Println("two")
}
无表达式
switch
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
default:
fmt.Println("C")
}
很适合写区间判断。
**11.4break、continue、**goto
for i := 0; i < 10; i++ {
if i == 3 {
continue
}
if i == 8 {
break
}
fmt.Println(i)
}
goto 也存在,但正常业务开发很少用。
12. 函数****func
函数是 Go 最核心的组织单位之一。Go 支持多返回值,这是它很重要的特性。
12.1 最基础函数
func add(a int, b int) int {
return a + b
}
相邻参数类型相同时可简写:
func add(a, b int) int {
return a + b
}
12.2 多返回值
func divide(a, b int) (int, int) {
return a / b, a % b
}
调用:
q, r := divide(10, 3)
fmt.Println(q, r)
12.3 命名返回值
func calc(a, b int) (sum int, diff int) {
sum = a + b
diff = a - b
return
}
能用,但别滥用。业务里普通 return x, y 通常更清晰。
12.4 可变参数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
调用:
fmt.Println(sum(1, 2, 3))
切片展开:
arr := []int{1, 2, 3}
fmt.Println(sum(arr...))
12.5 函数也是值
func apply(a, b int, fn func(int, int) int) int {
return fn(a, b)
}
12.6 匿名函数
fn := func(name string) {
fmt.Println("hello", name)
}
fn("Hua")
12.7 闭包
func counter() func() int {
n := 0
return func() int {
n++
return n
}
}
作用:
让内部函数记住外层变量,常用于状态封装、回调、工厂函数。
**13.**defer
defer 表示“当前函数快结束时再执行”。最常用于释放资源:关闭文件、解锁、关闭连接。
func readFile() {
f, err := os.Open("a.txt")
if err != nil {
return
}
defer f.Close()
// 使用 f
}
多个 defer 按后进先出执行。
func test() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
}
// 输出顺序:3 2 1
**14. 错误处理:**error
Go 不用异常做日常错误处理,而是显式返回 error。这是 Go 开发的基本功。
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为 0")
}
return a / b, nil
}
调用:
result, err := divide(10, 0)
if err != nil {
fmt.Println("出错:", err)
return
}
fmt.Println(result)
14.1 自定义错误
type MyError struct {
Msg string
}
func (e MyError) Error() string {
return e.Msg
}
只要实现了 Error() string,就满足 error 接口。
15.panic与****recover
panic 表示程序出现了严重错误,开始向上层栈展开;recover 只能在 defer 中捕获 panic。它们适合处理“不应该发生”的情况,不适合作为普通业务错误机制。
func test() {
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获到 panic:", r)
}
}()
panic("出大问题了")
}
16. 指针
Go 有指针,但没有指针运算。指针用于修改原值、避免大对象复制。
func inc(n *int) {
*n++
}
func main() {
x := 10
inc(&x)
fmt.Println(x) // 11
}
记法:
-
*T:指向 T 的指针类型
-
&x:取地址
-
*p:解引用
17. 数组****array
数组长度是类型的一部分。
var a [3]int
a[0] = 10
fmt.Println(a)
直接初始化:
b := [3]int{1, 2, 3}
自动推长度:
c := [...]int{1, 2, 3, 4}
Go 里数组用得没有切片多,因为数组长度固定,传参还会复制整个数组。
18. 切片****slice
切片是 Go 里最常用的数据结构之一。它是对底层数组的一层视图,带长度和容量。
18.1 声明
var s []int
零值是 nil。
18.2 初始化
s := []int{1, 2, 3}
18.3****make
s := make([]int, 3) // len=3 cap=3
s2 := make([]int, 3, 5) // len=3 cap=5
18.4 截取
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // [2 3 4]
规则:
-
a[start:end]
-
包左不包右
-
可省略一边
s[:3]
s[1:]
s[:]
18.5****append
s := []int{1, 2}
s = append(s, 3)
s = append(s, 4, 5)
注意:append 可能返回新底层数组,所以要接住返回值。
18.6****copy
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
18.7 常见坑:共享底层数组
a := []int{1, 2, 3, 4}
b := a[:2]
b[0] = 100
fmt.Println(a) // [100 2 3 4]
因为 b 和 a 可能指向同一底层数组。
**19.**map
map 是键值对结构。零值是 nil;nil map 可以读,不能写。通常用 make 初始化。
19.1 声明与初始化
var m map[string]int
m = make(map[string]int)
m["age"] = 18
字面量:
m := map[string]int{
"age": 18,
"score": 99,
}
19.2 取值
fmt.Println(m["age"])
如果 key 不存在,会返回 value 类型的零值。
19.3 判断 key 是否存在
v, ok := m["name"]
if ok {
fmt.Println("存在:", v)
} else {
fmt.Println("不存在")
}
19.4 删除
delete(m, "age")
19.5 遍历
for k, v := range m {
fmt.Println(k, v)
}
注意:map 遍历顺序不保证固定。
20. 结构体****struct
结构体是 Go 组合数据的核心方式。
20.1 声明
type User struct {
Name string
Age int
}
20.2 初始化
u1 := User{Name: "Hua", Age: 18}
u2 := User{"Tom", 20}
更推荐第一种,字段名清晰,不怕字段顺序变化。
20.3 访问字段
fmt.Println(u1.Name)
u1.Age = 19
20.4 指向结构体的指针
u := &User{Name: "Hua", Age: 18}
fmt.Println(u.Name) // Go 自动解引用
21. 方法****method
Go 里可以给类型绑定方法。方法本质上是“带接收者的函数”。
type User struct {
Name string
}
func (u User) SayHi() {
fmt.Println("Hi,", u.Name)
}
调用:
u := User{Name: "Hua"}
u.SayHi()
21.1 值接收者 vs 指针接收者
值接收者
func (u User) Rename(name string) {
u.Name = name
}
这里改的是副本,外面原值不变。
指针接收者
func (u *User) Rename(name string) {
u.Name = name
}
这样才能修改原对象。
经验:
-
需要修改对象:用指针接收者
-
结构体比较大:通常也用指针接收者
-
尽量保持同一类型的方法接收者风格一致
22. 类型type:自定义类型与别名
22.1 定义新类型
type MyInt int
这是新类型,不等于 int。
22.2 类型别名
type MyIntAlias = int
这是别名,等同于 int。
作用区别很大,别混了。
23. 接口****interface
接口是 Go 抽象能力的核心。Go 的接口是隐式实现:一个类型只要实现了接口要求的方法,就自动实现这个接口,不需要 implements。
23.1 定义接口
type Speaker interface {
Speak() string
}
23.2 实现接口
type Dog struct{}
func (Dog) Speak() string {
return "wang"
}
23.3 使用接口
func say(s Speaker) {
fmt.Println(s.Speak())
}
23.4 空接口
var x interface{}
x = 10
x = "hello"
interface{} 能表示任意值。
现在更常见也更直观的写法是:
var x any
any 是 interface{} 的别名。
24. 类型断言与类型选择
当你拿到接口值,想知道底层实际类型时,用类型断言。
24.1 类型断言
var x any = "hello"
s, ok := x.(string)
if ok {
fmt.Println(s)
}
如果你直接写:
s := x.(string)
断言失败会 panic。
24.2 type switch
func printType(v any) {
switch t := v.(type) {
case int:
fmt.Println("int", t)
case string:
fmt.Println("string", t)
default:
fmt.Println("unknown")
}
}
25. 组合与嵌入(embedding)
Go 没有传统 OOP 的继承,常用组合。结构体里可以嵌入另一个结构体或接口。
type Address struct {
City string
}
type User struct {
Name string
Address
}
func main() {
u := User{Name: "Hua", Address: Address{City: "Tokyo"}}
fmt.Println(u.City) // 直接访问嵌入字段
}
这是一种非常 Go 风格的代码复用方式。
26. 包package与导入****import
Go 用包组织代码。一个目录通常一个包。package main 表示可执行程序包。导入语法有单行和分组两种。
import "fmt"
import (
"fmt"
"time"
)
26.1 别名导入
import f "fmt"
26.2 匿名导入
import _ "github.com/lib/pq"
用于只执行包的 init(),自己不直接用包名。
26.3 点导入
import . "fmt"
这样可以直接写 Println(),但一般不推荐,容易污染命名空间。
27. 模块****go.mod
现代 Go 用 module 管理依赖。新项目通常先:
go mod init example/myapp
这会生成 go.mod。模块路径通常对应仓库地址;Go 官方教程也建议这样做。
常用命令:
go mod init example/myapp
go mod tidy
go run .
go build .
go test ./...
28. 可见性规则
Go 没有 public/private 关键字,靠首字母大小写控制导出。
type user struct{} // 包内私有
type User struct{} // 对外可见
func do() {} // 私有
func Do() {} // 导出
**29. 初始化:**init()
每个包可以定义 init() 函数,在包初始化时自动执行。常用于注册、默认配置、驱动初始化。
func init() {
fmt.Println("package init")
}
注意:
-
不能主动调用 init()
-
一个包可有多个 init()
-
不要把复杂业务逻辑塞进去
30. 并发:goroutine、channel、select、go 关键字
Go 的并发是语言级特性,不只是库功能。官方 Tour 也把它作为语言核心部分。
**30.1 goroutine:**go
go func() {
fmt.Println("异步执行")
}()
加上 go 关键字,函数就在新的 goroutine 里运行。
**30.2 channel:**chan
channel 用于 goroutine 之间通信。
ch := make(chan int)
go func() {
ch <- 42
}()
v := <-ch
fmt.Println(v)
带缓冲 channel
ch := make(chan int, 2)
ch <- 1
ch <- 2
关闭 channel
close(ch)
遍历 channel
for v := range ch {
fmt.Println(v)
}
前提是发送方最后会 close(ch)。
30.3 单向 channel
func send(ch chan<- int) {
ch <- 1
}
func recv(ch <-chan int) {
fmt.Println(<-ch)
}
-
chan<- int:只发
-
<-chan int:只收
30.4****select
同时等待多个 channel 操作。
select {
case v := <-ch1:
fmt.Println("ch1:", v)
case v := <-ch2:
fmt.Println("ch2:", v)
default:
fmt.Println("都没准备好")
}
这是 Go 并发模型里非常重要的语法。
31. 泛型(Generics)
Go 现在已经支持泛型;而 Effective Go 官方也明确说明它那篇文章较早,不覆盖泛型和 modules,要结合更新的规范与发行说明来看。
31.1 泛型函数
func PrintSlice[T any](items []T) {
for _, v := range items {
fmt.Println(v)
}
}
调用:
PrintSlice([]int{1, 2, 3})
PrintSlice([]string{"a", "b"})
[T any] 表示类型参数 T,约束是 any。
31.2 泛型类型
type Box[T any] struct {
Value T
}
使用:
b1 := Box[int]{Value: 10}
b2 := Box[string]{Value: "hello"}
31.3 约束
type Number interface {
~int | ~int64 | ~float64
}
func Add[T Number](a, b T) T {
return a + b
}
这类写法适合封装工具库,但业务开发里不用一上来就泛型化。
32. 标签、字符串字面量、Rune
32.1 原始字符串
s := `hello
world`
反引号字符串会保留换行,不处理转义。
32.2 解释型字符串
s := "hello\nworld"
32.3 字符与 rune
r := '中'
fmt.Printf("%T\n", r) // int32 / rune
单引号表示 rune 字面量,不是字符串。
33. 作用域
Go 主要有这些作用域:
-
包级作用域
-
函数作用域
-
代码块作用域
例子:
var global = "包级"
func main() {
x := 1
if true {
y := 2
fmt.Println(x, y)
}
// y 在这里不可见
}
短变量声明 := 还要小心遮蔽(shadowing):
err := doA()
if err != nil {
return err
}
if err := doB(); err != nil {
return err
}
第二个 err 是新的局部变量。
34. 标签语句与带标签的****break/continue
多层循环里非常有用。
Outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 1 {
break Outer
}
}
}
也可以 continue Outer。
35.range的常见对象
range 可用于:
-
array
-
slice
-
string
-
map
-
channel
-
整数迭代形式也已纳入较新的语言能力与规范演进中,但业务最常用的仍是前面几种容器/通道场景。
35.1 遍历字符串
for i, r := range "你好Go" {
fmt.Println(i, r, string(r))
}
注意这里拿到的是 rune,不是单字节。
36. 常见内建函数
Go 有一批预声明标识符和内建函数,开发中很常见。
最常用的:
len(x) // 长度
cap(x) // 容量
make(...) // 创建 slice/map/chan
new(T) // 分配零值并返回 *T
append(...)// 追加元素
copy(...) // 拷贝切片
delete(m,k)// 删除 map 键
close(ch) // 关闭 channel
panic(v) // 抛出 panic
recover() // 恢复 panic(defer 中)
例子:
p := new(int)
fmt.Println(*p) // 0
s := make([]int, 0, 10)
fmt.Println(len(s), cap(s))
37.new和make的区别
这个是 Go 初学者高频面试点。
new(T):
分配一块 T 类型零值内存,返回 *T
p := new(int) // *int
make(…):
只用于 slice、map、chan,返回初始化后的值本身,不是指针。
s := make([]int, 3)
m := make(map[string]int)
ch := make(chan int)
38. 结构体标签(tag)
很常用,尤其在 JSON、ORM、参数校验里。
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
这不是“运行时语法糖”,而是字符串元数据,常被 encoding/json 等包读取。
39.json编码解码最常见写法
这不是语言语法本身,但你开始做 Go 开发几乎一定会写。
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
u := User{Name: "Hua", Age: 18}
data, err := json.Marshal(u)
if err != nil {
return
}
var u2 User
err = json.Unmarshal(data, &u2)
if err != nil {
return
}
40. 面向对象在 Go 里怎么理解
Go 没有“类 class”,但有:
-
struct:承载数据
-
method:绑定行为
-
interface:抽象能力
-
embedding:复用能力
所以 Go 不是“不能面向对象”,而是不走 class 继承那套路子。官方也长期强调组合优于继承、用接口表达能力。
41. Go 最常见项目文件结构
入门阶段你可以先这样理解:
myapp/
go.mod
main.go
internal/
pkg/
但别一开始就搞很复杂的目录规范。很多 Go 项目完全可以先从:
myapp/
go.mod
main.go
user.go
service.go
开始,等代码多了再拆包。
42. Go 写 Web 服务最基础长什么样
这不是纯语法,但你看完这个就知道 Go 开发长什么样了。
package main
import (
"encoding/json"
"net/http"
)
type Resp struct {
Message string `json:"message"`
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
resp := Resp{Message: "hello go"}
_ = json.NewEncoder(w).Encode(resp)
}
func main() {
http.HandleFunc("/hello", helloHandler)
http.ListenAndServe(":8080", nil)
}
你已经能看到 Go 做服务端的基本套路:
-
定义结构体
-
写 handler
-
返回 JSON
-
启动 HTTP 服务
43. 新手必须掌握的 Go 开发习惯
Go 官方长期强调几件事:写清晰代码、让 gofmt 统一格式、用显式错误处理、接口从使用方抽象、保持包名简洁。Effective Go 虽然较早,不覆盖泛型和 modules,但这些核心风格仍然很有参考价值。
最重要的 7 条:
-
先写清楚,再谈抽象
-
优先组合,不迷信继承思维
-
错误显式处理,不吞错
-
能不用泛型时先不用
-
小接口优于大接口
-
不要过度封装
-
交给 gofmt 管格式
44. 新手最容易踩的坑
44.1nil map不能写
var m map[string]int
m["a"] = 1 // panic
要先 make。
44.2append忘了接返回值
s := []int{1, 2}
append(s, 3) // 没用
要写:
s = append(s, 3)
44.3 值接收者没改到原结构体
func (u User) SetName(name string) {
u.Name = name
}
应改成指针接收者。
44.4 字符串按字节不是按字符
s := "你好"
fmt.Println(s[0]) // 取到的是第一个字节,不是“你”
44.5 把
panic
当普通错误处理
大多数场景应该返回 error。
44.6 一上来就写很重的抽象层
Go 更适合从简单代码逐步抽象。
45. 你学完这些后,已经能写什么
只要你理解了上面这些内容,你已经可以开始写:
-
CLI 工具
-
文件处理脚本
-
HTTP API 服务
-
JSON 接口
-
并发任务
-
简单爬虫
-
WebSocket / TCP 基础服务
-
数据库 CRUD 程序
这些都属于“能进行 Go 开发”的范围。Go 官方教程本身也是从模块、包、函数、依赖、运行命令这些核心环节带你入门。
46. 给你一个真正能上手的入门顺序
按这个顺序学,效率最高:
先学:
-
变量、常量、基本类型
-
if、for、switch
-
函数、多返回值、defer
-
指针
-
array、slice、map
-
struct、方法
-
interface、错误处理
-
package / module
-
goroutine / channel / select
-
泛型
然后立刻做 3 个小项目:
-
命令行记账本
-
本地 JSON 文件用户管理
-
HTTP CRUD 服务
这样学得最快。
47. 一份最小 Go 语法速查表
// 变量
var a int = 1
b := 2
// 常量
const Pi = 3.14
// if
if a < b {
}
// for
for i := 0; i < 10; i++ {
}
// switch
switch a {
case 1:
default:
}
// 函数
func add(a, b int) int {
return a + b
}
// 多返回值
func div(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("zero")
}
return a / b, nil
}
// 指针
func inc(n *int) { *n++ }
// 数组
arr := [3]int{1,2,3}
// 切片
s := []int{1,2,3}
s = append(s, 4)
// map
m := map[string]int{"a": 1}
// struct
type User struct {
Name string
}
// 方法
func (u User) Say() {}
// 接口
type Speaker interface {
Speak() string
}
// goroutine
go task()
// channel
ch := make(chan int)
ch <- 1
v := <-ch
48. 最后一句实话
真正关键不是把规范背下来,而是把下面这些吃透:
-
slice
-
map
-
struct
-
method
-
interface
-
error
-
goroutine + channel
-
package + module
如果您觉得这篇文章有帮助,请点个赞吧~
评论
请登录后发表评论
去登录