适用人群:有其他语言基础的开发者、后端开发者、云原生开发者
学习时长:约3-5天(每天4小时)
Go版本:1.22+
适用场景:微服务、云原生、高并发后端、CLI工具、区块链
一、Go 是什么?
Go(Golang)是Google开发的编程语言,以简洁、高效、并发性能著称,是云原生时代的首选语言。
| 特点 | 说明 |
|---|---|
| 上手难度 | ★★☆☆☆(语法简洁,但需要理解并发模型) |
| 核心优势 | 编译型、高并发、部署简单、标准库强大 |
| 主要用途 | 微服务、K8S/Docker、高并发后端、DevOps工具 |
| 薪资水平 | 初级15-25K,中级25-40K,高级40K+ |
| 代表项目 | Docker、Kubernetes、Etcd、Prometheus |
二、环境搭建
# macOS
brew install go
# Ubuntu
sudo apt update
sudo apt install golang-go
# Windows
# 下载:https://go.dev/dl/
# 验证安装
go version
# 配置环境变量(~/.bashrc 或 ~/.zshrc)
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
export GOPROXY=https://goproxy.cn,direct # 国内代理
三、Hello World
// main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
# 运行
go run main.go
# 编译
go build -o myapp main.go
./myapp
四、Go 基础语法
4.1 变量与类型
package main
import "fmt"
func main() {
// ====================
// 变量声明
// ====================
// 方式1:完整声明
var name string = "张三"
var age int = 25
// 方式2:类型推断
var height = 175.5
// 方式3:短变量声明(最常用)
city := "北京"
isActive := true
// 方式4:多变量声明
var (
username = "admin"
email = "admin@example.com"
role = "管理员"
)
// 常量
const Pi = 3.14159
const (
StatusOK = 200
StatusNotFound = 404
)
fmt.Println(name, age, height, city, isActive)
fmt.Println(username, email, role)
// ====================
// 基本类型
// ====================
// 整数:int, int8, int16, int32, int64
// 无符号整数:uint, uint8, uint16, uint32, uint64
// 浮点数:float32, float64
// 布尔:bool
// 字符串:string
// 字节:byte (uint8别名)
// rune:int32别名,表示Unicode码点
// ====================
// 零值
// ====================
// 数字类型:0
// 布尔类型:false
// 字符串:""
// 指针、切片、映射、函数、接口、通道:nil
}
4.2 数组与切片
package main
import "fmt"
func main() {
// ====================
// 数组(固定长度)
// ====================
var arr [5]int = [5]int{1, 2, 3, 4, 5}
arr2 := [...]int{1, 2, 3} // 自动推断长度
fmt.Println(arr, arr2)
// ====================
// 切片(动态数组,最常用)
// ====================
// 创建切片
nums := []int{1, 2, 3, 4, 5}
nums2 := make([]int, 5) // 长度5
nums3 := make([]int, 0, 10) // 长度0,容量10
// 增
nums = append(nums, 6)
nums = append(nums, 7, 8, 9)
// 切片操作
sub := nums[1:4] // [2, 3, 4]
first3 := nums[:3] // [1, 2, 3]
last3 := nums[6:] // [7, 8, 9]
// 长度和容量
fmt.Println(len(nums), cap(nums))
// 遍历
for i, v := range nums {
fmt.Printf("索引: %d, 值: %d\n", i, v)
}
// ====================
// 切片是引用类型
// ====================
original := []int{1, 2, 3, 4, 5}
copied := make([]int, len(original))
copy(copied, original) // 深拷贝
_ = nums2
_ = nums3
_ = sub
_ = first3
_ = last3
}
4.3 Map(字典)
package main
import "fmt"
func main() {
// 创建map
user := map[string]interface{}{
"name": "张三",
"age": 25,
"city": "北京",
}
// 或使用make
scores := make(map[string]int)
scores["数学"] = 95
scores["英语"] = 88
scores["编程"] = 92
// 增/改
user["email"] = "zhangsan@example.com"
// 查
name := user["name"]
score, exists := scores["数学"] // 两个返回值
if exists {
fmt.Println("数学成绩:", score)
}
// 删
delete(scores, "英语")
// 遍历
for key, value := range user {
fmt.Printf("%s: %v\n", key, value)
}
// 判断key是否存在
if _, ok := user["phone"]; !ok {
fmt.Println("没有手机号")
}
fmt.Println(name)
}
4.4 条件与循环
package main
import "fmt"
func main() {
// ====================
// 条件判断
// ====================
age := 25
if age >= 18 {
fmt.Println("成年人")
} else if age >= 12 {
fmt.Println("青少年")
} else {
fmt.Println("儿童")
}
// if 可以包含初始化语句
if score := 85; score >= 90 {
fmt.Println("优秀")
} else if score >= 60 {
fmt.Println("及格")
}
// switch(不需要break)
day := "Monday"
switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("工作日")
case "Saturday", "Sunday":
fmt.Println("周末")
default:
fmt.Println("未知")
}
// ====================
// 循环(只有for)
// ====================
// 基本for
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// while风格
count := 0
for count < 5 {
fmt.Println(count)
count++
}
// 无限循环
// for {
// // break退出
// }
// range遍历
fruits := []string{"苹果", "香蕉", "橙子"}
for i, fruit := range fruits {
fmt.Printf("%d: %s\n", i, fruit)
}
// 只要值,不要索引
for _, fruit := range fruits {
fmt.Println(fruit)
}
// 遍历map
user := map[string]string{"name": "张三", "city": "北京"}
for key, value := range user {
fmt.Printf("%s: %s\n", key, value)
}
}
五、函数
package main
import (
"errors"
"fmt"
)
// ====================
// 基本函数
// ====================
func greet(name string) string {
return fmt.Sprintf("你好,%s!", name)
}
// ====================
// 多返回值
// ====================
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
// ====================
// 命名返回值
// ====================
func getUser(id int) (name string, age int, err error) {
// 命名返回值可以直接使用
name = "张三"
age = 25
return // 裸返回
}
// ====================
// 可变参数
// ====================
func sum(numbers ...int) int {
total := 0
for _, n := range numbers {
total += n
}
return total
}
// ====================
// 函数作为参数
// ====================
func apply(f func(int, int) int, a, b int) int {
return f(a, b)
}
// ====================
// 匿名函数(闭包)
// ====================
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
fmt.Println(greet("张三"))
result, err := divide(10, 3)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("结果:", result)
}
fmt.Println(sum(1, 2, 3, 4, 5))
add := func(a, b int) int { return a + b }
fmt.Println(apply(add, 3, 4))
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
fmt.Println(counter()) // 3
}
六、结构体(类的替代)
package main
import "fmt"
// ====================
// 定义结构体
// ====================
type User struct {
ID int
Name string
Email string
Age int
IsActive bool
}
// 方法(值接收者)
func (u User) Greet() string {
return fmt.Sprintf("我是%s,今年%d岁", u.Name, u.Age)
}
// 方法(指针接收者 - 可以修改结构体)
func (u *User) SetName(name string) {
u.Name = name
}
// 构造函数(Go惯例)
func NewUser(name string, email string, age int) *User {
return &User{
Name: name,
Email: email,
Age: age,
IsActive: true,
}
}
// ====================
// 接口
// ====================
type Logger interface {
Log(message string)
GetLog() string
}
func (u User) Log(message string) {
fmt.Printf("[%s] %s\n", u.Name, message)
}
func (u User) GetLog() string {
return fmt.Sprintf("User: %s", u.Name)
}
// 接口作为参数
func ProcessLog(l Logger) {
l.Log("处理中...")
fmt.Println(l.GetLog())
}
// ====================
// 继承(组合)
// ====================
type Admin struct {
User // 嵌入User(匿名字段)
Role string
}
func (a Admin) Greet() string {
return fmt.Sprintf("%s(%s)", a.User.Greet(), a.Role)
}
func main() {
// 创建实例
user := NewUser("张三", "zhangsan@example.com", 25)
fmt.Println(user.Greet())
user.SetName("李四")
fmt.Println(user.Greet())
// 接口使用
ProcessLog(*user)
// 组合
admin := Admin{
User: User{Name: "管理员", Age: 30},
Role: "superadmin",
}
fmt.Println(admin.Greet())
fmt.Println(admin.User.Greet()) // 也可以访问嵌入的方法
}
七、并发编程(Go的核心优势)
package main
import (
"fmt"
"sync"
"time"
)
// ====================
// Goroutine(轻量级线程)
// ====================
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 完成时通知WaitGroup
fmt.Printf("Worker %d 开始\n", id)
time.Sleep(time.Second) // 模拟工作
fmt.Printf("Worker %d 完成\n", id)
}
func mainGoroutine() {
var wg sync.WaitGroup
// 启动5个goroutine
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg) // go关键字启动goroutine
}
wg.Wait() // 等待所有goroutine完成
fmt.Println("所有worker完成")
}
// ====================
// Channel(通道 - goroutine间通信)
// ====================
func mainChannel() {
// 创建channel
ch := make(chan string)
// 发送数据到channel
go func() {
time.Sleep(time.Second)
ch <- "Hello from goroutine"
}()
// 从channel接收数据
msg := <-ch
fmt.Println(msg)
// 带缓冲的channel
buffered := make(chan int, 3)
buffered <- 1
buffered <- 2
buffered <- 3
// buffered <- 4 // 会阻塞,因为缓冲区已满
fmt.Println(<-buffered, <-buffered, <-buffered)
}
// ====================
// 生产者-消费者模式
// ====================
func producer(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
fmt.Printf("生产: %d\n", i)
}
close(ch) // 关闭channel
}
func consumer(ch <-chan int, id int) {
for num := range ch {
fmt.Printf("消费者%d 消费: %d\n", id, num)
time.Sleep(100 * time.Millisecond)
}
}
func mainProducerConsumer() {
ch := make(chan int, 5)
// 启动1个生产者
go producer(ch)
// 启动3个消费者
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
consumer(ch, id)
}(i)
}
wg.Wait()
fmt.Println("所有消费者完成")
}
// ====================
// Select(多路复用)
// ====================
func mainSelect() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "来自channel1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "来自channel2"
}()
// 等待第一个完成的
select {
case msg := <-ch1:
fmt.Println(msg)
case msg := <-ch2:
fmt.Println(msg)
case <-time.After(3 * time.Second):
fmt.Println("超时")
}
}
八、错误处理
package main
import (
"errors"
"fmt"
)
// 自定义错误
var (
ErrNotFound = errors.New("记录不存在")
ErrInvalid = errors.New("参数无效")
)
// 带上下文的错误
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("验证错误 [%s]: %s", e.Field, e.Message)
}
// 错误处理模式
func findUser(id int) (string, error) {
if id <= 0 {
return "", &ValidationError{Field: "id", Message: "必须大于0"}
}
if id > 100 {
return "", ErrNotFound
}
return "张三", nil
}
func main() {
name, err := findUser(0)
if err != nil {
// 使用errors.Is判断特定错误
if errors.Is(err, ErrNotFound) {
fmt.Println("用户不存在")
}
// 使用errors.As获取具体错误类型
var validationErr *ValidationError
if errors.As(err, &validationErr) {
fmt.Printf("字段: %s, 消息: %s\n", validationErr.Field, validationErr.Message)
}
fmt.Println("错误:", err)
return
}
fmt.Println("找到用户:", name)
}
九、JSON 处理
package main
import (
"encoding/json"
"fmt"
)
// 结构体标签
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"` // 空值时省略
Password string `json:"-"` // 永不序列化
Age int `json:"age"`
}
func main() {
// 序列化
user := User{ID: 1, Name: "张三", Email: "test@example.com", Password: "secret", Age: 25}
jsonBytes, err := json.Marshal(user)
if err != nil {
fmt.Println("序列化错误:", err)
return
}
fmt.Println(string(jsonBytes))
// {"id":1,"name":"张三","email":"test@example.com","age":25}
// 格式化输出
jsonBytes, _ = json.MarshalIndent(user, "", " ")
fmt.Println(string(jsonBytes))
// 反序列化
jsonStr := `{"id":2,"name":"李四","email":"lisi@example.com","age":30}`
var user2 User
err = json.Unmarshal([]byte(jsonStr), &user2)
if err != nil {
fmt.Println("反序列化错误:", err)
return
}
fmt.Printf("%+v\n", user2)
// 动态JSON(不确定结构)
var data map[string]interface{}
json.Unmarshal([]byte(jsonStr), &data)
fmt.Println(data["name"])
}
十、HTTP 服务(Gin框架)
# 安装Gin
go get -u github.com/gin-gonic/gin
// main.go
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// 模型
type User struct {
ID int `json:"id"`
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Age int `json:"age" binding:"min=0,max=150"`
}
// 模拟数据库
var users = []User{
{ID: 1, Name: "张三", Email: "zhangsan@example.com", Age: 25},
{ID: 2, Name: "李四", Email: "lisi@example.com", Age: 30},
}
var nextID = 3
func main() {
r := gin.Default()
// 路由组
api := r.Group("/api")
{
// GET - 用户列表
api.GET("/users", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": users})
})
// GET - 单个用户
api.GET("/users/:id", func(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
for _, u := range users {
if u.ID == id {
c.JSON(http.StatusOK, gin.H{"data": u})
return
}
}
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
})
// POST - 创建用户
api.POST("/users", func(c *gin.Context) {
var newUser User
if err := c.ShouldBindJSON(&newUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
newUser.ID = nextID
nextID++
users = append(users, newUser)
c.JSON(http.StatusCreated, gin.H{"data": newUser})
})
// PUT - 更新用户
api.PUT("/users/:id", func(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var updated User
if err := c.ShouldBindJSON(&updated); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
for i, u := range users {
if u.ID == id {
updated.ID = id
users[i] = updated
c.JSON(http.StatusOK, gin.H{"data": updated})
return
}
}
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
})
// DELETE - 删除用户
api.DELETE("/users/:id", func(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
for i, u := range users {
if u.ID == id {
users = append(users[:i], users[i+1:]...)
c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
return
}
}
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
})
}
r.Run(":8080")
}
学习建议
- 先理解Go的设计哲学:少即是多,显式优于隐式
- 重点掌握切片和map,这是最常用的数据结构
- 深入理解goroutine和channel,这是Go的核心竞争力
- 养成错误处理的习惯,Go的错误处理是显式的
- 学习Gin框架,做后端API开发的首选