Golang快速入门

帮助开发者在1小时内掌握Go语言核心概念和基础语法,涵盖环境安装、数据类型、控制流、函数、复合类型、并发入门和标准库速览

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.modgo.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 的学习之路上一帆风顺!

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「golang」更多文章

  1. 熔断、降级与限流:Go 微服务韧性设计完全指南
  2. 事件溯源与 CQRS 在 Go 中的实践:复杂业务系统的架构升级
  3. TinyGo 嵌入式开发与物联网实战:微控制器编程完全指南