Go 语言入门:从零开始学习 Golang – wiki大全


Go 语言入门:从零开始学习 Golang

Go 语言,又称 Golang,是 Google 在 2009 年推出的一种开源编程语言。它旨在提高开发效率、提升程序性能,并解决多核处理器和网络应用的挑战。Go 语言以其简洁的语法、强大的并发特性、快速的编译速度以及高效的垃圾回收机制,迅速在云计算、微服务、大数据等领域占据了一席之地。

如果你是编程新手,或者想学习一门现代、高效的语言,Go 语言无疑是一个非常好的选择。本文将带领你从零开始,逐步踏入 Golang 的世界。

为什么选择 Go 语言?

在开始学习之前,我们先了解一下 Go 语言的几个主要优势:

  1. 并发性 (Concurrency):Go 语言内置了 Goroutine(轻量级线程)和 Channel(通信机制),使得编写并发程序变得异常简单和高效。
  2. 性能 (Performance):Go 语言编译成机器码执行,性能接近 C/C++,但开发效率远高于它们。
  3. 简洁性 (Simplicity):Go 语言语法简洁,关键字少,易于学习和阅读,降低了代码维护成本。
  4. 高效的开发体验:拥有快速的编译速度、强大的标准库以及内置的工具链(如格式化工具 go fmt、测试工具 go test)。
  5. 跨平台 (Cross-Platform):Go 语言支持 Linux、Windows、macOS 等多种操作系统,可以轻松编译出跨平台的可执行文件。
  6. 静态类型 (Static Typing):在编译时进行类型检查,有助于捕获错误,提高代码的健壮性。

一、环境搭建

学习任何编程语言的第一步都是搭建开发环境。

1. 下载 Go 安装包

访问 Go 语言官方网站:https://golang.org/dl/。根据你的操作系统下载对应的安装包(Windows、macOS、Linux)。

2. 安装 Go

  • Windows:双击下载的 .msi 文件,按照提示一步步安装即可。通常会安装到 C:\Go 目录下,并自动配置好环境变量。
  • macOS:双击下载的 .pkg 文件,按照提示安装。
  • Linux
    1. 下载 go<version>.<os>-<arch>.tar.gz 文件。
    2. 解压到 /usr/local 目录:
      bash
      sudo rm -rf /usr/local/go
      sudo tar -C /usr/local -xzf go<version>.<os>-<arch>.tar.gz
    3. 配置环境变量:在 ~/.bashrc~/.zshrc 文件中添加以下行:
      bash
      export PATH=$PATH:/usr/local/go/bin
    4. 使配置生效:source ~/.bashrcsource ~/.zshrc

3. 验证安装

打开终端或命令行工具,输入:
bash
go version

如果能正确显示 Go 语言的版本信息,说明安装成功。

4. 配置 GOPROXY (推荐)

由于网络原因,有时访问 Go 模块(包)的官方源会很慢。推荐配置 GOPROXY 环境变量,使用国内镜像加速:
“`bash

Windows

go env -w GOPROXY=”https://goproxy.cn,https://mirrors.aliyun.com/goproxy/,direct”

macOS/Linux

export GOPROXY=”https://goproxy.cn,https://mirrors.aliyun.com/goproxy/,direct”

将其添加到 ~/.bashrc 或 ~/.zshrc 以便永久生效

“`

二、你的第一个 Go 程序:Hello World

让我们来编写经典的 “Hello World” 程序。

1. 创建项目目录

在你的工作区创建一个新的文件夹,例如 myproject
bash
mkdir myproject
cd myproject

2. 初始化 Go 模块

Go 语言使用模块 (Module) 来管理依赖。在项目根目录运行:
bash
go mod init myproject

这会创建一个 go.mod 文件,用于记录项目依赖。

3. 编写 main.go 文件

myproject 目录下创建 main.go 文件,并输入以下内容:
“`go
package main // 声明主包,可执行程序必须包含 main 包

import “fmt” // 导入 fmt 包,用于格式化输入输出

func main() { // 主函数,程序执行的入口
fmt.Println(“Hello, Go!”) // 打印字符串到控制台
}
“`

4. 运行程序

在终端中,进入 myproject 目录,然后运行:
bash
go run main.go

你将看到输出:
Hello, Go!

5. 编译程序

你也可以将程序编译成可执行文件:
bash
go build main.go

这会在当前目录下生成一个名为 main (Linux/macOS) 或 main.exe (Windows) 的可执行文件。你可以直接运行它:
bash
./main # Linux/macOS
main.exe # Windows

三、Go 语言基础语法

1. 变量

Go 语言是静态类型语言,变量在使用前必须声明类型。

  • 完整声明
    go
    var name string = "Alice"
    var age int = 30
  • 类型推断:Go 编译器可以自动推断变量类型。
    go
    var name = "Bob" // string
    var age = 25 // int
  • 短变量声明 (推荐):在函数内部,可以使用 := 运算符声明并初始化变量。
    go
    message := "Hello" // string
    count := 10 // int
    isTrue := true // bool
  • 多变量声明
    go
    var a, b, c int = 1, 2, 3
    x, y := 10, "world"

2. 常量

常量在程序编译时就已经确定,不能在运行时修改。
go
const PI float64 = 3.14159
const GREETING = "Hello" // 类型推断

iota 是 Go 语言中一个特殊的常量生成器,用于创建一系列相关的常量:
go
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
)

3. 数据类型

Go 语言支持以下基本数据类型:

  • 布尔型bool (true, false)
  • 数值类型
    • 整型int (根据系统决定 32/64 位), int8, int16, int32, int64 (有符号);uint, uint8, uint16, uint32, uint64, uintptr (无符号)。
    • 浮点型float32, float64
    • 复数型complex64, complex128
    • 字节型byte (等同于 uint8)。
    • 字符型rune (等同于 int32,用于表示 Unicode 码点)。
  • 字符串型string

4. 流程控制

Go 语言的流程控制语句与 C 家族语言类似,但有一些独特的语法。

  • If 语句
    “`go
    if score >= 60 {
    fmt.Println(“及格”)
    } else if score >= 0 {
    fmt.Println(“不及格”)
    } else {
    fmt.Println(“无效分数”)
    }

    // if 语句可以包含一个初始化语句
    if err := someFunc(); err != nil {
    fmt.Println(“Error:”, err)
    }
    ``
    **注意**:Go 语言的
    if语句条件不需要加括号()`。

  • For 循环:Go 语言中只有 for 循环,没有 whiledo-while

    • 经典 for 循环
      go
      for i := 0; i < 5; i++ {
      fmt.Println(i)
      }
    • 等同于 while 循环
      go
      sum := 1
      for sum < 100 {
      sum += sum
      }
      fmt.Println(sum)
    • 无限循环
      go
      for {
      fmt.Println("无限循环")
      break // 需要 break 跳出
      }
    • for-range 循环 (遍历数组、切片、映射、字符串、通道)
      “`go
      numbers := []int{1, 2, 3, 4, 5}
      for index, value := range numbers {
      fmt.Printf(“索引: %d, 值: %d\n”, index, value)
      }

      s := “你好 Go”
      for i, r := range s {
      fmt.Printf(“索引: %d, Unicode 码点: %d, 字符: %c\n”, i, r, r)
      }
      “`

  • Switch 语句
    “`go
    day := “Monday”
    switch day {
    case “Monday”, “Tuesday”: // 可以匹配多个值
    fmt.Println(“工作日”)
    case “Saturday”, “Sunday”:
    fmt.Println(“周末”)
    default:
    fmt.Println(“未知”)
    }

    // switch 语句也可以不带表达式,此时 case 后面跟条件
    age := 18
    switch {
    case age < 18:
    fmt.Println(“未成年”)
    case age >= 18 && age < 60:
    fmt.Println(“成年人”)
    default:
    fmt.Println(“老年人”)
    }
    ``
    **注意**:Go 语言的
    switch语句默认包含break,不需要显式添加。如果要继续执行下一个case,使用fallthrough` 关键字。

5. 函数

函数是组织代码的基本单位。

“`go
func add(x int, y int) int { // 参数类型在变量名之后,返回值类型在参数列表之后
return x + y
}

// 当相邻参数的类型相同时,可以省略前面参数的类型
func subtract(x, y int) int {
return x – y
}

// 多个返回值
func swap(x, y string) (string, string) {
return y, x
}

// 命名返回值
func divide(dividend, divisor float64) (quotient float64, err error) {
if divisor == 0 {
err = fmt.Errorf(“不能除以零”)
return // 返回命名返回值 quotient 和 err
}
quotient = dividend / divisor
return
}

func main() {
result := add(5, 3)
fmt.Println(“5 + 3 =”, result)

a, b := swap("hello", "world")
fmt.Println(a, b)

q, e := divide(10, 2)
if e != nil {
    fmt.Println(e)
} else {
    fmt.Println("10 / 2 =", q)
}

}
“`

6. 数组和切片 (Slice)

  • 数组 (Array):固定长度的同类型元素集合。
    “`go
    var a [5]int // 声明一个包含 5 个整数的数组,默认初始化为 0
    a[0] = 10
    fmt.Println(a) // 输出: [10 0 0 0 0]

    b := [3]string{“Go”, “Python”, “Java”} // 声明并初始化
    fmt.Println(b)

    c := […]int{1, 2, 3, 4, 5} // 编译器根据初始化列表推断数组长度
    fmt.Println(len(c)) // 输出: 5
    “`

  • 切片 (Slice):动态长度的同类型元素集合,是 Go 语言中最常用的序列类型。切片是对数组的一个引用。
    “`go
    // 从数组创建切片
    nums := [6]int{1, 2, 3, 4, 5, 6}
    s := nums[1:4] // 创建一个切片,包含索引 1, 2, 3 的元素 (即 2, 3, 4)
    fmt.Println(s) // 输出: [2 3 4]

    // 直接创建切片
    var s2 []int // 声明一个空切片,nil
    s3 := []int{10, 20, 30} // 声明并初始化

    // 使用 make 函数创建切片
    // make([]type, length, capacity)
    // length 是切片中元素的数量
    // capacity 是底层数组的容量
    s4 := make([]int, 5, 10) // 长度 5,容量 10
    fmt.Println(s4) // 输出: [0 0 0 0 0]
    fmt.Println(“Length:”, len(s4), “Capacity:”, cap(s4))

    // 切片操作
    s5 := []int{1, 2, 3}
    s5 = append(s5, 4, 5) // 追加元素
    fmt.Println(s5) // 输出: [1 2 3 4 5]

    s6 := []int{6, 7}
    s5 = append(s5, s6…) // 追加另一个切片
    fmt.Println(s5) // 输出: [1 2 3 4 5 6 7]
    “`

7. 映射 (Map)

映射是键值对的无序集合。

“`go
// 声明并初始化一个映射
m := map[string]int{
“apple”: 1,
“banana”: 2,
}
fmt.Println(m)

// 使用 make 函数创建映射
// make(map[KeyType]ValueType)
colors := make(map[string]string)
colors[“red”] = “#FF0000”
colors[“blue”] = “#0000FF”
fmt.Println(colors)

// 获取值
redCode := colors[“red”]
fmt.Println(“Red code:”, redCode)

// 检查键是否存在
value, ok := colors[“green”]
if ok {
fmt.Println(“Green code:”, value)
} else {
fmt.Println(“Green not found”)
}

// 删除键值对
delete(colors, “blue”)
fmt.Println(colors)
“`

8. 结构体 (Struct)

结构体是自定义的复合数据类型,可以把多个不同类型的数据组合在一起。

“`go
type Person struct {
Name string
Age int
City string
}

func main() {
// 创建结构体实例
p1 := Person{“Alice”, 30, “New York”}
fmt.Println(p1) // 输出: {Alice 30 New York}

// 通过字段名访问
fmt.Println(p1.Name)

// 创建结构体实例的另一种方式
p2 := Person{Name: "Bob", Age: 25} // City 为零值 ""
fmt.Println(p2)

// 结构体指针
p3 := &Person{Name: "Charlie", Age: 35, City: "London"}
fmt.Println(p3.Name) // 通过指针访问字段,Go 会自动解引用

}
“`

四、面向对象?Go 语言的接口和方法

Go 语言没有类 (Class) 的概念,但通过 结构体 (Struct)方法 (Method)接口 (Interface) 实现了面向对象编程的特性。

1. 方法 (Method)

方法是绑定到特定类型(结构体或自定义类型)的函数。

“`go
type Rectangle struct {
Width, Height float64
}

// 为 Rectangle 类型定义一个 Area 方法
// (r Rectangle) 称为接收者 (receiver)
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}

// 接收者也可以是指针类型,这样可以修改结构体本身
func (r Rectangle) Scale(factor float64) {
r.Width
= factor
r.Height *= factor
}

func main() {
rect := Rectangle{Width: 10, Height: 5}
fmt.Println(“Area:”, rect.Area()) // 调用方法

rect.Scale(2)
fmt.Println("Scaled Width:", rect.Width) // Width 变为 20

}
“`

2. 接口 (Interface)

接口定义了一组方法的签名。任何实现了接口中所有方法的类型,都被认为实现了该接口。这实现了多态性。

“`go
// 定义一个 Shape 接口
type Shape interface {
Area() float64
Perimeter() float64
}

// Circle 类型
type Circle struct {
Radius float64
}

// Circle 实现 Area 方法
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}

// Circle 实现 Perimeter 方法
func (c Circle) Perimeter() float64 {
return 2 * 3.14159 * c.Radius
}

// Rectangle 类型 (已定义)
// func (r Rectangle) Area() float64 { … } (已定义)

// Rectangle 实现 Perimeter 方法
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}

func main() {
rect := Rectangle{Width: 10, Height: 5}
circ := Circle{Radius: 7}

// rect 和 circ 都实现了 Shape 接口
var s Shape
s = rect
fmt.Printf("Rectangle Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())

s = circ
fmt.Printf("Circle Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())

}
“`

五、并发编程:Goroutine 和 Channel

这是 Go 语言最强大的特性之一。

1. Goroutine

Goroutine 是 Go 语言的轻量级并发执行单元,由 Go 运行时调度。它比操作系统的线程开销小得多。

使用 go 关键字即可启动一个 Goroutine:
“`go
import (
“fmt”
“time”
)

func say(s string) {
for i := 0; i < 3; i++ {
time.Sleep(100 * time.Millisecond) // 模拟耗时操作
fmt.Println(s)
}
}

func main() {
go say(“world”) // 在新的 Goroutine 中执行 say(“world”)
say(“hello”) // 在主 Goroutine 中执行 say(“hello”)

// 主 Goroutine 不会等待其他 Goroutine 结束,所以需要一些机制来等待
// 这里简单地等待一段时间,以便 Goroutine 有机会执行
time.Sleep(1 * time.Second)
fmt.Println("main goroutine finished")

}
运行结果可能像这样(顺序不固定):
hello
world
hello
world
hello
world
main goroutine finished
“`

2. Channel

Channel 是 Goroutine 之间进行通信的管道。它允许 Goroutine 安全地发送和接收数据。

“`go
import “fmt”

func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 将和发送到 channel c
}

func main() {
s := []int{7, 2, 8, -9, 4, 0}

// 创建一个 int 类型的 channel
c := make(chan int)

// 将切片 s 分成两部分,分别在不同的 Goroutine 中求和
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)

// 从 channel c 接收数据
x, y := <-c, <-c // 接收两个 Goroutine 发送的和

fmt.Println(x, y, x+y) // 输出: -5 17 12 (具体顺序可能不同,但结果相同)

}
“`
Channel 默认是无缓冲的,这意味着发送操作会阻塞,直到有 Goroutine 从 Channel 接收数据;接收操作也会阻塞,直到有 Goroutine 向 Channel 发送数据。

你可以创建带缓冲的 Channel:
go
ch := make(chan int, 2) // 创建一个容量为 2 的带缓冲 channel
ch <- 1
ch <- 2
// ch <- 3 // 此时会阻塞,因为容量已满
fmt.Println(<-ch)
fmt.Println(<-ch)

六、错误处理

Go 语言通过返回 error 类型来处理错误,而不是使用 try-catch 机制。函数的最后一个返回值通常是 error 类型。

“`go
import (
“errors”
“fmt”
)

func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New(“除数不能为零”) // 返回一个错误
}
return a / b, nil // 没有错误时返回 nil
}

func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println(“错误:”, err)
} else {
fmt.Println(“结果:”, result)
}

result, err = divide(10, 0)
if err != nil {
    fmt.Println("错误:", err) // 输出: 错误: 除数不能为零
} else {
    fmt.Println("结果:", result)
}

}
“`

七、包 (Package)

Go 语言通过包来组织代码。每个 Go 程序都由包组成。

  • main 包是可执行程序的入口。
  • 其他包通常是库包,提供可重用的功能。

创建自定义包

  1. myproject 目录下创建一个 utils 文件夹。
  2. utils 文件夹中创建 math.go 文件:
    “`go
    // utils/math.go
    package utils // 声明包名

    // 函数名首字母大写表示是公共的(可导出的)
    func Add(a, b int) int {
    return a + b
    }

    // 函数名首字母小写表示是私有的(只在包内可见)
    func multiply(a, b int) int {
    return a * b
    }
    3. 修改 `main.go` 文件来使用 `utils` 包:go
    // main.go
    package main

    import (
    “fmt”
    “myproject/utils” // 导入自定义包,路径是 go.mod 中定义的模块名 + 包路径
    )

    func main() {
    sum := utils.Add(10, 5) // 调用 utils 包中的 Add 函数
    fmt.Println(“Sum:”, sum)
    // fmt.Println(utils.multiply(2, 3)) // 错误: multiply 是私有的
    }
    4. 运行 `main.go`:bash
    go run main.go
    输出:
    Sum: 15
    “`

总结

至此,你已经学习了 Go 语言的基础知识,包括环境搭建、基本语法、数据类型、流程控制、函数、数组、切片、映射、结构体、方法、接口、并发编程以及错误处理和包管理。

这只是 Go 语言旅程的开始。接下来,你可以:

  • 深入学习 Go 的标准库,如 net/http 用于网络编程,io 用于输入输出,encoding/json 用于 JSON 处理等。
  • 掌握 go test 进行单元测试。
  • 了解 Go 模块的更高级用法。
  • 探索更复杂的并发模式,如 select 语句。
  • 阅读 Go 语言官方文档 (https://golang.org/doc/) 和 Effective Go (https://golang.org/doc/effective_go.html)。

Go 语言的学习曲线相对平缓,但其强大的特性和简洁的设计理念,将帮助你编写出高性能、高可维护性的现代化应用程序。祝你学习愉快!


滚动至顶部