Go 语言(通常称为 Golang)是一种开源编程语言,由 Google 的 Robert Griesemer、Rob Pike 和 Ken Thompson 在 2007 年设计并开发。它以简洁、高效、性能优异而著称,特别适合用于构建大型软件系统、网络服务和并发程序。本文的目标是让你在 1 小时内掌握 Go 的核心概念,能够独立编写能用的 Go 程序。
Go 语言的特点
在正式开始学习之前,了解 Go 语言的核心特点有助于建立正确的学习预期:
- 简洁的语法:Go 语言的语法简单直观,关键字仅有 25 个,学习曲线平缓
- 静态类型:Go 是静态类型语言,类型在编译时确定,能捕获大量潜在错误
- 编译型语言:Go 编译成机器码执行,启动速度快,运行时开销低
- 并发支持:Go 语言原生支持并发,使用 goroutines 和 channels 简化并发编程
- 内存管理:Go 拥有垃圾回收机制,简化内存管理,但相比 Java 有更短的 GC 停顿
- 跨平台编译:Go 支持跨平台编译,可以在 macOS 上编译出 Linux/Windows 的可执行文件
- 强大的标准库:Go 的标准库非常丰富,涵盖了网络、加密、压缩、数据库驱动等常用功能
- 单文件分发:编译后的 Go 程序不依赖运行时,单二进制文件即可部署
环境安装(多平台)
macOS
使用 Homebrew 安装是最简单的方式:
brew install go
或者从官网下载安装包:https://go.dev/dl/
Linux
# 下载最新版本(替换为实际版本号)
wget https://go.dev/dl/go1.23.4.linux-amd64.tar.gz
# 解压到 /usr/local
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.23.4.linux-amd64.tar.gz
# 添加到 PATH
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
Windows
下载 MSI 安装包运行即可,安装程序会自动配置环境变量。
验证安装
安装完成后,验证 Go 是否正确安装:
go version
go env GOPATH
建议设置 GOPROXY 环境变量来加速国内模块下载:
go env -w GOPROXY=https://goproxy.cn,direct
第一个 Hello World
创建 hello.go 文件:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
运行程序:
go run hello.go
编译为可执行文件:
go build hello.go
./hello
每个 Go 程序都由包组成。package main 表示这是一个可执行程序的入口包,func main() 是程序的入口函数。
基础语法速览
变量
Go 提供了多种变量声明方式:
package main
import "fmt"
func main() {
// 方式 1:完整声明
var name string = "Alice"
var age int = 25
// 方式 2:类型推断
var city = "Beijing" // 编译器推断为 string
var year = 2024 // 编译器推断为 int
// 方式 3:短变量声明(函数体内最常用)
country := "China"
temperature := 23.5
// 同时声明多个变量
var x, y, z int = 1, 2, 3
a, b, c := true, "hello", 42
// 零值:未初始化的变量有默认值
var number int // 0
var str string // ""
var flag bool // false
fmt.Println(name, age, city, year, country, temperature)
fmt.Println(x, y, z, a, b, c, number, str, flag)
}
常量
package main
import "fmt"
const Pi = 3.14159
// 常量组
const (
Sunday = 0
Monday = 1
Tuesday = 2
Wednesday = 3
Thursday = 4
Friday = 5
Saturday = 6
)
// iota:常量枚举生成器
const (
Low = iota // 0
Medium // 1
High // 2
)
func main() {
fmt.Println("Pi =", Pi)
fmt.Println("Days:", Sunday, Monday, Tuesday)
fmt.Println("Levels:", Low, Medium, High)
}
基本数据类型
package main
import "fmt"
func main() {
// 布尔型
var flag bool = true
// 整型
var i int = 42 // 平台依赖(32位或64位)
var i8 int8 = 127 // -128 到 127
var i64 int64 = 99999999
var ui uint = 0 // 无符号
var ui8 uint8 = 255 // 0 到 255(同 byte)
// 浮点型
var f32 float32 = 3.14
var f64 float64 = 2.718281828
// 字符串
var s string = "Hello, Go!"
// 自动类型推断
text := "这是字符串"
num := 100
fmt.Printf("%T %v\n", flag, flag)
fmt.Printf("%T %v\n", i, i)
fmt.Printf("%T %v\n", f64, f64)
fmt.Printf("%T %v\n", s, s)
fmt.Printf("%T %v\n", text, text)
fmt.Printf("%T %v\n", num, num)
}
控制流
package main
import "fmt"
func main() {
// if 语句
score := 85
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else {
fmt.Println("C")
}
// if 中声明变量
if age := 20; age >= 18 {
fmt.Println("Adult")
}
// for 循环(Go 只有 for,没有 while)
for i := 0; i < 5; i++ {
fmt.Print(i, " ")
}
fmt.Println()
// 类似 while 的 for
n := 0
for n < 5 {
fmt.Print(n, " ")
n++
}
fmt.Println()
// 无限循环
// for {
// fmt.Println("infinite")
// }
// switch 语句
day := "Monday"
switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("Workday")
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Unknown")
}
// 无表达式的 switch
score = 75
switch {
case score >= 90:
fmt.Println("Excellent")
case score >= 60:
fmt.Println("Pass")
default:
fmt.Println("Fail")
}
}
函数与包
函数定义
package main
import "fmt"
// 基本函数
func add(a int, b int) int {
return a + b
}
// 连续参数类型可省略
func multiply(a, b int) int {
return a * b
}
// 多返回值(Go 的特色)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide by zero")
}
return a / b, nil
}
// 命名返回值
func rectangle(width, height float64) (area, perimeter float64) {
area = width * height
perimeter = 2 * (width + height)
return // 裸 return,返回命名值
}
// 变参函数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func main() {
fmt.Println("add:", add(3, 5))
fmt.Println("multiply:", multiply(4, 6))
result, err := divide(10, 3)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("divide:", result)
}
area, peri := rectangle(5, 3)
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", area, peri)
fmt.Println("sum:", sum(1, 2, 3, 4, 5))
fmt.Println("sum:", sum(10, 20))
}
包管理
Go 使用 import 导入包:
package main
import (
"fmt"
"math"
"strings"
)
func main() {
fmt.Println(strings.ToUpper("hello"))
fmt.Println(math.Pi)
fmt.Println(math.Sqrt(16))
}
Go 1.11 之后引入的 Go Modules 是标准的依赖管理方式。初始化模块:
go mod init myproject
安装第三方包:
go get github.com/gin-gonic/gin
复合类型
数组与切片
package main
import "fmt"
func main() {
// 数组:固定长度
var arr [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Println("Array:", arr)
// 切片:动态数组(更常用)
slice := []int{1, 2, 3, 4, 5}
fmt.Println("Slice:", slice)
// 从数组创建切片
s1 := arr[1:4] // 索引 1 到 3
fmt.Println("s1:", s1)
// make 创建切片
s2 := make([]int, 5, 10) // 长度 5,容量 10
fmt.Println("s2:", s2, "len:", len(s2), "cap:", cap(s2))
// 追加元素
s3 := []int{1, 2}
s3 = append(s3, 3, 4, 5)
fmt.Println("s3:", s3)
// 遍历切片
fruits := []string{"apple", "banana", "cherry"}
for i, fruit := range fruits {
fmt.Printf("%d: %s\n", i, fruit)
}
}
Map(字典)
package main
import "fmt"
func main() {
// 创建 map
scores := map[string]int{
"Alice": 90,
"Bob": 85,
"Carol": 95,
}
fmt.Println(scores)
// 添加/修改
scores["David"] = 88
scores["Alice"] = 92
// 访问
fmt.Println("Alice:", scores["Alice"])
// 安全访问(检查是否存在)
if score, ok := scores["Eve"]; ok {
fmt.Println("Eve:", score)
} else {
fmt.Println("Eve not found")
}
// 删除
delete(scores, "Bob")
// 遍历
for name, score := range scores {
fmt.Printf("%s: %d\n", name, score)
}
// len
fmt.Println("Count:", len(scores))
}
结构体
package main
import "fmt"
// 定义结构体
type Person struct {
Name string
Age int
City string
}
// 方法:值接收者
func (p Person) Introduce() string {
return fmt.Sprintf("Hi, I'm %s, %d years old.", p.Name, p.Age)
}
// 方法:指针接收者(可修改原对象)
func (p *Person) HaveBirthday() {
p.Age++
}
func main() {
// 创建结构体
alice := Person{Name: "Alice", Age: 25, City: "Beijing"}
fmt.Println(alice)
// 访问字段
fmt.Println(alice.Name, alice.Age)
// 调用方法
fmt.Println(alice.Introduce())
// 指针修改
alice.HaveBirthday()
fmt.Println("After birthday:", alice.Age)
// 匿名结构体
point := struct {
X, Y float64
}{X: 3.14, Y: 2.71}
fmt.Println(point)
}
指针与接口简介
指针
Go 有指针,但没有指针运算(不像 C)。
package main
import "fmt"
func main() {
num := 42
// 获取指针
ptr := &num
fmt.Println("Address:", ptr)
fmt.Println("Value:", *ptr)
// 通过指针修改值
*ptr = 100
fmt.Println("num:", num)
// 函数中修改外部变量
increment(&num)
fmt.Println("After increment:", num)
}
func increment(n *int) {
*n++
}
接口
接口是 Go 最核心的设计之一。一个类型只要实现了接口定义的所有方法,就自动实现了该接口。
package main
import "fmt"
// 定义接口
type Shape interface {
Area() float64
Perimeter() float64
}
// 矩形类型
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// 圆形类型
type Circle struct {
Radius float64
}
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("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}
func main() {
r := Rectangle{Width: 5, Height: 3}
c := Circle{Radius: 4}
PrintShapeInfo(r)
PrintShapeInfo(c)
}
错误处理基础
Go 没有异常机制,错误通过多返回值显式处理:
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
result, err = divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
惯用的 Go 错误处理模式是:先检查 error,处理错误后再使用返回值。
并发入门
Go 的并发模型是其最大亮点。核心概念只有两个:goroutine 和 channel。
Goroutine
goroutine 是轻量级的执行线程,由 Go 运行时管理。创建 goroutine 极其简单:
package main
import (
"fmt"
"time"
)
func sayHello() {
for i := 0; i < 5; i++ {
fmt.Println("Hello")
time.Sleep(100 * time.Millisecond)
}
}
func sayWorld() {
for i := 0; i < 5; i++ {
fmt.Println("World")
time.Sleep(150 * time.Millisecond)
}
}
func main() {
go sayHello() // 在独立的 goroutine 中执行
go sayWorld() // 在另一个 goroutine 中执行
// 主程序等待一下,让 goroutine 有时间执行
// 生产环境应使用 sync.WaitGroup
time.Sleep(2 * time.Second)
fmt.Println("Main done")
}
Channel
channel 用于在 goroutine 之间安全地传递数据:
package main
import "fmt"
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, j)
results <- j * j
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// 启动 3 个 worker
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送 9 个任务
for j := 1; j <= 9; j++ {
jobs <- j
}
close(jobs)
// 收集结果
for a := 1; a <= 9; a++ {
fmt.Println("Result:", <-results)
}
}
sync.WaitGroup
更优雅的 goroutine 协调方式:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Worker %d done\n", id)
}(i)
}
wg.Wait() // 等待所有 goroutine 完成
fmt.Println("All workers completed")
}
常用标准库速览
Go 的标准库非常丰富,以下是开发中最常用的包:
fmt 包——格式化输入输出
package main
import "fmt"
func main() {
// Print 直接输出
fmt.Print("Hello ")
fmt.Print("World\n")
// Println 自动添加换行
fmt.Println("Hello", "World")
// Printf 格式化输出
name := "Alice"
age := 25
fmt.Printf("Name: %s, Age: %d\n", name, age)
// 格式化字符串
result := fmt.Sprintf("Name: %s", name)
fmt.Println(result)
// 常用格式动词
fmt.Printf("%v\n", name) // 默认格式
fmt.Printf("%+v\n", struct{ A int }{A: 1}) // 带字段名
fmt.Printf("%T\n", age) // 类型
fmt.Printf("%d\n", age) // 十进制整数
fmt.Printf("%f\n", 3.14) // 浮点数
fmt.Printf("%s\n", "text") // 字符串
fmt.Printf("%t\n", true) // 布尔值
}
strings 包——字符串处理
package main
import (
"fmt"
"strings"
)
func main() {
s := "Hello, Golang!"
fmt.Println(strings.Contains(s, "Go"))
fmt.Println(strings.HasPrefix(s, "Hello"))
fmt.Println(strings.HasSuffix(s, "!"))
fmt.Println(strings.Index(s, "l"))
fmt.Println(strings.ToUpper(s))
fmt.Println(strings.ToLower(s))
fmt.Println(strings.TrimSpace(" hello "))
fmt.Println(strings.Split("a,b,c", ","))
fmt.Println(strings.Join([]string{"a", "b", "c"}, "-"))
fmt.Println(strings.ReplaceAll(s, "l", "L"))
}
strconv 包——字符串与数字转换
package main
import (
"fmt"
"strconv"
)
func main() {
// 字符串转整数
n, _ := strconv.Atoi("42")
fmt.Println("Atoi:", n)
// 整数转字符串
s := strconv.Itoa(100)
fmt.Println("Itoa:", s)
// 字符串转浮点数
f, _ := strconv.ParseFloat("3.14", 64)
fmt.Println("ParseFloat:", f)
// 格式化浮点数
fmt.Println("FormatFloat:", strconv.FormatFloat(3.14159, 'f', 2, 64))
// 解析布尔值
b, _ := strconv.ParseBool("true")
fmt.Println("ParseBool:", b)
}
time 包——时间处理
package main
import (
"fmt"
"time"
)
func main() {
// 当前时间
now := time.Now()
fmt.Println("Now:", now)
fmt.Println("Year:", now.Year())
fmt.Println("Month:", now.Month())
fmt.Println("Day:", now.Day())
fmt.Println("Weekday:", now.Weekday())
// 格式化输出
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(now.Format("2006/01/02"))
// 时间戳
fmt.Println("Unix:", now.Unix())
fmt.Println("UnixMilli:", now.UnixMilli())
// 解析时间字符串
t, _ := time.Parse("2006-01-02", "2024-08-13")
fmt.Println("Parsed:", t)
// 定时器
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("Timer fired")
// 延时
time.Sleep(1 * time.Second)
fmt.Println("After sleep")
// 计算时间差
start := time.Now()
time.Sleep(100 * time.Millisecond)
elapsed := time.Since(start)
fmt.Println("Elapsed:", elapsed)
}
os 包——操作系统功能
package main
import (
"fmt"
"os"
)
func main() {
// 读取文件
content, err := os.ReadFile("hello.go")
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Printf("File size: %d bytes\n", len(content))
}
// 写入文件
err = os.WriteFile("test.txt", []byte("Hello Go!"), 0644)
if err != nil {
fmt.Println("Write error:", err)
}
// 环境变量
fmt.Println("PATH:", os.Getenv("PATH"))
// 创建目录
os.MkdirAll("test_dir", 0755)
// 获取工作目录
wd, _ := os.Getwd()
fmt.Println("Working dir:", wd)
// 命令行参数
fmt.Println("Args:", os.Args)
}
net/http 包——HTTP 服务器
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!\n", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", helloHandler)
fmt.Println("Server starting on :8080...")
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println("Server error:", err)
}
}
运行后访问 http://localhost:8080/World 即可看到响应。
下一步学习路径
完成本快速入门后,建议按照以下步骤继续深入学习 Go:
1. 官方教程
完成 Go 语言之旅(tour.golang.org),巩固所有基础概念。
2. 学习 Go Modules
理解 go.mod、go.sum 的作用,学会管理项目依赖和版本控制。
3. 深入并发编程
学习 channel 的高级用法、select 语句、context 包和 sync 包的更多功能。
4. 学习 Web 开发
使用标准库 net/http 或框架(如 Gin)开发 REST API。
5. 学习数据库操作
使用 database/sql 和 GORM 进行数据库增删改查。
6. 学习测试
掌握单元测试、基准测试和性能分析工具 pprof。
7. 阅读优秀开源代码
通过阅读 Gin、Cobra 等优质项目的源码来理解 Go 的工程实践。
总结
Go 语言以简洁著称,掌握本文介绍的核心概念后,你已经具备了编写实用 Go 程序的基础能力。Go 的语法规则不多但组合能力很强,最好的学习方式就是动手写代码。尝试将本文中的示例自己敲一遍,修改参数观察结果,然后挑战自己编写一个小工具或 Web 服务。Go 社区有丰富的文档和活跃的讨论,遇到问题时善用搜索引擎和 Stack Overflow。祝你在 Go 的学习之路上一帆风顺!
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。