02-Go基础语法
Go语法简洁,吸收C系语言优点,去除冗余特性。25个关键字,学习成本低。
数据类型
基本类型
// 整型
int8, int16, int32, int64
uint8, uint16, uint32, uint64
int // 32或64位(取决于平台)
uint // 无符号int
byte // uint8别名
rune // int32别名,表示Unicode码点
// 浮点型
float32, float64
// 复数
complex64, complex128
// 布尔型
bool // true或false
// 字符串
string // UTF-8编码,不可变
类型零值
Go变量声明后自动初始化为零值(无未定义行为)。
var i int // 0
var f float64 // 0.0
var b bool // false
var s string // ""(空字符串)
var p *int // nil
var slice []int // nil
var m map[string]int // nil
var ch chan int // nil
var fn func() // nil
类型转换
// 必须显式转换(无隐式转换)
var i int = 42
var f float64 = float64(i)
var u uint = uint(i)
// 字符串转换
import "strconv"
i, err := strconv.Atoi("123") // 字符串→int
s := strconv.Itoa(123) // int→字符串
f, err := strconv.ParseFloat("3.14", 64)
s := strconv.FormatFloat(3.14, 'f', 2, 64)
变量声明
四种方式
// 1. var声明
var name string = "Alice"
var age int = 25
// 2. 类型推断
var name = "Alice" // 自动推断为string
// 3. 短变量声明(函数内)
name := "Alice" // 最常用
age := 25
// 4. 批量声明
var (
name string = "Alice"
age int = 25
city string
)
常量
const Pi = 3.14159
const MaxSize = 100
// 批量常量
const (
Red = 0
Green = 1
Blue = 2
)
// iota(自增)
const (
_ = iota // 0,跳过
KB = 1 << (10 * iota) // 1024
MB // 1048576
GB // 1073741824
)
const (
Monday = iota + 1 // 1
Tuesday // 2
Wednesday // 3
)
基本语法
控制结构
// if(无需括号)
if x > 0 {
// ...
} else if x < 0 {
// ...
} else {
// ...
}
// if with初始化
if err := doSomething(); err != nil {
return err
}
// err作用域仅在if块内
// switch(无需break,自动break)
switch x {
case 1:
// ...
case 2, 3: // 多个条件
// ...
default:
// ...
}
// switch with初始化
switch err := doSomething(); err {
case nil:
// ...
default:
return err
}
// switch无表达式(替代if-else链)
switch {
case x > 0:
// ...
case x < 0:
// ...
default:
// ...
}
// fallthrough(显式穿透)
switch x {
case 1:
fmt.Println("one")
fallthrough // 继续执行case 2
case 2:
fmt.Println("two")
}
// for循环(唯一循环语句)
for i := 0; i < 10; i++ {
// ...
}
// while风格
for condition {
// ...
}
// 无限循环
for {
// ...
break
}
// range遍历
for i, v := range slice {
// i是索引,v是值
}
for k, v := range map {
// k是键,v是值
}
for i := range slice {
// 仅索引
}
for _, v := range slice {
// 忽略索引(_是空标识符)
}
// 标签和goto(少用)
Loop:
for {
for {
break Loop // 跳出外层循环
}
}
函数
函数定义
// 基本函数
func add(a int, b int) int {
return a + b
}
// 简化参数
func add(a, b int) int {
return a + b
}
// 多返回值
func swap(a, b int) (int, int) {
return b, a
}
x, y := swap(1, 2)
// 命名返回值
func divide(a, b int) (result int, err error) {
if b == 0 {
err = errors.New("division by zero")
return // 返回result和err的当前值
}
result = a / b
return
}
// 可变参数
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
sum(1, 2, 3, 4, 5)
nums := []int{1, 2, 3}
sum(nums...) // 展开切片
函数类型和闭包
// 函数类型
type Operation func(int, int) int
func apply(op Operation, a, b int) int {
return op(a, b)
}
apply(func(a, b int) int { return a + b }, 3, 4)
// 闭包
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
c := counter()
c() // 1
c() // 2
defer延迟执行
// defer按LIFO顺序执行
func example() {
defer fmt.Println("3")
defer fmt.Println("2")
fmt.Println("1")
}
// 输出:1 2 3
// 常用于资源清理
func readFile() error {
f, err := os.Open("file.txt")
if err != nil {
return err
}
defer f.Close() // 函数返回前自动关闭
// 读取文件
return nil
}
// defer参数立即求值
func deferTest() {
x := 1
defer fmt.Println(x) // 立即求值:1
x = 2
// 输出:1(不是2)
}
数据结构
数组
固定长度,值类型。
// 声明
var arr [5]int // [0, 0, 0, 0, 0]
arr := [5]int{1, 2, 3, 4, 5}
arr := [...]int{1, 2, 3} // 自动计算长度
// 访问
arr[0] = 10
len(arr) // 5
// 数组是值类型
arr2 := arr // 复制整个数组
切片(slice)
动态数组,引用类型,最常用。
// 创建
var s []int // nil切片
s := []int{1, 2, 3}
s := make([]int, 5) // 长度5,容量5
s := make([]int, 5, 10) // 长度5,容量10
// 切片数组
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // [2, 3, 4]
s := arr[:3] // [1, 2, 3]
s := arr[2:] // [3, 4, 5]
// 操作
s = append(s, 6) // 追加元素
s = append(s, 7, 8, 9) // 追加多个
s = append(s1, s2...) // 合并切片
copy(dst, src) // 复制
len(s) // 长度
cap(s) // 容量
// 删除元素
s = append(s[:i], s[i+1:]...) // 删除索引i
s = s[:len(s)-1] // 删除最后一个
s = s[1:] // 删除第一个
映射(map)
哈希表,引用类型。
// 创建
var m map[string]int // nil map(不能写入)
m := map[string]int{} // 空map
m := make(map[string]int)
m := map[string]int{"a": 1, "b": 2}
// 操作
m["key"] = value // 添加/更新
value := m["key"] // 读取
value, ok := m["key"] // 检查存在性
if ok {
// key存在
}
delete(m, "key") // 删除
len(m) // 元素个数
// 遍历(无序)
for k, v := range m {
fmt.Println(k, v)
}
for k := range m { // 仅键
// ...
}
结构体(struct)
// 定义
type Person struct {
Name string
Age int
City string
}
// 创建
p1 := Person{"Alice", 25, "Beijing"}
p2 := Person{Name: "Bob", Age: 30} // 字段名初始化
p3 := Person{Name: "Charlie"} // 未指定字段为零值
var p4 Person // 零值初始化
// 访问
p1.Name = "Alice2"
age := p1.Age
// 结构体指针
p := &Person{Name: "Alice"}
p.Age = 25 // 自动解引用(无需p->Age)
// 匿名字段(嵌入)
type Student struct {
Person // 匿名字段,继承Person的字段
StudentID string
}
s := Student{Person: Person{Name: "Alice"}, StudentID: "123"}
s.Name // 直接访问Person.Name
// 标签(tag)
type User struct {
Name string `json:"name" db:"user_name"`
Email string `json:"email" validate:"required,email"`
}
指针
Go有指针但无指针运算,更安全。
// 指针声明
var p *int
p = &x // 取地址
// 解引用
*p = 10
// new分配内存
p := new(int) // 返回*int,值为0
// 指针vs值
func modifyValue(x int) {
x = 100 // 不影响原变量
}
func modifyPointer(p *int) {
*p = 100 // 修改原变量
}
x := 1
modifyValue(x)
fmt.Println(x) // 1
modifyPointer(&x)
fmt.Println(x) // 100
方法
Go无类,用方法为类型添加行为。
type Rectangle struct {
Width, Height float64
}
// 值接收者
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 指针接收者(可修改)
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
r := Rectangle{Width: 10, Height: 5}
r.Area() // 50
r.Scale(2) // Width=20, Height=10
// 任意类型都可定义方法
type MyInt int
func (m MyInt) IsEven() bool {
return m%2 == 0
}
var x MyInt = 10
x.IsEven() // true
选择: 需要修改或大对象用指针接收者,否则用值接收者。
接口
隐式实现,鸭子类型。
// 接口定义
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// 组合接口
type ReadWriter interface {
Reader
Writer
}
// 实现接口(无需显式声明)
type File struct {}
func (f *File) Read(p []byte) (int, error) {
// 实现Read,File就实现了Reader接口
return 0, nil
}
// 空接口(任意类型)
interface{} // 或 any(Go 1.18+)
func Print(v interface{}) {
fmt.Println(v)
}
Print(42)
Print("hello")
Print([]int{1, 2, 3})
// 类型断言
var i interface{} = "hello"
s := i.(string) // "hello"
s, ok := i.(string) // "hello", true(安全断言)
n, ok := i.(int) // 0, false
// 类型switch
switch v := i.(type) {
case int:
fmt.Printf("int: %d\n", v)
case string:
fmt.Printf("string: %s\n", v)
default:
fmt.Printf("unknown type\n")
}
错误处理
Go使用多返回值处理错误,无异常机制。
import "errors"
// 返回错误
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// 使用
result, err := divide(10, 0)
if err != nil {
log.Fatal(err) // 处理错误
}
// 自定义错误
type MyError struct {
Code int
Message string
}
func (e *MyError) Error() string {
return fmt.Sprintf("code %d: %s", e.Code, e.Message)
}
// errors.Is(Go 1.13+)
var ErrNotFound = errors.New("not found")
if errors.Is(err, ErrNotFound) {
// 错误匹配
}
// errors.As(类型转换)
var myErr *MyError
if errors.As(err, &myErr) {
fmt.Println(myErr.Code)
}
// 错误包装
return fmt.Errorf("failed to process: %w", err)
并发基础
goroutine
轻量级线程,栈初始2KB,可创建百万级。
// 启动goroutine
go func() {
fmt.Println("goroutine")
}()
go processData()
// 匿名函数闭包
for i := 0; i < 5; i++ {
go func(n int) { // 参数传递,避免闭包陷阱
fmt.Println(n)
}(i)
}
// 等待goroutine(用sync.WaitGroup)
import "sync"
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
fmt.Println(n)
}(i)
}
wg.Wait() // 等待所有goroutine完成
channel
goroutine间通信,类型安全的管道。
// 创建channel
ch := make(chan int) // 无缓冲
ch := make(chan int, 10) // 缓冲大小10
// 发送和接收
ch <- 42 // 发送
value := <-ch // 接收
// 关闭channel
close(ch)
// 接收检查关闭
value, ok := <-ch
if !ok {
// channel已关闭
}
// range遍历(直到关闭)
for value := range ch {
fmt.Println(value)
}
// select多路复用
select {
case msg1 := <-ch1:
fmt.Println("ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("ch2:", msg2)
case ch3 <- value:
fmt.Println("sent to ch3")
case <-time.After(time.Second):
fmt.Println("timeout")
default:
fmt.Println("no communication")
}
包和可见性
// 包声明(每个文件)
package main
// 导入
import "fmt"
import (
"fmt"
"os"
)
// 别名
import f "fmt"
f.Println("hello")
// 点导入(不推荐)
import . "fmt"
Println("hello") // 直接使用
// 仅初始化
import _ "image/png" // 执行init函数
// 可见性:大写=导出(public),小写=未导出(private)
type PublicStruct struct {
ExportedField int // 导出
unexportedField int // 未导出
}
init函数
包初始化时自动执行,可有多个。
var config Config
func init() {
// 包初始化
loadConfig()
}
func init() {
// 可以有多个init
setupDatabase()
}
// 执行顺序:
// 1. 导入包的init
// 2. 当前包变量初始化
// 3. 当前包init
// 4. main函数
类型系统
类型定义
// type定义新类型
type MyInt int
var x MyInt = 10
var y int = 10
// x = y // 错误!不同类型
// type别名(Go 1.9+)
type MyInt2 = int
var a MyInt2 = 10
var b int = 10
a = b // OK,相同类型
类型嵌入
// 嵌入结构体
type Engine struct {
Power int
}
func (e *Engine) Start() {
fmt.Println("Engine started")
}
type Car struct {
Engine // 嵌入,继承Engine的字段和方法
Brand string
}
car := Car{Engine: Engine{Power: 200}, Brand: "Toyota"}
car.Power // 200(提升字段)
car.Start() // "Engine started"(提升方法)
// 嵌入接口
type ReadWriter struct {
Reader
Writer
}
常见陷阱
// 1. range循环变量陷阱
for i, v := range slice {
go func() {
fmt.Println(i, v) // 错误!闭包捕获变量,值可能变化
}()
}
// 正确:传参
for i, v := range slice {
go func(i, v int) {
fmt.Println(i, v)
}(i, v)
}
// 2. slice共享底层数组
arr := []int{1, 2, 3, 4, 5}
s1 := arr[0:2] // [1, 2]
s2 := arr[1:3] // [2, 3]
s1[1] = 999 // s2[0]也变成999(共享数组)
// 3. map并发读写panic
m := make(map[string]int)
go func() { m["key"] = 1 }() // 写
go func() { _ = m["key"] }() // 读(panic!)
// 使用sync.Map或加锁
// 4. nil map不能写入
var m map[string]int // nil
// m["key"] = 1 // panic
m = make(map[string]int) // 必须初始化
// 5. slice作为函数参数
func modify(s []int) {
s[0] = 999 // 影响原slice(引用类型)
s = append(s, 100) // 不影响原slice(可能重新分配)
}
最佳实践
短变量声明:函数内优先用
:=错误处理:每个error都检查
defer清理资源:文件、锁、连接
指针接收者:修改对象或大对象
命名返回值:复杂函数增强可读性
channel通信:不要通过共享内存通信,通过通信共享内存
select处理多channel:超时、取消、多路复用
空接口慎用:丧失类型安全
gofmt格式化:自动化,无争议
接口小而专注:io.Reader只有一个方法
核心: Go追求简洁和并发,利用goroutine和channel构建高并发系统。