GoLang教程:快速掌握Go语言基础 – wiki基地

GoLang教程:快速掌握Go语言基础

Go(或Golang)是由Google开发的一种开源编程语言,旨在提高开发效率。它结合了编译型语言的性能和解释型语言的易用性。如果你想快速掌握Go语言的基础,本文将为你提供一个详细的教程。

1. 为什么选择Go?

在深入学习之前,我们先了解一下Go语言的优势:

  • 并发性强:Go语言内置了goroutine和channel,使得编写高并发程序变得异常简单和高效。
  • 性能优异:Go是编译型语言,其性能接近C/C++,但开发效率远超它们。
  • 开发效率高:简洁的语法、快速的编译速度以及强大的标准库,让开发者能够快速构建应用。
  • 静态类型:编译时检查类型错误,减少运行时bug。
  • 跨平台:一次编译,多平台运行。
  • 垃圾回收:自动内存管理,减少内存泄漏的风险。

2. 环境搭建

首先,你需要安装Go。

  1. 下载Go:访问Go官网 (go.dev/dl/),根据你的操作系统下载对应的安装包。
  2. 安装:按照安装向导的指示完成安装。
  3. 验证:打开命令行或终端,输入 go version。如果显示Go的版本信息,说明安装成功。

3. Go语言基础

3.1. Hello, World!

让我们从经典的“Hello, World!”开始。创建一个名为 main.go 的文件,并写入以下代码:

“`go
package main // 声明主包,可执行程序必须包含main包

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

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

在命令行中,导航到 main.go 所在的目录,然后运行:

bash
go run main.go

你将看到输出:Hello, World!

3.2. 变量与常量

变量声明

Go语言的变量声明有多种方式:

“`go
package main

import “fmt”

func main() {
// 方式一:完整声明(推荐)
var age int = 30
fmt.Println(“Age:”, age)

// 方式二:类型推断
var name = "Alice"
fmt.Println("Name:", name)

// 方式三:短变量声明(只能在函数内部使用)
message := "Hello, Go!"
fmt.Println("Message:", message)

// 方式四:批量声明
var (
    width  int    = 100
    height int    = 200
    active bool   = true
)
fmt.Println("Width:", width, "Height:", height, "Active:", active)

}
“`

常量声明

常量的值在编译时确定,且不能被修改。

“`go
package main

import “fmt”

const PI = 3.14159
const GREETING string = “Hello”

func main() {
fmt.Println(“PI:”, PI)
fmt.Println(“Greeting:”, GREETING)

// iota:用于枚举,自动递增
const (
    A = iota // 0
    B        // 1
    C        // 2
)
fmt.Println("A:", A, "B:", B, "C:", C)

}
“`

3.3. 数据类型

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

  • 布尔型bool (true/false)
  • 整型int, int8, int16, int32, int64, uint, uint8
  • 浮点型float32, float64
  • 复数型complex64, complex128
  • 字符串string
  • 字符型rune (Unicode字符), byte (ASCII字符)

“`go
package main

import “fmt”

func main() {
var isGoFun bool = true
var myInt int = 42
var myFloat float64 = 3.14
var myString string = “Go Language”
var myRune rune = ‘A’ // 单引号表示rune
var myByte byte = 65 // 对应ASCII码 ‘A’

fmt.Printf("isGoFun: %t, Type: %T\n", isGoFun, isGoFun)
fmt.Printf("myInt: %d, Type: %T\n", myInt, myInt)
fmt.Printf("myFloat: %f, Type: %T\n", myFloat, myFloat)
fmt.Printf("myString: %s, Type: %T\n", myString, myString)
fmt.Printf("myRune: %c, Type: %T\n", myRune, myRune)
fmt.Printf("myByte: %c, Type: %T\n", myByte, myByte)

}
“`

3.4. 控制流语句

If/Else

“`go
package main

import “fmt”

func main() {
score := 85

if score >= 90 {
    fmt.Println("优秀")
} else if score >= 60 {
    fmt.Println("及格")
} else {
    fmt.Println("不及格")
}

// if语句可以带一个初始化语句
if x := 10; x > 5 {
    fmt.Println("x大于5")
}

}
“`

For 循环

Go语言只有 for 循环,但它可以实现 while 和无限循环的效果。

“`go
package main

import “fmt”

func main() {
// 经典for循环
for i := 0; i < 5; i++ {
fmt.Print(i, ” “)
}
fmt.Println()

// 模拟while循环
j := 0
for j < 5 {
    fmt.Print(j, " ")
    j++
}
fmt.Println()

// 无限循环
k := 0
for {
    if k >= 3 {
        break
    }
    fmt.Print(k, " ")
    k++
}
fmt.Println()

// range循环(用于遍历数组、切片、map、字符串、channel)
s := "hello"
for index, char := range s {
    fmt.Printf("Index: %d, Char: %c\n", index, char)
}

}
“`

Switch 语句

“`go
package main

import “fmt”

func main() {
day := “Monday”

switch day {
case "Monday":
    fmt.Println("工作日")
case "Saturday", "Sunday": // 多个case
    fmt.Println("周末")
default:
    fmt.Println("未知")
}

// switch 也可以不带表达式,此时case中为布尔表达式
hour := 15
switch {
case hour < 12:
    fmt.Println("上午")
case hour < 18:
    fmt.Println("下午")
default:
    fmt.Println("晚上")
}

}
“`

3.5. 函数

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

“`go
package main

import “fmt”

// 无参数,无返回值
func sayHello() {
fmt.Println(“Hello from a function!”)
}

// 带参数,无返回值
func greet(name string) {
fmt.Println(“Hello,”, name)
}

// 带参数,带一个返回值
func add(a int, b int) int { // 参数类型和返回值类型都放在变量名后面
return a + b
}

// 带参数,带多个返回值
func swap(x, y string) (string, string) { // 相同类型的参数可以只写一个类型
return y, x
}

// 命名返回值
func divide(a, b int) (result int, err error) {
if b == 0 {
return 0, fmt.Errorf(“除数不能为0”)
}
result = a / b
return // 直接返回命名返回值
}

func main() {
sayHello()
greet(“World”)

sum := add(5, 3)
fmt.Println("Sum:", sum)

a, b := swap("Go", "Lang")
fmt.Println("Swapped:", a, b)

res, err := divide(10, 2)
if err != nil {
    fmt.Println("Error:", err)
} else {
    fmt.Println("Division Result:", res)
}

res2, err2 := divide(10, 0)
if err2 != nil {
    fmt.Println("Error:", err2)
} else {
    fmt.Println("Division Result:", res2)
}

}
“`

3.6. 数组与切片 (Slice)

数组

数组是固定长度的同类型元素序列。

“`go
package main

import “fmt”

func main() {
// 声明并初始化
var numbers [5]int // 声明一个包含5个整型的数组,默认值为0
fmt.Println(“Numbers:”, numbers)

numbers[0] = 10
numbers[4] = 50
fmt.Println("Numbers:", numbers)
fmt.Println("Length of numbers:", len(numbers))

// 声明时直接赋值
primes := [3]int{2, 3, 5}
fmt.Println("Primes:", primes)

// 编译器推断长度
colors := [...]string{"Red", "Green", "Blue"}
fmt.Println("Colors:", colors)

}
“`

切片 (Slice)

切片是对数组的一个封装,它更灵活、可变长度。切片是Go中最常用的数据结构之一。

“`go
package main

import “fmt”

func main() {
// 声明切片
var s []int // nil切片

// 使用make函数创建切片
// make([]type, length, capacity)
// length: 切片的长度
// capacity: 底层数组的容量
s = make([]int, 3, 5) // 长度为3,容量为5
fmt.Println("s:", s, "len:", len(s), "cap:", cap(s))

// 初始化切片
names := []string{"Alice", "Bob", "Charlie"}
fmt.Println("Names:", names)

// 切片操作:切片可以从现有数组或切片创建
arr := [5]int{1, 2, 3, 4, 5}
slice1 := arr[0:3] // 从索引0到2 (不包含3)
slice2 := arr[2:]  // 从索引2到末尾
slice3 := arr[:3]  // 从开头到索引2
slice4 := arr[:]   // 整个数组
fmt.Println("slice1:", slice1, "slice2:", slice2, "slice3:", slice3, "slice4:", slice4)

// append:向切片添加元素
s = append(s, 10, 20)
fmt.Println("s after append:", s, "len:", len(s), "cap:", cap(s))
s = append(s, 30, 40, 50) // 容量不足时,Go会自动扩容
fmt.Println("s after more append:", s, "len:", len(s), "cap:", cap(s))

// copy:复制切片
source := []int{1, 2, 3}
destination := make([]int, len(source))
copy(destination, source)
fmt.Println("Destination:", destination)

}
“`

3.7. 映射 (Map)

Map是一种无序的键值对集合,类似于其他语言中的字典或哈希表。

“`go
package main

import “fmt”

func main() {
// 声明并初始化map
var ages map[string]int // nil map,不能直接添加元素

// 使用make创建map
ages = make(map[string]int)
ages["Alice"] = 30
ages["Bob"] = 25
fmt.Println("Ages:", ages)

// 声明时直接赋值
salaries := map[string]float64{
    "Alice":   50000.0,
    "Bob":     60000.0,
    "Charlie": 75000.0,
}
fmt.Println("Salaries:", salaries)

// 获取元素
fmt.Println("Alice's salary:", salaries["Alice"])

// 判断键是否存在
value, ok := salaries["David"]
if ok {
    fmt.Println("David's salary:", value)
} else {
    fmt.Println("David not found")
}

// 删除元素
delete(salaries, "Bob")
fmt.Println("Salaries after deleting Bob:", salaries)

// 遍历map
for name, salary := range salaries {
    fmt.Printf("%s: %.2f\n", name, salary)
}

}
“`

3.8. 结构体 (Struct)

结构体是用户自定义的类型,用于组合不同类型的字段。

“`go
package main

import “fmt”

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

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

// 访问字段
fmt.Println("Name:", p1.Name)
p1.Age = 31
fmt.Println("New Age:", p1.Age)

// 使用字段名初始化(推荐,可读性好,顺序无关)
p2 := Person{
    Name: "Bob",
    City: "London",
    Age:  25,
}
fmt.Println("Person 2:", p2)

// 结构体指针
p3 := &Person{Name: "Charlie", Age: 28, City: "Paris"}
fmt.Println("Person 3 (pointer):", p3)
fmt.Println("Person 3 Name (dereferenced):", (*p3).Name) // 显式解引用
fmt.Println("Person 3 Name (implicit):", p3.Name)      // Go会自动处理,更常用

}
“`

3.9. 方法 (Methods)

Go语言中的方法是作用于特定类型(结构体或自定义类型)的函数。

“`go
package main

import “fmt”

type Rectangle struct {
Width float64
Height float64
}

// Area方法作用于Rectangle类型(值接收者)
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}

// Scale方法作用于Rectangle类型(指针接收者),可以修改结构体字段
func (r Rectangle) Scale(factor float64) {
r.Width
= factor
r.Height *= factor
}

func main() {
rect := Rectangle{Width: 10, Height: 5}
fmt.Println(“Original Area:”, rect.Area())

rect.Scale(2) // 使用指针接收者修改了rect
fmt.Println("Scaled Area:", rect.Area())

// 值接收者和指针接收者的区别:
// 值接收者:方法操作的是结构体的一个副本,不会修改原始结构体。
// 指针接收者:方法操作的是原始结构体的指针,可以修改原始结构体。

}
“`

4. 并发编程:Goroutine与Channel

这是Go语言的精髓之一。

4.1. Goroutine

Goroutine是Go语言轻量级的线程,由Go运行时管理。

“`go
package main

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”) // main goroutine执行say(“hello”)
// main函数执行完毕,所有goroutine都会退出。
// 为了让goroutine有机会执行,我们可以让main goroutine等待一会
time.Sleep(500 * time.Millisecond)
fmt.Println(“main goroutine finished”)
}
“`

运行你会发现 helloworld 的输出是交错的,说明它们是并发执行的。

4.2. Channel

Channel是goroutine之间通信的管道,用于在goroutine之间传递数据。

“`go
package main

import (
“fmt”
“time”
)

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}

c := make(chan int) // 创建一个channel
go sum(s[:len(s)/2], c) // 计算前半部分的和
go sum(s[len(s)/2:], c) // 计算后半部分的和

x, y := <-c, <-c // 从channel c接收值,这里会阻塞直到有值可接收

fmt.Println(x, y, x+y)

// 带缓冲的channel
bufferedChannel := make(chan int, 2) // 创建一个容量为2的缓冲channel
bufferedChannel <- 1
bufferedChannel <- 2
// bufferedChannel <- 3 // 此时会阻塞,因为容量已满

fmt.Println(<-bufferedChannel)
fmt.Println(<-bufferedChannel)

}
“`

5. 错误处理

Go语言没有异常处理机制,而是通过函数返回多个值(通常最后一个是 error 类型)来表示错误。

“`go
package main

import (
“fmt”
“strconv” // 用于字符串和数字转换
)

func parseAndAdd(s1, s2 string) (int, error) {
num1, err := strconv.Atoi(s1) // Atoi将字符串转换为整型
if err != nil {
return 0, fmt.Errorf(“无法解析第一个数字:%w”, err)
}

num2, err := strconv.Atoi(s2)
if err != nil {
    return 0, fmt.Errorf("无法解析第二个数字:%w", err)
}

return num1 + num2, nil // 成功时返回结果和nil错误

}

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

result, err = parseAndAdd("abc", "20")
if err != nil {
    fmt.Println("错误:", err)
} else {
    fmt.Println("结果:", result)
}

result, err = parseAndAdd("10", "xyz")
if err != nil {
    fmt.Println("错误:", err)
} else {
    fmt.Println("结果:", result)
}

}
“`

6. 指针

Go语言支持指针,但没有指针运算。

“`go
package main

import “fmt”

func main() {
i := 42
p := &i // p指向i的内存地址
fmt.Println(“i的值:”, i)
fmt.Println(“i的地址:”, p)
fmt.Println(“通过指针p获取i的值:”, p) // p 解引用,获取指针指向的值

*p = 21 // 通过指针修改i的值
fmt.Println("i的新值:", i)

j := 10
var ptr *int // 声明一个int类型的指针,默认值为nil
ptr = &j
fmt.Println("j的值:", *ptr)

var nilPtr *int
if nilPtr == nil {
    fmt.Println("nilPtr是nil")
}

}
“`

7. 模块管理

Go 1.11 引入了Go Modules作为官方的依赖管理系统。

  1. 初始化模块
    bash
    go mod init your_module_name

    这会在当前目录创建一个 go.mod 文件。

  2. 添加依赖:当你 import 一个新的包时,Go会自动下载并添加到 go.modgo.sum 文件中。
    bash
    go get github.com/gorilla/mux # 例如,获取一个web框架

  3. 清理依赖
    bash
    go mod tidy

    清理不再使用的依赖。

  4. 构建项目
    bash
    go build

    这将根据 go.mod 中的依赖构建可执行文件。

结语

本教程涵盖了Go语言的基础知识,包括语法、数据结构、函数、并发模型(Goroutine和Channel)、错误处理以及模块管理。掌握这些基础,你就能开始用Go编写高效、并发的应用程序。 Go语言还有更多高级特性,例如接口、反射、嵌入类型等,鼓励你继续深入学习。 实践是最好的老师,多动手编写代码,你将更快地成为Go语言的专家!

滚动至顶部