Go(又称 Golang)是一门静态类型、编译型语言,主打简洁、高效、并发安全,语法设计贴近工程实践,避免冗余特性。以下是其核心语法的全面梳理,适合入门者系统学习,也可作为日常参考。
一、基础语法:程序结构与变量常量
1. 程序入口与包声明
Go 程序的执行入口是 main 包下的 main() 函数,必须满足以下规则:
- 每个文件属于一个包(
package 包名声明在文件第一行); - 只有
main包能包含main()函数(程序入口); - 导入依赖包使用
import,支持单行 / 多行导入。
go
// 单行导入
import "fmt"
// 多行导入(推荐,更清晰)
import (
"fmt"
"math"
)
// 程序入口:无参数、无返回值
func main() {
fmt.Println("Hello, Go!") // 输出:Hello, Go!
}
2. 变量声明
Go 变量声明有 4 种方式,核心原则:强类型、声明即初始化(默认零值)。
| 声明方式 | 语法示例 | 适用场景 |
|---|---|---|
| 完整声明 | var 变量名 类型 = 值 |
类型明确,需显式指定 |
| 类型推导(推荐) | var 变量名 = 值 |
编译器自动推导类型,简洁 |
| 短变量声明(仅函数内) | 变量名 := 值 |
函数内快速声明,最常用 |
| 批量声明 | var (a int = 1; b string = "test") |
多变量集中声明,减少重复代码 |
零值规则:未显式赋值的变量会被赋予默认零值(int=0、string=""、bool=false、指针 = nil 等)。
go
func main() {
var a int = 10 // 完整声明
var b = "hello" // 类型推导(string 类型)
c := 3.14 // 短变量声明(float64 类型)
var (
d bool // 零值:false
e []int // 零值:nil(切片)
)
fmt.Println(a, b, c, d, e) // 输出:10 hello 3.14 false []
}
3. 常量声明
使用 const 关键字,值必须是编译期可确定的常量(数字、字符串、布尔值),支持批量声明和枚举。
go
// 基本常量
const PI = 3.14159
const NAME string = "Go"
// 批量声明
const (
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
)
// iota 枚举(自增常量,默认从 0 开始,每行+1)
const (
A = iota // 0
B = iota // 1
C = 100 // 手动赋值,打破自增
D = iota // 3(恢复自增,继续计数)
)
二、数据类型:值类型与引用类型
Go 数据类型分为 值类型 (拷贝传递)和 引用类型(指针传递),核心类型如下:
1. 基本类型(值类型)
| 类型类别 | 具体类型 | 说明 |
|---|---|---|
| 整数类型 | int8/int16/int32/int64、uint8/uint16... | int 随系统位数(32/64 位),uint 同理 |
| 浮点类型 | float32(精度约 6 位)、float64(默认) | float64 精度更高,推荐优先使用 |
| 布尔类型 | bool | 取值 true/false,不可用 0/1 替代 |
| 字符串类型 | string | 不可变(修改需重新生成),UTF-8 编码 |
| 字符类型 | rune(别名 int32,表示 Unicode 字符) | 区别于 byte(uint8,ASCII 字符) |
go
func main() {
var age int = 25
var weight float64 = 68.5
var isStudent bool = true
var name string = "张三"
var char rune = '中' // 注意:单引号表示字符,双引号表示字符串
fmt.Printf("%T %T %T %T %T\n", age, weight, isStudent, name, char)
// 输出:int float64 bool string int32
}
2. 复合类型
(1)数组(值类型)
固定长度、同类型元素的集合,长度是类型的一部分([3]int 和 [5]int 是不同类型)。
go
// 声明方式
var arr1 [3]int = [3]int{1, 2, 3} // 完整声明
arr2 := [3]int{4, 5, 6} // 类型推导
arr3 := [...]int{7, 8, 9} // 自动推导长度
// 访问与修改
fmt.Println(arr1[0]) // 输出:1
arr2[1] = 10
fmt.Println(arr2) // 输出:[4 10 6]
(2)切片(引用类型)
动态长度的 "动态数组",基于数组实现,核心结构:指针+长度+容量,声明时无需指定长度。
go
// 声明方式
var s1 []int // 零值:nil
s2 := []int{1, 2, 3} // 直接初始化
s3 := make([]int, 3, 5) // make(类型, 长度, 容量):长度 3,容量 5
// 常用操作
s4 := append(s2, 4, 5) // 追加元素(容量不足时自动扩容)
s5 := s2[1:3] // 切片截取:[start:end),左闭右开
fmt.Println(len(s3), cap(s3)) // 输出:3 5(len=长度,cap=容量)
(3)映射(map,引用类型)
无序键值对集合,键必须是 "可比较类型"(int、string、bool 等,不能是切片、map)。
go
// 声明与初始化(必须用 make 或直接赋值,否则为 nil,无法添加元素)
m1 := make(map[string]int) // 空 map
m2 := map[string]string{
"name": "李四",
"age": "28",
}
// 增删改查
m1["score"] = 90 // 新增/修改
fmt.Println(m1["score"]) // 查询:90
delete(m1, "score") // 删除
v, ok := m1["score"] // 安全查询(避免键不存在时返回零值)
fmt.Println(v, ok) // 输出:0 false(ok 表示键是否存在)
(4)结构体(值类型)
自定义复合类型,用于封装多个不同类型的字段。
go
// 定义结构体
type Person struct {
Name string
Age int
Sex string
}
// 初始化
p1 := Person{Name: "王五", Age: 30, Sex: "男"} // 指定字段名(推荐)
p2 := Person{"赵六", 26, "女"} // 按字段顺序(不推荐,易出错)
// 访问字段
fmt.Println(p1.Name) // 输出:王五
p2.Age = 27
(5)指针(引用类型)
存储变量内存地址的变量,核心用途:修改函数参数的值(值类型默认拷贝传递)。
go
func main() {
a := 10
var p *int = &a // &a:取变量 a 的地址;p 是 *int 类型指针
fmt.Println(*p) // *p:通过指针访问变量的值,输出:10
modify(p) // 传递指针
fmt.Println(a) // 输出:20(变量被修改)
}
// 函数接收指针参数
func modify(x *int) {
*x = 20 // 修改指针指向的变量值
}
三、流程控制:条件、循环、跳转
1. 条件语句(if-else)
- 无需括号包裹条件表达式;
- 条件表达式必须是 bool 类型(不能用 0/1 替代);
- 支持 "初始化语句"(在条件前声明变量,作用域仅限 if 块)。
go
func main() {
age := 18
// 基础用法
if age >= 18 {
fmt.Println("成年")
} else {
fmt.Println("未成年")
}
// 带初始化语句
if score := 95; score >= 90 {
fmt.Println("优秀")
} else if score >= 80 {
fmt.Println("良好")
} else {
fmt.Println("合格")
}
}
2. 循环语句(for)
Go 只有 for 一种循环结构,支持三种用法:
(1)普通循环(类似 while)
go
i := 0
for i < 5 { // 条件满足时循环
fmt.Println(i)
i++
}
(2)经典 for 循环(初始化 + 条件 + 自增)
go
for j := 0; j < 3; j++ {
fmt.Println(j) // 输出:0 1 2
}
(3)无限循环(无条件)
go
count := 0
for { // 等价于 for ; ; {}
count++
if count == 3 {
break // 跳出循环
}
fmt.Println(count) // 输出:1 2
}
(4)循环遍历(for range)
专门用于遍历数组、切片、map、字符串等,更简洁高效:
go
// 遍历切片
s := []int{1, 2, 3}
for index, value := range s {
fmt.Printf("索引:%d,值:%d\n", index, value)
}
// 遍历 map(无序)
m := map[string]int{"a":1, "b":2}
for key, val := range m {
fmt.Printf("键:%s,值:%d\n", key, val)
}
// 遍历字符串(按 rune 遍历,支持中文)
str := "Hello 世界"
for i, c := range str {
fmt.Printf("索引:%d,字符:%c\n", i, c)
}
3. 跳转语句(break、continue、goto)
break:跳出当前循环 /switch 块;continue:跳过当前循环迭代,进入下一次;goto:跳转到当前函数内的标签(谨慎使用,避免破坏代码结构)。
go
// break 示例(跳出循环)
for i := 0; i < 5; i++ {
if i == 3 {
break
}
fmt.Println(i) // 输出:0 1 2
}
// goto 示例
func main() {
fmt.Println("1")
goto label
fmt.Println("2") // 不会执行
label:
fmt.Println("3") // 输出:1 3
}
4. 选择语句(switch)
Go 的 switch 更灵活,支持任意类型,无需 break(默认自动跳出,需穿透用 fallthrough)。
go
func main() {
num := 2
switch num {
case 1:
fmt.Println("一")
case 2:
fmt.Println("二")
case 3:
fmt.Println("三")
default:
fmt.Println("其他")
}
// 带初始化语句
switch score := 85; { // 条件可以是表达式(类似 if-else 链)
case score >= 90:
fmt.Println("优秀")
case score >= 80:
fmt.Println("良好")
default:
fmt.Println("合格")
}
}
四、函数:声明、参数、返回值
1. 函数声明
核心语法:func 函数名(参数列表) (返回值列表) { 函数体 }
go
// 无参数、无返回值
func sayHello() {
fmt.Println("Hello, Go Function!")
}
// 有参数、有返回值(单返回值)
func add(a int, b int) int { // 参数类型相同可简写为 (a, b int)
return a + b
}
// 多返回值(Go 特色,常用于返回结果+错误)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为 0") // 返回错误
}
return a / b, nil // 无错误返回 nil
}
// 调用函数
func main() {
sayHello()
sum := add(10, 20)
fmt.Println(sum) // 输出:30
res, err := divide(10, 2)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(res) // 输出:5
}
}
2. 函数进阶特性
(1)命名返回值
返回值声明时指定名称,函数内可直接赋值,return 无需带参数(更简洁)。
go
func calc(a, b int) (sum, sub int) {
sum = a + b
sub = a - b
return // 裸返回,自动返回 sum 和 sub
}
(2)可变参数
参数列表最后一个参数用 ...类型 表示,函数内视为切片处理。
go
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
// 调用:可传递任意个 int 类型参数
fmt.Println(sum(1, 2, 3)) // 输出:6
fmt.Println(sum(4, 5, 6, 7)) // 输出:22
(3)函数作为参数 / 返回值(高阶函数)
Go 中函数是 "一等公民",可作为参数传递或作为返回值返回。
go
// 函数作为参数
func operate(a, b int, f func(int, int) int) int {
return f(a, b)
}
// 函数作为返回值
func getAddFunc() func(int, int) int {
return func(a, b int) int {
return a + b
}
}
// 调用
addFunc := getAddFunc()
fmt.Println(operate(5, 3, addFunc)) // 输出:8
五、接口与面向对象
Go 没有类和继承,通过 接口(interface) 实现多态,核心思想:鸭子类型("看起来像鸭子,走起来像鸭子,就是鸭子")。
1. 接口声明
接口是方法签名的集合,只定义 "做什么",不定义 "怎么做"(无实现)。
go
// 定义接口
type Animal interface {
Speak() string // 方法签名:无参数,返回 string
Move() string
}
2. 接口实现
无需显式声明 "实现接口",只要结构体实现了接口的所有方法,就自动实现该接口(非侵入式)。
go
// 定义结构体
type Dog struct {
Name string
}
type Cat struct {
Name string
}
// Dog 实现 Animal 接口(实现所有方法)
func (d Dog) Speak() string {
return fmt.Sprintf("%s 汪汪叫", d.Name)
}
func (d Dog) Move() string {
return fmt.Sprintf("%s 跑起来", d.Name)
}
// Cat 实现 Animal 接口
func (c Cat) Speak() string {
return fmt.Sprintf("%s 喵喵叫", c.Name)
}
func (c Cat) Move() string {
return fmt.Sprintf("%s 跳起来", c.Name)
}
3. 接口使用(多态)
接口变量可存储任意实现该接口的结构体实例,调用方法时自动执行对应实现。
go
func main() {
var animal Animal // 接口变量
animal = Dog{Name: "旺财"}
fmt.Println(animal.Speak()) // 输出:旺财 汪汪叫
fmt.Println(animal.Move()) // 输出:旺财 跑起来
animal = Cat{Name: "咪宝"}
fmt.Println(animal.Speak()) // 输出:咪宝 喵喵叫
}
4. 空接口(interface {})
无任何方法的接口,可存储任意类型的值(类似 Java 的 Object),常用于处理未知类型。
go
// 空接口变量
var any interface{}
any = 10 // 存储 int
any = "hello" // 存储 string
any = Dog{Name: "旺财"} // 存储结构体
// 类型断言(判断空接口存储的实际类型)
val, ok := any.(Dog)
if ok {
fmt.Println(val.Name) // 输出:旺财
}
六、错误处理
Go 不使用 try-catch 捕获异常,而是通过 显式返回错误(error 类型) 处理错误,核心原则:"错误也是值"。
1. 错误类型与创建
error 是接口类型,定义:type error interface { Error() string },常用 fmt.Errorf() 或 errors.New() 创建错误。
go
import (
"errors"
"fmt"
)
func checkAge(age int) error {
if age < 0 || age > 120 {
// 创建错误:两种方式
return errors.New("年龄必须在 0-120 之间")
// return fmt.Errorf("年龄 %d 非法:必须在 0-120 之间", age) // 带格式化信息
}
return nil // 无错误返回 nil
}
// 调用:必须检查错误
func main() {
err := checkAge(150)
if err != nil {
fmt.Println("错误:", err) // 输出:错误:年龄必须在 0-120 之间
return
}
fmt.Println("年龄合法")
}
2. 自定义错误(进阶)
通过结构体实现 error 接口,可携带更多错误信息(如错误码)。
go
// 自定义错误结构体
type MyError struct {
Code int
Message string
}
// 实现 error 接口的 Error() 方法
func (e *MyError) Error() string {
return fmt.Sprintf("错误码:%d,信息:%s", e.Code, e.Message)
}
// 返回自定义错误
func doSomething() error {
return &MyError{Code: 500, Message: "服务器内部错误"}
}
// 调用
err := doSomething()
if err != nil {
fmt.Println(err) // 输出:错误码:500,信息:服务器内部错误
// 类型断言获取自定义错误信息
if myErr, ok := err.(*MyError); ok {
fmt.Println("错误码:", myErr.Code) // 输出:500
}
}
七、并发编程(Go 核心特性)
Go 原生支持并发,通过 goroutine(轻量级线程) 和 channel(通道) 实现 "通信顺序进程(CSP)" 模型,简单高效。
1. Goroutine(协程)
- 轻量级线程,由 Go 运行时(runtime)管理,而非操作系统内核;
- 启动成本极低(栈初始 2KB,可动态扩容),支持百万级并发;
- 用
go关键字启动(后跟函数 / 方法调用)。
go
func sayHello(name string) {
fmt.Printf("Hello, %s\n", name)
}
func main() {
// 启动 3 个 goroutine
go sayHello("A")
go sayHello("B")
go sayHello("C")
// 主 goroutine 退出后,子 goroutine 会被强制终止,需等待
time.Sleep(100 * time.Millisecond) // 简单等待(不推荐)
}
2. Channel(通道)
用于 goroutine 间通信,解决数据竞争,实现 "同步 + 传值",分为无缓冲通道 和有缓冲通道。
(1)无缓冲通道(同步通道)
- 发送操作(
ch <- val)会阻塞,直到有 goroutine 接收; - 接收操作(
val := <-ch)会阻塞,直到有 goroutine 发送。
go
func send(ch chan int) {
ch <- 10 // 发送 10 到通道,阻塞直到接收
fmt.Println("发送完成")
}
func main() {
ch := make(chan int) // 无缓冲通道(容量默认 0)
go send(ch) // 启动发送 goroutine
val := <-ch // 接收通道数据,阻塞直到发送
fmt.Println("接收值:", val) // 输出:接收值:10
time.Sleep(100 * time.Millisecond) // 输出:发送完成
}
(2)有缓冲通道(异步通道)
- 容量 > 0,发送操作仅当缓冲区满时阻塞;
- 接收操作仅当缓冲区空时阻塞。
go
func main() {
ch := make(chan int, 2) // 有缓冲通道,容量 2
ch <- 1 // 发送成功(缓冲区未满)
ch <- 2 // 发送成功(缓冲区未满)
// ch <- 3 // 阻塞(缓冲区满)
fmt.Println(<-ch) // 接收 1,缓冲区剩余 1
fmt.Println(<-ch) // 接收 2,缓冲区空
}
(3)通道关闭与遍历
- 用
close(ch)关闭通道,关闭后不能再发送数据,但可继续接收; - 用
for range遍历通道,直到通道关闭。
go
func send(ch chan int) {
for i := 0; i < 3; i++ {
ch <- i
}
close(ch) // 发送完成后关闭通道
}
func main() {
ch := make(chan int, 3)
go send(ch)
// 遍历通道,直到关闭
for val := range ch {
fmt.Println(val) // 输出:0 1 2
}
}
3. sync 包(同步原语)
除了 channel,还可通过 sync 包的工具实现同步(如互斥锁、等待组)。
(1)等待组(sync.WaitGroup)
替代 time.Sleep,优雅等待多个 goroutine 完成。
go
func work(id int, wg *sync.WaitGroup) {
defer wg.Done() // goroutine 完成后调用(计数器-1)
fmt.Printf("工作 %d 开始\n", id)
time.Sleep(100 * time.Millisecond)
fmt.Printf("工作 %d 结束\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // 启动 goroutine 前计数器+1
go work(i, &wg) // 传递指针(避免拷贝)
}
wg.Wait() // 阻塞,直到计数器为 0
fmt.Println("所有工作完成")
}
(2)互斥锁(sync.Mutex)
解决多个 goroutine 并发修改共享变量的数据竞争问题。
go
var (
count int
mutex sync.Mutex // 互斥锁
)
func increment() {
mutex.Lock() // 加锁
defer mutex.Unlock() // 延迟解锁(确保函数退出时释放)
count++
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("count:", count) // 输出:1000(无数据竞争)
}
八、常用标准库
Go 标准库丰富,以下是入门必备的核心库:
| 库名 | 核心功能 | 常用函数 / 类型 |
|---|---|---|
fmt |
输入输出格式化 | Println()、Printf()、Scanln() |
os |
操作系统交互(文件、环境变量等) | os.Args、os.Open()、os.Exit() |
io/ioutil |
文件读写(简化版) | ioutil.ReadFile()、ioutil.WriteFile() |
bufio |
带缓冲的读写(高效) | bufio.NewReader()、bufio.Scanner |
net/http |
HTTP 客户端 / 服务端 | http.Get()、http.HandleFunc() |
encoding/json |
JSON 序列化 / 反序列化 | json.Marshal()、json.Unmarshal() |
time |
时间处理 | time.Now()、time.Sleep()、time.Parse() |
九、语法核心注意事项
- 大小写敏感 :
Name和name是不同变量 / 函数; - 语句结尾无分号:编译器自动添加,一行一个语句(多行语句需用分号分隔);
- 变量 / 导入未使用:编译报错(避免冗余代码);
- 值类型 vs 引用类型:值类型拷贝传递,引用类型传递指针(修改会影响原数据);
- map 和切片必须初始化 :nil map / 切片无法添加元素,需用
make或直接赋值; - 并发安全:多个 goroutine 操作共享变量时,需用 mutex 或 channel 同步。