Golang基础教程
从零开始学习Go语言的基础语法和核心概念
第1章:环境安装与配置
1.1 下载Go
访问Go官方网站 golang.org 下载适合你操作系统的Go安装包。Go支持Windows、macOS和Linux系统。
# 验证Go是否安装成功
go version
# 输出示例
go version go1.23.0 linux/amd64
1.2 配置环境变量
Go需要配置两个重要的环境变量:GOROOT和GOPATH。
Linux/macOS:
# 在 ~/.bashrc 或 ~/.zshrc 中添加
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
Windows:
在系统环境变量中添加GOROOT和GOPATH
1.3 第一个Go程序
创建你的第一个Go程序,经典的"Hello, World!"。
package main
import "fmt"
func main() {
fmt.Println("Hello, Golang!")
fmt.Println("欢迎使用Go Master学习平台")
}
运行程序:
# 保存为 main.go
go run main.go
# 或者编译后运行
go build main.go
./main
第2章:基础语法
2.1 变量声明
Go语言提供了多种变量声明方式,包括var关键字和简短变量声明。
package main
import "fmt"
func main() {
// 1. 使用var关键字声明
var name string = "Golang"
var age int = 15
var isOpen bool = true
// 2. 类型推断
var language = "Go"
var version = 1.23
// 3. 简短变量声明(只能在函数内部使用)
message := "Hello, World!"
count := 42
// 4. 多变量声明
var (
author string = "Google"
year int = 2009
license string = "BSD"
)
fmt.Printf("语言: %s, 版本: %.2f\n", language, version)
fmt.Printf("作者: %s, 年份: %d\n", author, year)
}
2.2 常量
常量使用const关键字声明,一旦赋值不能修改。
package main
import "fmt"
func main() {
// 单行常量
const Pi = 3.14159
const AppName = "Go Master"
// 多行常量
const (
StatusActive = "active"
StatusInactive = "inactive"
StatusPending = "pending"
)
// iota枚举
const (
Monday = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
fmt.Printf("应用名称: %s\n", AppName)
fmt.Printf("圆周率: %.5f\n", Pi)
fmt.Printf("星期: %d\n", Wednesday) // 输出: 2
}
2.3 数据类型
Go语言提供了丰富的数据类型,包括基本类型和复合类型。
基本类型:
- • 整数:int, int8, int16, int32, int64
- • 无符号整数:uint, uint8, uint16, uint32, uint64
- • 浮点数:float32, float64
- • 布尔:bool
- • 字符串:string
- • 字符:byte (uint8), rune (int32)
复合类型:
- • 数组:Array
- • 切片:Slice
- • 映射:Map
- • 结构体:Struct
- • 接口:Interface
- • 通道:Channel
package main
import "fmt"
func main() {
// 整数类型
var i8 int8 = 127
var i16 int16 = 32767
var i32 int32 = 2147483647
var i64 int64 = 9223372036854775807
// 无符号整数
var ui8 uint8 = 255
var ui16 uint16 = 65535
// 浮点数
var f32 float32 = 3.14
var f64 float64 = 3.141592653589793
// 布尔类型
var isTrue bool = true
var isFalse bool = false
// 字符串
var str string = "Hello, 世界"
// 字符类型
var b byte = 'A' // 等同于 uint8
var r rune = '中' // 等同于 int32
fmt.Printf("int8: %d, uint8: %d\n", i8, ui8)
fmt.Printf("float32: %.2f, float64: %.15f\n", f32, f64)
fmt.Printf("bool: %v, %v\n", isTrue, isFalse)
fmt.Printf("string: %s\n", str)
fmt.Printf("byte: %c, rune: %c\n", b, r)
}
第3章:控制结构
3.1 if语句
Go的if语句支持在条件判断前执行简单语句。
package main
import "fmt"
func main() {
age := 25
// 基本if语句
if age >= 18 {
fmt.Println("成年人")
} else {
fmt.Println("未成年")
}
// if-else if-else
if age < 13 {
fmt.Println("儿童")
} else if age < 18 {
fmt.Println("青少年")
} else if age < 60 {
fmt.Println("成年人")
} else {
fmt.Println("老年人")
}
// 在if前执行语句
if x := 10; x > 5 {
fmt.Printf("%d 大于 5\n", x)
}
// 多个条件
score := 85
if score >= 90 && score <= 100 {
fmt.Println("优秀")
} else if score >= 80 && score < 90 {
fmt.Println("良好")
} else if score >= 60 && score < 80 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
}
3.2 for循环
Go只有for循环,没有while循环,但for可以实现各种循环模式。
package main
import "fmt"
func main() {
// 1. 传统for循环
for i := 0; i < 5; i++ {
fmt.Printf("i = %d\n", i)
}
// 2. 类似while的循环
j := 0
for j < 3 {
fmt.Printf("j = %d\n", j)
j++
}
// 3. 无限循环
counter := 0
for {
if counter >= 3 {
break
}
fmt.Printf("counter = %d\n", counter)
counter++
}
// 4. range循环(数组、切片、映射)
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
fmt.Printf("索引: %d, 值: %d\n", index, value)
}
// 5. 只获取值
for _, value := range numbers {
fmt.Printf("值: %d\n", value)
}
// 6. 映射遍历
m := map[string]int{"a": 1, "b": 2, "c": 3}
for key, value := range m {
fmt.Printf("键: %s, 值: %d\n", key, value)
}
}
3.3 switch语句
Go的switch语句非常灵活,可以没有表达式,自动break。
package main
import "fmt"
func main() {
day := "Monday"
// 基本switch
switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("工作日")
case "Saturday", "Sunday":
fmt.Println("周末")
default:
fmt.Println("无效的日期")
}
// 没有表达式的switch(相当于if-else)
score := 85
switch {
case score >= 90:
fmt.Println("优秀")
case score >= 80:
fmt.Println("良好")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
// 带条件的switch
switch x := 10; {
case x > 0 && x < 10:
fmt.Println("个位数正数")
case x >= 10 && x < 100:
fmt.Println("两位数正数")
default:
fmt.Println("其他数字")
}
// fallthrough(继续执行下一个case)
num := 2
switch num {
case 1:
fmt.Println("一")
fallthrough
case 2:
fmt.Println("二")
fallthrough
case 3:
fmt.Println("三")
default:
fmt.Println("其他")
}
}
第4章:函数
4.1 函数定义与调用
函数是Go语言的核心,支持多返回值和命名返回值。
package main
import "fmt"
// 基本函数
func greet(name string) {
fmt.Printf("Hello, %s!\n", name)
}
// 带返回值的函数
func add(a, b int) int {
return a + b
}
// 多返回值
func calculate(a, b int) (int, int, int) {
sum := a + b
difference := a - b
product := a * b
return sum, difference, product
}
// 命名返回值
func divmod(a, b int) (quotient, remainder int) {
quotient = a / b
remainder = a % b
return // 隐式返回
}
// 可变参数
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
func main() {
// 函数调用
greet("Go开发者")
result := add(10, 20)
fmt.Printf("10 + 20 = %d\n", result)
sum, diff, prod := calculate(15, 5)
fmt.Printf("和: %d, 差: %d, 积: %d\n", sum, diff, prod)
q, r := divmod(17, 3)
fmt.Printf("商: %d, 余数: %d\n", q, r)
total := sum(1, 2, 3, 4, 5)
fmt.Printf("总和: %d\n", total)
}
4.2 函数作为参数
Go语言支持高阶函数,可以将函数作为参数传递。
package main
import "fmt"
// 定义函数类型
type Operation func(int, int) int
// 加法
func add(a, b int) int {
return a + b
}
// 减法
func subtract(a, b int) int {
return a - b
}
// 乘法
func multiply(a, b int) int {
return a * b
}
// 高阶函数
func calculate(a, b int, op Operation) int {
return op(a, b)
}
// 返回函数的函数
func getOperation(name string) Operation {
switch name {
case "add":
return add
case "subtract":
return subtract
case "multiply":
return multiply
default:
return nil
}
}
func main() {
// 直接调用
result1 := calculate(10, 5, add)
fmt.Printf("10 + 5 = %d\n", result1)
result2 := calculate(10, 5, subtract)
fmt.Printf("10 - 5 = %d\n", result2)
// 从函数获取操作
op := getOperation("multiply")
if op != nil {
result3 := calculate(10, 5, op)
fmt.Printf("10 * 5 = %d\n", result3)
}
// 匿名函数
divide := func(a, b int) int {
return a / b
}
result4 := calculate(10, 5, divide)
fmt.Printf("10 / 5 = %d\n", result4)
}
4.3 闭包
闭包是函数和其引用的外部变量的组合。
package main
import "fmt"
// 返回一个闭包
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
// 记忆函数
func memoize(f func(int) int) func(int) int {
cache := make(map[int]int)
return func(n int) int {
if val, ok := cache[n]; ok {
return val
}
result := f(n)
cache[n] = result
return result
}
}
// 斐波那契数列
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
func main() {
// 使用闭包
counter1 := counter()
fmt.Println(counter1()) // 1
fmt.Println(counter1()) // 2
fmt.Println(counter1()) // 3
counter2 := counter()
fmt.Println(counter2()) // 1
fmt.Println(counter2()) // 2
// 使用记忆化
memoFib := memoize(fibonacci)
fmt.Println(memoFib(10)) // 第一次计算
fmt.Println(memoFib(10)) // 第二次从缓存读取
fmt.Println(memoFib(20)) // 新的计算
}
第5章:数组与切片
5.1 数组
数组是固定长度的同类型元素序列。
package main
import "fmt"
func main() {
// 声明数组
var numbers [5]int
numbers[0] = 1
numbers[1] = 2
numbers[2] = 3
numbers[3] = 4
numbers[4] = 5
// 数组字面量
var fruits = [5]string{"apple", "banana", "orange", "grape", "pear"}
// 让编译器推断长度
colors := [...]string{"red", "blue", "green", "yellow"}
// 访问数组元素
fmt.Printf("第一个水果: %s\n", fruits[0])
fmt.Printf("第三个颜色: %s\n", colors[2])
// 数组长度
fmt.Printf("数字数组长度: %d\n", len(numbers))
fmt.Printf("水果数组长度: %d\n", len(fruits))
// 遍历数组
fmt.Println("所有水果:")
for i, fruit := range fruits {
fmt.Printf(" %d: %s\n", i, fruit)
}
// 多维数组
var matrix [3][3]int
matrix[0][0] = 1
matrix[1][1] = 2
matrix[2][2] = 3
fmt.Println("矩阵:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf("%d ", matrix[i][j])
}
fmt.Println()
}
}
5.2 切片
切片是动态数组,是Go中最常用的数据结构。
package main
import "fmt"
func main() {
// 创建切片
var slice1 []int
fmt.Printf("空切片: %v, 长度: %d\n", slice1, len(slice1))
// 使用make创建切片
slice2 := make([]int, 5) // 长度为5,容量为5
slice3 := make([]int, 3, 10) // 长度为3,容量为10
fmt.Printf("slice2: %v, len: %d, cap: %d\n", slice2, len(slice2), cap(slice2))
fmt.Printf("slice3: %v, len: %d, cap: %d\n", slice3, len(slice3), cap(slice3))
// 切片字面量
numbers := []int{1, 2, 3, 4, 5}
fmt.Printf("数字切片: %v\n", numbers)
// 从数组创建切片
arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice4 := arr[2:7] // 从索引2到6
fmt.Printf("从数组切片: %v\n", slice4)
// 追加元素
slice5 := []int{1, 2, 3}
slice5 = append(slice5, 4, 5, 6)
fmt.Printf("追加后: %v\n", slice5)
// 追加切片
slice6 := []int{7, 8, 9}
slice5 = append(slice5, slice6...)
fmt.Printf("追加切片后: %v\n", slice5)
// 复制切片
source := []int{1, 2, 3, 4, 5}
dest := make([]int, len(source))
copy(dest, source)
fmt.Printf("复制后: %v\n", dest)
// 删除元素
slice7 := []int{1, 2, 3, 4, 5}
// 删除索引为2的元素
slice7 = append(slice7[:2], slice7[3:]...)
fmt.Printf("删除元素后: %v\n", slice7)
// 遍历切片
fmt.Println("遍历切片:")
for i, val := range numbers {
fmt.Printf(" 索引 %d: %d\n", i, val)
}
}
5.3 切片操作技巧
掌握切片的常用操作和最佳实践。
package main
import "fmt"
// 过滤切片
func filter(slice []int, predicate func(int) bool) []int {
var result []int
for _, val := range slice {
if predicate(val) {
result = append(result, val)
}
}
return result
}
// 映射切片
func mapSlice(slice []int, transform func(int) int) []int {
result := make([]int, len(slice))
for i, val := range slice {
result[i] = transform(val)
}
return result
}
// 反转切片
func reverse(slice []int) []int {
result := make([]int, len(slice))
for i, j := 0, len(slice)-1; i < len(slice); i, j = i+1, j-1 {
result[i] = slice[j]
}
return result
}
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 过滤偶数
evens := filter(numbers, func(n int) bool {
return n%2 == 0
})
fmt.Printf("偶数: %v\n", evens)
// 过滤奇数
odds := filter(numbers, func(n int) bool {
return n%2 != 0
})
fmt.Printf("奇数: %v\n", odds)
// 映射:所有数字乘以2
doubled := mapSlice(numbers, func(n int) int {
return n * 2
})
fmt.Printf("乘以2: %v\n", doubled)
// 反转切片
reversed := reverse(numbers)
fmt.Printf("反转: %v\n", reversed)
// 切片排序
unsorted := []int{5, 2, 8, 1, 9, 3}
// 使用sort包排序
fmt.Printf("排序前: %v\n", unsorted)
// 注意:需要导入 sort 包
// import "sort"
// sort.Ints(unsorted)
// fmt.Printf("排序后: %v\n", unsorted)
}
第6章:映射(Map)
6.1 Map基础操作
Map是Go语言中的键值对数据结构,类似于其他语言的字典或哈希表。
package main
import "fmt"
func main() {
// 创建map
var m1 map[string]int
fmt.Printf("空map: %v\n", m1)
// 使用make创建map
m2 := make(map[string]int)
m3 := make(map[string]int, 10) // 指定初始容量
// Map字面量
m4 := map[string]int{
"apple": 5,
"banana": 3,
"orange": 8,
}
fmt.Printf("m2: %v\n", m2)
fmt.Printf("m4: %v\n", m4)
// 添加和修改元素
m4["grape"] = 12
m4["apple"] = 10
fmt.Printf("修改后: %v\n", m4)
// 访问元素
value := m4["apple"]
fmt.Printf("苹果数量: %d\n", value)
// 检查键是否存在
value, exists := m4["pear"]
if exists {
fmt.Printf("梨数量: %d\n", value)
} else {
fmt.Println("梨不存在")
}
// 删除元素
delete(m4, "banana")
fmt.Printf("删除后: %v\n", m4)
// 获取map长度
fmt.Printf("Map长度: %d\n", len(m4))
// 遍历map
fmt.Println("遍历map:")
for key, value := range m4 {
fmt.Printf(" %s: %d\n", key, value)
}
}
6.2 Map高级用法
Map可以存储复杂的数据类型,包括结构体、切片等。
package main
import "fmt"
// 学生信息
type Student struct {
Name string
Age int
Grades map[string]float64
}
func main() {
// Map存储结构体
students := make(map[string]Student)
students["Alice"] = Student{
Name: "Alice",
Age: 20,
Grades: map[string]float64{
"Math": 95.5,
"English": 88.0,
"Science": 92.5,
},
}
students["Bob"] = Student{
Name: "Bob",
Age: 21,
Grades: map[string]float64{
"Math": 78.5,
"English": 85.0,
"Science": 80.0,
},
}
// 访问嵌套map
alice := students["Alice"]
fmt.Printf("Alice的数学成绩: %.1f\n", alice.Grades["Math"])
// Map存储切片
phoneBook := make(map[string][]string)
phoneBook["Alice"] = []string{"123-456", "789-012"}
phoneBook["Bob"] = []string{"456-789"}
fmt.Printf("Alice的所有电话: %v\n", phoneBook["Alice"])
// Map作为集合使用
visited := make(map[string]bool)
cities := []string{"Beijing", "Shanghai", "Beijing", "Guangzhou", "Shanghai"}
for _, city := range cities {
if !visited[city] {
visited[city] = true
fmt.Printf("首次访问: %s\n", city)
}
}
// 统计单词频率
text := "hello world hello go world"
words := make(map[string]int)
for _, word := range []string{"hello", "world", "hello", "go", "world"} {
words[word]++
}
fmt.Println("单词频率:")
for word, count := range words {
fmt.Printf(" %s: %d\n", word, count)
}
}
6.3 Map与JSON
Map与JSON的相互转换是Web开发中的常见操作。
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
// Map转JSON
data := map[string]interface{}{
"name": "Alice",
"age": 30,
"email": "alice@example.com",
"hobbies": []string{"reading", "swimming", "coding"},
"address": map[string]string{
"city": "Beijing",
"country": "China",
},
}
// 编码为JSON
jsonData, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
fmt.Printf("JSON: %s\n", jsonData)
// JSON转Map
var decodedData map[string]interface{}
err = json.Unmarshal(jsonData, &decodedData)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解码后的Map: %v\n", decodedData)
// 访问解码后的数据
fmt.Printf("姓名: %v\n", decodedData["name"])
fmt.Printf("年龄: %v\n", decodedData["age"])
// 处理嵌套数据
if address, ok := decodedData["address"].(map[string]interface{}); ok {
fmt.Printf("城市: %v\n", address["city"])
}
// 处理数组数据
if hobbies, ok := decodedData["hobbies"].([]interface{}); ok {
fmt.Println("爱好:")
for _, hobby := range hobbies {
fmt.Printf(" - %v\n", hobby)
}
}
}
第7章:结构体
7.1 结构体定义与使用
结构体是Go语言中的自定义数据类型,用于组合多个字段。
package main
import "fmt"
// 定义结构体
type Person struct {
Name string
Age int
}
// 带有更多字段的结构体
type Student struct {
Name string
Age int
Grade float64
Subjects []string
Address *Address // 指针字段
}
type Address struct {
City string
Country string
}
func main() {
// 创建结构体实例
var p1 Person
p1.Name = "Alice"
p1.Age = 25
// 结构体字面量
p2 := Person{
Name: "Bob",
Age: 30,
}
// 简写形式(按字段顺序)
p3 := Person{"Charlie", 35}
fmt.Printf("p1: %+v\n", p1)
fmt.Printf("p2: %+v\n", p2)
fmt.Printf("p3: %+v\n", p3)
// 访问结构体字段
fmt.Printf("%s 的年龄是 %d\n", p1.Name, p1.Age)
// 创建复杂结构体
student := Student{
Name: "David",
Age: 20,
Grade: 89.5,
Subjects: []string{"Math", "English", "Science"},
Address: &Address{
City: "Beijing",
Country: "China",
},
}
fmt.Printf("学生信息: %+v\n", student)
fmt.Printf("城市: %s\n", student.Address.City)
// 结构体比较
p4 := Person{Name: "Alice", Age: 25}
if p1 == p4 {
fmt.Println("p1 和 p4 相等")
}
// 结构体数组
people := []Person{
{Name: "Eve", Age: 28},
{Name: "Frank", Age: 32},
{Name: "Grace", Age: 27},
}
fmt.Println("所有人:")
for _, person := range people {
fmt.Printf(" %s (%d岁)\n", person.Name, person.Age)
}
}
7.2 方法定义
方法是与特定类型关联的函数,可以为任何类型定义方法。
package main
import (
"fmt"
"math"
)
// 定义结构体
type Rectangle struct {
Width float64
Height float64
}
type Circle struct {
Radius float64
}
// 值接收者方法
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// 指针接收者方法
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
// Circle的方法
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// 字符串表示方法
func (r Rectangle) String() string {
return fmt.Sprintf("矩形[宽:%.2f, 高:%.2f]", r.Width, r.Height)
}
func (c Circle) String() string {
return fmt.Sprintf("圆形[半径:%.2f]", c.Radius)
}
// 自定义类型方法
type MyInt int
func (mi MyInt) IsEven() bool {
return mi%2 == 0
}
func (mi MyInt) IsPositive() bool {
return mi > 0
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
circle := Circle{Radius: 3}
// 调用方法
fmt.Printf("%s 面积: %.2f\n", rect, rect.Area())
fmt.Printf("%s 周长: %.2f\n", rect, rect.Perimeter())
fmt.Printf("%s 面积: %.2f\n", circle, circle.Area())
fmt.Printf("%s 周长: %.2f\n", circle, circle.Perimeter())
// 修改结构体
fmt.Printf("缩放前: %s\n", rect)
rect.Scale(2)
fmt.Printf("缩放后: %s\n", rect)
// 自定义类型方法
num := MyInt(42)
fmt.Printf("%d 是偶数: %v\n", num, num.IsEven())
fmt.Printf("%d 是正数: %v\n", num, num.IsPositive())
// 方法值和方法表达式
areaMethod := rect.Area
fmt.Printf("使用方法值: %.2f\n", areaMethod())
scaleMethod := (*Rectangle).Scale
scaleMethod(&rect, 0.5)
fmt.Printf("使用方法表达式: %s\n", rect)
}
7.3 结构体嵌套与组合
Go语言通过结构体嵌套和组合来实现代码复用。
package main
import "fmt"
// 基础结构体
type Person struct {
Name string
Age int
}
// 嵌套结构体
type Employee struct {
Person // 匿名字段,继承Person的所有字段和方法
EmployeeID string
Department string
Salary float64
}
// 显式嵌套
type Student struct {
Person Person // 命名字段
StudentID string
Major string
GPA float64
}
// 方法定义
func (p Person) GetInfo() string {
return fmt.Sprintf("%s (%d岁)", p.Name, p.Age)
}
func (e Employee) GetEmployeeInfo() string {
return fmt.Sprintf("员工 %s, 部门: %s, 薪资: %.2f",
e.Name, e.Department, e.Salary)
}
func main() {
// 匿名字段嵌套
emp := Employee{
Person: Person{
Name: "Alice",
Age: 30,
},
EmployeeID: "E001",
Department: "Engineering",
Salary: 80000.0,
}
// 可以直接访问嵌套字段
fmt.Printf("员工姓名: %s\n", emp.Name)
fmt.Printf("员工年龄: %d\n", emp.Age)
// 也可以显式访问
fmt.Printf("员工姓名: %s\n", emp.Person.Name)
// 调用嵌套结构体的方法
fmt.Printf("基本信息: %s\n", emp.GetInfo())
fmt.Printf("员工信息: %s\n", emp.GetEmployeeInfo())
// 命名字段嵌套
student := Student{
Person: Person{
Name: "Bob",
Age: 22,
},
StudentID: "S001",
Major: "Computer Science",
GPA: 3.8,
}
fmt.Printf("学生姓名: %s\n", student.Person.Name)
fmt.Printf("专业: %s\n", student.Major)
// 结构体字面量简写
emp2 := Employee{
Person: Person{Name: "Charlie", Age: 35},
EmployeeID: "E002",
Department: "Marketing",
Salary: 70000.0,
}
fmt.Printf("新员工: %s\n", emp2.GetEmployeeInfo())
// 多层嵌套
type Company struct {
Name string
Employees []Employee
Address Address
}
company := Company{
Name: "Tech Corp",
Employees: []Employee{emp, emp2},
Address: Address{
City: "San Francisco",
Country: "USA",
},
}
fmt.Printf("公司名称: %s\n", company.Name)
fmt.Printf("员工数量: %d\n", len(company.Employees))
fmt.Printf("总部: %s, %s\n", company.Address.City, company.Address.Country)
}
第8章:接口
8.1 接口基础
接口是Go语言中的类型,它定义了一组方法签名。任何实现了这些方法的类型都实现了该接口。
package main
import "fmt"
// 定义接口
type Shape interface {
Area() float64
Perimeter() float64
}
// 定义结构体
type Rectangle struct {
Width float64
Height float64
}
type Circle struct {
Radius float64
}
// Rectangle实现Shape接口
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// Circle实现Shape接口
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * 3.14159 * c.Radius
}
// 使用接口的函数
func printShapeInfo(s Shape) {
fmt.Printf("面积: %.2f\n", s.Area())
fmt.Printf("周长: %.2f\n", s.Perimeter())
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
circle := Circle{Radius: 3}
// 结构体实例作为接口使用
var shape Shape
shape = rect
fmt.Println("矩形信息:")
printShapeInfo(shape)
shape = circle
fmt.Println("\n圆形信息:")
printShapeInfo(shape)
// 接口切片
shapes := []Shape{rect, circle}
fmt.Println("\n所有形状:")
for i, shape := range shapes {
fmt.Printf("形状 %d:\n", i+1)
printShapeInfo(shape)
fmt.Println()
}
// 空接口
var anything interface{}
anything = 42
fmt.Printf("anything = %v (%T)\n", anything, anything)
anything = "Hello"
fmt.Printf("anything = %v (%T)\n", anything, anything)
anything = Rectangle{Width: 1, Height: 2}
fmt.Printf("anything = %v (%T)\n", anything, anything)
}
8.2 接口组合与空接口
接口可以组合其他接口,空接口可以表示任何类型。
package main
import "fmt"
// 基础接口
type Writer interface {
Write([]byte) (int, error)
}
type Reader interface {
Read([]byte) (int, error)
}
// 组合接口
type ReadWriter interface {
Reader
Writer
}
// 空接口
type Empty interface{}
// 实现类型
type File struct {
Name string
}
func (f File) Read(b []byte) (int, error) {
fmt.Printf("从文件 %s 读取数据\n", f.Name)
return len(b), nil
}
func (f File) Write(b []byte) (int, error) {
fmt.Printf("向文件 %s 写入数据\n", f.Name)
return len(b), nil
}
// 使用空接口的函数
func printAnything(v interface{}) {
fmt.Printf("值: %v, 类型: %T\n", v, v)
}
// 类型断言
func checkType(v interface{}) {
switch val := v.(type) {
case int:
fmt.Printf("这是一个整数: %d\n", val)
case string:
fmt.Printf("这是一个字符串: %s\n", val)
case bool:
fmt.Printf("这是一个布尔值: %v\n", val)
case Rectangle:
fmt.Printf("这是一个矩形: %+v\n", val)
default:
fmt.Printf("未知类型: %T\n", val)
}
}
// 类型查询
func describe(i interface{}) {
// 类型断言
if str, ok := i.(string); ok {
fmt.Printf("字符串: %s\n", str)
}
// 类型switch
switch v := i.(type) {
case int, int32, int64:
fmt.Printf("整数: %d\n", v)
case float32, float64:
fmt.Printf("浮点数: %f\n", v)
case string:
fmt.Printf("字符串: %s\n", v)
default:
fmt.Printf("其他类型: %T\n", v)
}
}
type Rectangle struct {
Width float64
Height float64
}
func main() {
file := File{Name: "example.txt"}
// 使用组合接口
var rw ReadWriter = file
data := []byte("Hello, World!")
rw.Read(data)
rw.Write(data)
// 空接口使用
fmt.Println("\n空接口示例:")
printAnything(42)
printAnything("Hello")
printAnything(true)
printAnything(Rectangle{Width: 10, Height: 5})
// 类型断言
fmt.Println("\n类型断言示例:")
checkType(42)
checkType("Hello")
checkType(true)
checkType(Rectangle{Width: 10, Height: 5})
// 类型查询
fmt.Println("\n类型查询示例:")
describe(42)
describe(3.14)
describe("Hello")
describe([]int{1, 2, 3})
}
8.3 类型断言与类型查询
类型断言和类型查询是在运行时检查接口变量具体类型的方法。
package main
import (
"fmt"
"strconv"
)
// 类型断言示例
func assertType(i interface{}) {
// 方式1: 简单断言(可能panic)
// str := i.(string)
// 方式2: 安全断言
if str, ok := i.(string); ok {
fmt.Printf("字符串: %s\n", str)
} else {
fmt.Printf("不是字符串,实际类型: %T\n", i)
}
// 方式3: 类型switch(推荐)
switch v := i.(type) {
case string:
fmt.Printf("字符串类型: %s\n", v)
case int:
fmt.Printf("整数类型: %d\n", v)
case float64:
fmt.Printf("浮点数类型: %f\n", v)
case bool:
fmt.Printf("布尔类型: %v\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
}
// 接口转换函数
func convertToString(v interface{}) (string, error) {
switch val := v.(type) {
case string:
return val, nil
case int:
return strconv.Itoa(val), nil
case float64:
return strconv.FormatFloat(val, 'f', -1, 64), nil
case bool:
return strconv.FormatBool(val), nil
default:
return "", fmt.Errorf("unsupported type: %T", v)
}
}
// 接口切片处理
func processSlice(slice []interface{}) {
for i, item := range slice {
fmt.Printf("索引 %d: ", i)
switch v := item.(type) {
case int:
fmt.Printf("整数 %d 的平方: %d\n", v, v*v)
case string:
fmt.Printf("字符串 '%s' 的长度: %d\n", v, len(v))
case []int:
sum := 0
for _, num := range v {
sum += num
}
fmt.Printf("整数数组的和: %d\n", sum)
default:
fmt.Printf("未知类型: %T\n", v)
}
}
}
func main() {
// 类型断言示例
fmt.Println("类型断言示例:")
assertType("Hello")
assertType(42)
assertType(3.14)
assertType(true)
// 接口转换
fmt.Println("\n接口转换示例:")
values := []interface{}{"Hello", 42, 3.14, true}
for _, v := range values {
str, err := convertToString(v)
if err != nil {
fmt.Printf("转换失败: %v\n", err)
} else {
fmt.Printf("转换结果: %s\n", str)
}
}
// 处理接口切片
fmt.Println("\n接口切片处理:")
mixedSlice := []interface{}{
1, 2, 3,
"hello", "world",
[]int{4, 5, 6},
3.14,
}
processSlice(mixedSlice)
// 接口嵌套
fmt.Println("\n接口嵌套示例:")
var nested interface{}
nested = map[string]interface{}{
"name": "Alice",
"age": 30,
"skills": []string{"Go", "JavaScript", "Python"},
}
if m, ok := nested.(map[string]interface{}); ok {
fmt.Printf("姓名: %v\n", m["name"])
if skills, ok := m["skills"].([]interface{}); ok {
fmt.Printf("技能: %v\n", skills)
}
}
}
第9章:错误处理
9.1 Error接口
Go语言使用error接口来表示错误,error是一个内置接口类型。
package main
import (
"errors"
"fmt"
"strconv"
)
// 自定义错误类型
type MyError struct {
Code int
Message string
}
func (e MyError) Error() string {
return fmt.Sprintf("错误代码 %d: %s", e.Code, e.Message)
}
// 返回错误的函数
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
// 使用自定义错误
func validateAge(age int) error {
if age < 0 {
return MyError{Code: 400, Message: "年龄不能为负数"}
}
if age > 150 {
return MyError{Code: 400, Message: "年龄超出合理范围"}
}
return nil
}
// 包装错误
func readConfig() error {
err := readFile("config.txt")
if err != nil {
return fmt.Errorf("读取配置文件失败: %w", err)
}
return nil
}
func readFile(filename string) error {
return errors.New("文件不存在")
}
func main() {
// 基本错误处理
result, err := divide(10, 2)
if err != nil {
fmt.Printf("错误: %v\n", err)
} else {
fmt.Printf("结果: %.2f\n", result)
}
// 错误处理
result, err = divide(10, 0)
if err != nil {
fmt.Printf("错误: %v\n", err)
}
// 自定义错误
err = validateAge(-5)
if err != nil {
fmt.Printf("验证错误: %v\n", err)
// 类型断言获取详细信息
if myErr, ok := err.(MyError); ok {
fmt.Printf("错误代码: %d\n", myErr.Code)
}
}
// 包装错误
err = readConfig()
if err != nil {
fmt.Printf("配置错误: %v\n", err)
// 使用errors.Is和errors.As
var myErr MyError
if errors.As(err, &myErr) {
fmt.Printf("自定义错误: %v\n", myErr)
}
}
// 字符串转换错误处理
str := "123"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Printf("转换错误: %v\n", err)
} else {
fmt.Printf("转换结果: %d\n", num)
}
// 错误的错误处理示例
str2 := "abc"
num2, err := strconv.Atoi(str2)
if err != nil {
fmt.Printf("转换 '%s' 失败: %v\n", str2, err)
num2 = 0 // 使用默认值
}
fmt.Printf("使用默认值: %d\n", num2)
}
9.2 defer语句
defer语句用于延迟函数的执行,常用于资源清理和错误处理。
package main
import (
"fmt"
"os"
)
// defer基础用法
func deferExample() {
fmt.Println("开始")
defer fmt.Println("延迟执行1")
defer fmt.Println("延迟执行2")
fmt.Println("结束")
// 输出顺序:开始 -> 结束 -> 延迟执行2 -> 延迟执行1
}
// defer与资源管理
func fileOperation(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close() // 确保文件在函数结束时关闭
// 处理文件...
fmt.Printf("处理文件: %s\n", filename)
return nil
}
// defer与匿名函数
func anonymousDefer() {
x := 10
defer func() {
fmt.Printf("x的值: %d\n", x) // 捕获变量
}()
x = 20
fmt.Printf("修改后的x: %d\n", x)
}
// defer参数求值
func deferParameter() {
x := 10
defer fmt.Printf("defer x = %d\n", x) // 参数在defer时求值
x = 20
fmt.Printf("x = %d\n", x)
}
// 多个defer的执行顺序
func multipleDefer() {
fmt.Println("函数开始")
for i := 1; i <= 5; i++ {
defer fmt.Printf("defer %d\n", i)
}
fmt.Println("函数结束")
// defer按LIFO顺序执行
}
// defer与返回值
func deferWithReturn() (result int) {
defer func() {
result++ // 可以修改命名返回值
}()
return 10 // 实际返回11
}
// 错误处理模式
func processResource(id string) error {
resource, err := acquireResource(id)
if err != nil {
return fmt.Errorf("获取资源失败: %w", err)
}
// 使用defer确保资源释放
defer func() {
if err := releaseResource(resource); err != nil {
fmt.Printf("释放资源失败: %v\n", err)
}
}()
// 处理资源
if err := doWork(resource); err != nil {
return fmt.Errorf("处理资源失败: %w", err)
}
return nil
}
// 模拟函数
func acquireResource(id string) (string, error) {
if id == "error" {
return "", fmt.Errorf("资源不存在")
}
return "resource-" + id, nil
}
func releaseResource(resource string) error {
fmt.Printf("释放资源: %s\n", resource)
return nil
}
func doWork(resource string) error {
fmt.Printf("处理资源: %s\n", resource)
return nil
}
func main() {
fmt.Println("=== defer基础示例 ===")
deferExample()
fmt.Println("\n=== defer参数求值 ===")
deferParameter()
fmt.Println("\n=== 多个defer ===")
multipleDefer()
fmt.Println("\n=== defer与返回值 ===")
result := deferWithReturn()
fmt.Printf("返回值: %d\n", result)
fmt.Println("\n=== 资源管理示例 ===")
if err := fileOperation("test.txt"); err != nil {
fmt.Printf("文件操作失败: %v\n", err)
}
fmt.Println("\n=== 错误处理模式 ===")
if err := processResource("123"); err != nil {
fmt.Printf("处理失败: %v\n", err)
}
if err := processResource("error"); err != nil {
fmt.Printf("处理失败: %v\n", err)
}
fmt.Println("\n=== 匿名函数defer ===")
anonymousDefer()
}
9.3 panic与recover
panic用于抛出异常,recover用于捕获异常,应该谨慎使用。
package main
import "fmt"
// panic示例
func panicExample() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("捕获到panic: %v\n", r)
}
}()
fmt.Println("开始执行")
panic("发生严重错误")
fmt.Println("这行不会执行")
}
// 安全的除法函数
func safeDivide(a, b float64) (result float64, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("除法运算panic: %v", r)
}
}()
if b == 0 {
panic("除数为零")
}
result = a / b
return result, nil
}
// 检查函数
func checkValue(v int) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("检查值时发生panic: %v\n", r)
}
}()
if v < 0 {
panic("值不能为负数")
}
if v > 100 {
panic("值不能大于100")
}
fmt.Printf("值 %d 有效\n", v)
}
// 资源清理示例
func processWithCleanup() {
fmt.Println("开始处理")
defer func() {
fmt.Println("清理资源")
if r := recover(); r != nil {
fmt.Printf("处理过程中发生panic: %v\n", r)
fmt.Println("执行错误恢复逻辑")
}
}()
// 模拟处理步骤
fmt.Println("步骤1: 验证输入")
fmt.Println("步骤2: 处理数据")
// 模拟错误
panic("数据处理失败")
fmt.Println("步骤3: 保存结果")
}
// 自定义panic类型
type MyPanic struct {
Code int
Message string
}
func (p MyPanic) String() string {
return fmt.Sprintf("MyPanic[%d]: %s", p.Code, p.Message)
}
func customPanicExample() {
defer func() {
if r := recover(); r != nil {
switch val := r.(type) {
case MyPanic:
fmt.Printf("捕获到自定义panic: %v\n", val)
case string:
fmt.Printf("捕获到字符串panic: %s\n", val)
default:
fmt.Printf("捕获到未知panic: %v\n", val)
}
}
}()
panic(MyPanic{Code: 500, Message: "内部服务器错误"})
}
func main() {
fmt.Println("=== panic与recover基础 ===")
panicExample()
fmt.Println("程序继续执行")
fmt.Println("\n=== 安全的除法 ===")
result, err := safeDivide(10, 2)
if err != nil {
fmt.Printf("错误: %v\n", err)
} else {
fmt.Printf("结果: %.2f\n", result)
}
result, err = safeDivide(10, 0)
if err != nil {
fmt.Printf("错误: %v\n", err)
}
fmt.Println("\n=== 值检查 ===")
checkValue(50)
checkValue(-1)
checkValue(150)
fmt.Println("\n=== 资源清理 ===")
processWithCleanup()
fmt.Println("程序继续执行")
fmt.Println("\n=== 自定义panic ===")
customPanicExample()
fmt.Println("程序继续执行")
}
第10章:包管理
10.1 Go模块系统
Go模块是Go 1.11引入的包管理系统,用于管理项目依赖。
# 初始化模块
go mod init github.com/yourusername/myproject
# 查看模块信息
go mod tidy
# 下载依赖
go mod download
# 验证依赖
go mod verify
# 查看依赖图
go mod graph
# 更新依赖
go get -u ./...
# 清理未使用的依赖
go mod tidy
// go.mod 示例
module github.com/yourusername/myproject
go 1.23
require (
github.com/gin-gonic/gin v1.9.1
github.com/golang-jwt/jwt/v5 v5.0.0
gorm.io/driver/mysql v1.5.1
gorm.io/gorm v1.25.2
)
require (
// 间接依赖
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
// ... 更多间接依赖
)
10.2 包的组织与导入
了解如何组织包结构和管理包的导入。
// 项目结构示例
/*
myproject/
├── go.mod
├── go.sum
├── main.go
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── auth/
│ │ └── jwt.go
│ ├── handlers/
│ │ └── user.go
│ └── models/
│ └── user.go
├── pkg/
│ ├── logger/
│ │ └── logger.go
│ └── utils/
│ └── string.go
└── api/
└── openapi.yaml
*/
// main.go
package main
import (
"fmt"
"log"
"github.com/yourusername/myproject/internal/auth"
"github.com/yourusername/myproject/internal/handlers"
"github.com/yourusername/myproject/internal/models"
"github.com/yourusername/myproject/pkg/logger"
)
func main() {
// 初始化日志
logger.Init()
// 初始化数据库
if err := models.InitDB(); err != nil {
log.Fatal(err)
}
// 初始化认证
auth.Init()
// 启动服务器
if err := handlers.StartServer(); err != nil {
log.Fatal(err)
}
}
// 包的可见性规则
package example
// 导出的标识符(首字母大写)
var PublicVariable = "任何人都可以访问"
const PublicConstant = "公开常量"
func PublicFunction() {
fmt.Println("这是公开函数")
}
type PublicStruct struct {
PublicField string
privateField string // 私有字段
}
// 未导出的标识符(首字母小写)
var privateVariable = "只能在包内访问"
const privateConstant = "私有常量"
func privateFunction() {
fmt.Println("这是私有函数")
}
type privateStruct struct {
field string
}
10.3 依赖管理
管理项目依赖,包括添加、更新和移除依赖。
# 添加依赖
go get github.com/gin-gonic/gin
# 添加特定版本
go get github.com/gin-gonic/gin@v1.9.1
# 添加最新版本
go get github.com/gin-gonic/gin@latest
# 更新依赖
go get -u github.com/gin-gonic/gin
# 更新所有依赖
go get -u ./...
# 移除未使用的依赖
go mod tidy
# 查看依赖树
go mod graph
# 查看为什么需要某个依赖
go mod why github.com/gin-gonic/gin
# 下载依赖到本地缓存
go mod download
# 清理模块缓存
go clean -modcache
# 编辑go.mod
go mod edit -replace github.com/old/package=github.com/new/package@v1.0.0
// 版本管理示例
/*
go.mod中的版本语法:
v1.2.3 - 精确版本
v1.2.x - 补丁版本可变
v1.x.x - 次版本可变
latest - 最新版本
语义化版本:主版本.次版本.补丁版本
v1.0.0 - 初始稳定版本
v1.1.0 - 向后兼容的新功能
v1.1.1 - 向后兼容的bug修复
v2.0.0 - 破坏性变更
*/
// 使用replace指令
module myproject
go 1.23
require (
github.com/gin-gonic/gin v1.9.1
// 使用本地版本
)
replace github.com/gin-gonic/gin => ../local/gin
// 使用exclude排除版本
exclude github.com/some/package v1.2.3
// 使用retract撤回版本
retract (
v1.0.0 // 有严重bug
v1.0.1 // 向后不兼容
)
10.4 私有模块与代理
配置私有模块和代理服务器,解决依赖下载问题。
# 设置GOPROXY(中国用户推荐)
export GOPROXY=https://goproxy.cn,direct
# 设置私有模块
export GOPRIVATE=*.corp.example.com,github.com/mycompany/*
# 设置GOSUMDB(校验和数据库)
export GOSUMDB=sum.golang.org
# 完全禁用校验和验证(不推荐)
export GOSUMDB=off
# 使用环境变量配置
export GO111MODULE=on
export GOPROXY=https://proxy.golang.org,direct
export GOSUMDB=sum.golang.org
# 在go.mod中配置私有模块
/*
go.mod:
go 1.23
require (
github.com/gin-gonic/gin v1.9.1
gitlab.com/private/package v1.0.0
)
*/
# 使用ssh代替https下载私有模块
git config --global url."ssh://git@github.com/".insteadOf "https://github.com/"
# 配置多个代理
export GOPROXY="https://goproxy.cn|https://proxy.golang.org|direct"
# 查看当前配置
go env GOPROXY
go env GOSUMDB
go env GOPRIVATE