Learn in 10 minutes

Learn in 10 minutes

10分钟学会Go语言

Go(也称为Golang)是Google设计的一种静态类型、编译型编程语言。它以其简洁性、高效性和出色的并发支持而闻名。本教程将帮助你快速学习Go编程。

1. 编写第一个Go程序

让我们从一个简单的程序开始。创建一个名为hello.go的文件并输入以下代码:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

保存文件并在终端中运行以下命令:

go run hello.go

输出将是:

Hello, World!

这个简单程序展示了Go的基本结构:

  • package main 声明包名
  • import "fmt" 导入格式化包用于I/O操作
  • func main() 是程序的入口点
  • fmt.Println() 向控制台打印文本

2. 基本语法

Go具有简洁明了的语法。与Python不同,Go使用花括号{}定义代码块,并且需要在语句末尾使用分号(虽然通常会自动插入)。

// 这是单行注释
fmt.Println("Hello, World!")

/*
这是多行注释
跨越多行
*/

Go中的基本语法规则:

  • 代码块:由花括号{}定义
  • 注释:单行注释以//开头,多行注释以/* */包围
  • 语句:以分号结束(自动插入)
  • 包声明:每个文件都以包声明开始

3. 变量和数据类型

Go是静态类型语言,意味着你必须声明变量类型。但是,Go支持使用:=操作符进行类型推断。

变量声明方法

// 显式类型声明
var name string = "John"
var age int = 25

// 类型推断
name := "John"
age := 25

// 多变量声明
var x, y int = 10, 20
x, y := 10, 20

Go的主要基本数据类型

  • 整数类型int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr
  • 浮点类型float32, float64
  • 字符串string
  • 布尔值bool
  • 复数类型complex64, complex128

3.1 数值类型

Go为不同的使用场景提供了各种数值类型:

// 整数类型
var age int = 25
var smallNumber int8 = 127
var largeNumber int64 = 9223372036854775807

// 浮点类型
var temperature float32 = 36.5
var pi float64 = 3.14159265359

// 复数
var complexNum complex64 = 3 + 4i

3.2 字符串类型

Go中的字符串是字节序列并且是不可变的:

// 字符串声明
var greeting string = "Hello, Go!"
name := "Alice"

// 字符串操作
fmt.Println(len(greeting))        // 字符串长度
fmt.Println(greeting[0])          // 访问第一个字符(字节)
fmt.Println(greeting[0:5])        // 字符串切片
fmt.Println(strings.ToUpper(name)) // 转换为大写

3.3 布尔类型

布尔类型有两个值:truefalse

var isActive bool = true
var isComplete bool = false

// 布尔运算
result1 := true && false  // false
result2 := true || false  // true
result3 := !true          // false

4. 常量

常量使用const关键字声明且不能更改:

const Pi = 3.14159
const MaxUsers = 1000

// 多个常量
const (
    StatusOK = 200
    StatusNotFound = 404
    StatusError = 500
)

// 类型化常量
const Version string = "1.0.0"

5. 数据结构

Go提供了几种内置数据结构来存储和操作数据。

5.1 数组

数组是相同类型元素的固定大小序列:

// 数组声明
var numbers [5]int = [5]int{1, 2, 3, 4, 5}
names := [3]string{"Alice", "Bob", "Charlie"}

// 访问元素
fmt.Println(numbers[0])  // 1
numbers[0] = 10         // 修改元素

// 数组长度
fmt.Println(len(numbers)) // 5

5.2 切片

切片是可以增长和缩小的动态数组:

// 切片声明
numbers := []int{1, 2, 3, 4, 5}
var emptySlice []int

// 从数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]  // [2, 3, 4]

// 切片操作
numbers = append(numbers, 6)      // 添加元素
numbers = append(numbers, 7, 8, 9) // 添加多个元素

// 切片容量和长度
fmt.Println(len(numbers)) // 长度: 9
fmt.Println(cap(numbers)) // 容量: 10 (可能变化)

5.3 映射

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

// 映射声明
student := map[string]interface{}{
    "name": "John",
    "age":  20,
    "major": "Computer Science",
}

// 替代声明
var scores map[string]int = make(map[string]int)
scores["math"] = 95
scores["science"] = 88

// 访问和修改
fmt.Println(student["name"])
student["age"] = 21
student["gpa"] = 3.8

// 安全访问
if phone, exists := student["phone"]; exists {
    fmt.Println(phone)
} else {
    fmt.Println("未提供电话号码")
}

// 遍历映射
for key, value := range student {
    fmt.Printf("%s: %v\n", key, value)
}

5.4 结构体

结构体是可以具有不同类型的字段集合:

// 结构体定义
type Person struct {
    Name string
    Age  int
    City string
}

// 创建结构体实例
person1 := Person{"Alice", 25, "New York"}
person2 := Person{
    Name: "Bob",
    Age:  30,
    City: "London",
}

// 访问字段
fmt.Println(person1.Name)
person1.Age = 26

6. 运算和操作符

Go提供了丰富的操作符集用于各种计算和比较。

  • 算术操作符+, -, *, /, %(取模)
  • 比较操作符==, !=, >, <, >=, <=
  • 逻辑操作符&&, ||, !
  • 位操作符&, |, ^, <<, >>
  • 赋值操作符=, +=, -=, *=, /=

6.1 算术操作符

a, b := 10, 3

fmt.Printf("加法: %d\n", a + b)      // 13
fmt.Printf("减法: %d\n", a - b)   // 7
fmt.Printf("乘法: %d\n", a * b)  // 30
fmt.Printf("除法: %d\n", a / b)      // 3
fmt.Printf("取模: %d\n", a % b)      // 1

6.2 比较操作符

x, y := 5, 10

fmt.Printf("相等: %t\n", x == y)     // false
fmt.Printf("不相等: %t\n", x != y) // true
fmt.Printf("大于: %t\n", x > y)  // false
fmt.Printf("小于: %t\n", x < y)  // true

6.3 逻辑操作符

a, b := true, false

fmt.Printf("与运算: %t\n", a && b)  // false
fmt.Printf("或运算: %t\n", a || b)    // true
fmt.Printf("非运算: %t\n", !a)    // false

7. 控制流

Go提供了几种控制流语句来管理程序执行。

7.1 if语句

age := 20
if age >= 18 {
    fmt.Println("成年人")
} else if age >= 13 {
    fmt.Println("青少年")
} else {
    fmt.Println("儿童")
}

// 带有简短语句的if
if score := 85; score >= 90 {
    fmt.Println("等级: A")
} else if score >= 80 {
    fmt.Println("等级: B")
} else {
    fmt.Println("等级: C")
}

7.2 for循环

Go只有一个循环结构:for

// 基本for循环
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// While风格循环
count := 0
for count < 5 {
    fmt.Println(count)
    count++
}

// 无限循环
for {
    fmt.Println("这将永远运行")
    break // 使用break退出
}

// Range循环(用于切片、数组、映射)
fruits := []string{"apple", "banana", "cherry"}
for index, fruit := range fruits {
    fmt.Printf("%d: %s\n", index, fruit)
}

7.3 switch语句

day := "Monday"
switch day {
case "Monday":
    fmt.Println("一周开始")
case "Friday":
    fmt.Println("周末临近")
case "Saturday", "Sunday":
    fmt.Println("周末!")
default:
    fmt.Println("普通日子")
}

// 没有表达式的switch
score := 85
switch {
case score >= 90:
    fmt.Println("等级: A")
case score >= 80:
    fmt.Println("等级: B")
case score >= 70:
    fmt.Println("等级: C")
default:
    fmt.Println("等级: F")
}

8. 函数

Go中的函数是一等公民,支持多个返回值。

8.1 基本函数

func greet(name string) string {
    return "Hello, " + name + "!"
}

// 调用函数
message := greet("John")
fmt.Println(message)

8.2 多个返回值

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("不能除以零")
    }
    return a / b, nil
}

// 使用多个返回值
result, err := divide(10, 2)
if err != nil {
    fmt.Println("错误:", err)
} else {
    fmt.Println("结果:", result)
}

8.3 命名返回值

func calculateRectangle(width, height float64) (area float64, perimeter float64) {
    area = width * height
    perimeter = 2 * (width + height)
    return // 裸返回
}

area, perimeter := calculateRectangle(5, 3)
fmt.Printf("面积: %.2f, 周长: %.2f\n", area, perimeter)

8.4 可变参数函数

func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

fmt.Println(sum(1, 2, 3, 4))  // 10
fmt.Println(sum(5, 10, 15))   // 30

9. 指针

Go有指针,但语法比C/C++更简单:

func modifyValue(x *int) {
    *x = *x * 2
}

func main() {
    value := 10
    fmt.Println("修改前:", value) // 10

    modifyValue(&value)
    fmt.Println("修改后:", value)  // 20
}

10. 方法

方法是带有接收器参数的函数:

type Rectangle struct {
    Width  float64
    Height float64
}

// 带有值接收器的方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 带有指针接收器的方法
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

rect := Rectangle{Width: 5, Height: 3}
fmt.Println("面积:", rect.Area()) // 15

rect.Scale(2)
fmt.Println("缩放后面积:", rect.Area()) // 60

11. 接口

接口定义了类型可以实现的方法签名:

type Shape interface {
    Area() float64
    Perimeter() float64
}

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("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}

circle := Circle{Radius: 5}
printShapeInfo(circle)

12. 错误处理

Go使用显式错误处理而不是异常:

func readFile(filename string) (string, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return "", fmt.Errorf("读取文件%s失败: %w", filename, err)
    }
    return string(data), nil
}

content, err := readFile("example.txt")
if err != nil {
    fmt.Println("错误:", err)
    return
}
fmt.Println("内容:", content)

13. 使用Goroutines的并发

Goroutines是由Go运行时管理的轻量级线程:

func worker(id int) {
    for i := 0; i < 3; i++ {
        fmt.Printf("工作者 %d: %d\n", id, i)
        time.Sleep(time.Millisecond * 100)
    }
}

func main() {
    // 启动多个goroutines
    for i := 1; i <= 3; i++ {
        go worker(i)
    }

    // 等待goroutines完成
    time.Sleep(time.Second)
    fmt.Println("所有工作者已完成")
}

14. 通道

通道用于goroutines之间的通信:

func producer(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i // 向通道发送值
        time.Sleep(time.Millisecond * 100)
    }
    close(ch) // 完成后关闭通道
}

func consumer(ch <-chan int) {
    for value := range ch {
        fmt.Println("接收到:", value)
    }
}

func main() {
    ch := make(chan int, 3) // 缓冲通道

    go producer(ch)
    consumer(ch)

    fmt.Println("通道通信完成")
}

15. 文件操作

Go提供了简单的方法来读写文件:

// 读取文件
data, err := os.ReadFile("example.txt")
if err != nil {
    fmt.Println("读取文件错误:", err)
    return
}
fmt.Println("文件内容:", string(data))

// 写入文件
content := "Hello, Go!\n"
err = os.WriteFile("output.txt", []byte(content), 0644)
if err != nil {
    fmt.Println("写入文件错误:", err)
    return
}
fmt.Println("文件写入成功")

16. 包和模块

Go模块管理依赖关系和包版本:

// 导入标准库包
import (
    "fmt"
    "math"
    "strings"
)

func main() {
    fmt.Println(math.Sqrt(16))        // 4
    fmt.Println(strings.ToUpper("go")) // GO
}

要创建自己的包,请创建一个以包名命名的目录,并通过大写函数名来导出函数。

17. 测试

Go具有内置的测试支持:

// 在文件math_test.go中
package main

import "testing"

func TestAdd(t *testing.T) {
    result := add(2, 3)
    expected := 5
    if result != expected {
        t.Errorf("add(2, 3) = %d; 期望 %d", result, expected)
    }
}

func add(a, b int) int {
    return a + b
}

运行测试:go test

18. 最佳实践

  • 使用gofmt格式化代码
  • 遵循Go命名约定(变量使用camelCase,导出使用PascalCase)
  • 显式处理错误
  • 使用接口进行抽象
  • 优先使用组合而不是继承
  • 编写全面的测试
  • 尽可能使用标准库

本教程涵盖了Go编程的基本特性。通过练习,你将能够使用Go的强大功能构建高效且并发的应用程序。