1 Go 语言背景
Go 语言被称为 21 世纪的 C 语言,最终的目标据说是设计网络和多核时代的 C 语言。
Go 语言的历史可以说是充满了奇闻和趣事,故事要从 2007 年说起,当时谷歌的工程师们因为软件开发过程中频繁使用的 C++存在一些痛点,比如编译速度慢、语言复杂等。于是,由 Robert Griesemer、Rob Pike 和 Ken Thompson 三位谷歌工程师联合发起了一个新的项目,旨在开发一门更高效、更简洁的编程语言。
他们的努力在 2009 年开始见到成果,那一年的 3 月,Go 语言正式面向全球开源,并发布了首个公开的版本。
Go 语言的设计目标是提供一种简洁、高效和易于使用的编程语言,以满足现代软件开发的需求。它借鉴了其他语言的优点,同时也摒弃了一些复杂性和冗余性。Go 语言拥有强大的并发性能和内置的垃圾回收机制,使其成为构建高性能和可伸缩的应用程序的理想选择。
有趣的是,Go 语言的名称源自游戏世界。根据创始人之一 Rob Pike 的说法,他们希望给这门语言一个简短而有力的名称,于是取名为"Go",寓意着前进、快速和简洁。
2 为什么要学习 Go 语言
学习 Go 语言(也被称为 Golang)有多种原因,它是一门现代的、高效的编程语言,适用于各种应用场景。以下是学习 Go 语言的一些主要原因:
- 高效性能: Go语言被设计成具有高效的执行性能。它的编译速度快,运行时性能优秀,适用于需要快速响应时间的应用程序,如网络服务和分布式系统。
- 并发支持: Go语言内置了强大的并发支持,通过goroutines和channels可以轻松实现并发编程,这对于构建高性能、高并发的应用程序非常有用。
- 内置工具: Go语言提供了丰富的标准库,包括网络、文本处理、加密、测试等方面的工具,使开发变得更加便捷。
- 静态类型检查: Go是一门静态类型语言,它在编译时进行类型检查,有助于减少运行时错误,提高代码的稳定性和可维护性。
- 跨平台支持: Go语言支持多个操作系统和体系结构,可以编写跨平台的应用程序。
- 开源社区: Go语言拥有活跃的开源社区,提供了大量的第三方库和工具,使开发人员能够更轻松地构建各种应用。
- 学习曲线低: Go语言的语法相对简单,与其他编程语言相比,学习曲线较低,新手可以相对快速地上手。
- Google支持: Go语言是由Google开发的,Google在许多项目中使用Go语言,这表明它在大规模系统开发方面有强大的支持。
- 云原生和微服务: Go语言在云原生和微服务架构中得到广泛应用,如Docker和Kubernetes等重要工具都是用Go语言编写的。
3 快速开始 Hello World
下面是在 Mac 上的实现:
3.1 安装 Go 环境
- 访问官方Go下载页面 https://go.dev/dl/ ,选择适合你操作系统的安装包,并下载最新版本的 Go 安装程序。
- 执行下载的安装程序,并按照提示进行安装。默认情况下,Go会安装到一个默认的目录,不需要进行额外配置。
- 验证 Go 是否安装成功,打开终端或命令行窗口,运行以下命令:
go version
如果显示 Go 的版本号,说明 Go 已成功安装。
3.2 编写第一个 go 程序
-
打开你喜欢的文本编辑器,创建一个新文件,将以下代码复制并粘贴到文件中:
package main import "fmt" func main() { fmt.Println("Hello, World!") }
这是一个简单的 Go 程序,它会输出"Hello, World!"。
-
保存文件,通常使用
.go
扩展名,比如hello.go
。
在上面的代码中,我们定义了一个包,例如 main
package main
我们引入了一个标准的包:
import "fmt"
最后是我们最重要的 main
函数,它是应用程序的入口,就像 C、Java 或 C# 等其他语言一样。
func main() {
...
}
3.3 运行 Go 程序
-
打开终端或命令行窗口,导航到包含你的 Go 程序文件的目录。
-
使用以下命令来编译和运行程序:
go run hello.go
如果一切正常,你将在终端上看到输出结果:"Hello, World!"。
这就是编写并运行第一个 Go 程序的基本步骤。你现在已经安装了 Go 环境,可以开始编写更多的 Go 代码并探索 Go 语言的功能和库。请注意,Go 具有强制的代码格式要求,你可以使用 gofmt
工具来格式化你的 Go 代码以符合标准。
Go 语言是编译型的语言,Go 的工具链将程序的源文件转变成为机器相关的二进制指令,这些工具可以通过单一的 go 命令配合其子命令进行使用,最简单的命令就是 run,它将一个或多个 .go
为后缀的源文件进行编译,链接,最后生成可执行文件。
如果我们不是一次性的实验,我们可以先编译输出一个可复用的程序,可以通过 build 来实现:
go build hello.go
上面的命令会生成一个 hello 的二进制程序,可以不经过任何的处理,随时可以运行:
./hello
4 Go 语言编程基本认识
4.1 包
Go 的标准库中有 100 多个包用来完成输入,输出,排序,文本处理等任务,比如我们常用的 fmt 包中的函数大多数是用于格式化的输入输出的。
main 包比较特殊,用于定义一个独立的可执行程序,而不是库。同样的,main 包中的 main 函数也是特殊的存在,main 函数相当于程序的入口,总是在程序开始执行的地方。
我们如果需要使用某一个包的内容,需要精准地导入该包,比如:
import(
"fmt"
"os"
)
4.2 函数
一个函数的声明由 func 关键字,函数名,参数列表(main 函数的参数是空的),返回值列表(可以为空),放在大括号内的函数体组成,函数体定义函数时用来做什么的。
Go 语言不需要再语句或者声明后面加上分号,除非是多个语句或者声明出现在同一行。Go 语言对于代码的格式化要求非常的严格,比如函数定义的 “{” 必须和关键字 “func” 在同一行,不能独自作为一行。
4.3 循环
Go 语言中有两种主要的循环结构:for
循环和 range
循环。下面我会分别介绍这两种循环的用法。
for
循环在 Go 语言中可以用来执行一段代码块多次,通常有三种基本形式:
4.3.1 基本的 for
循环:
for 初始化语句; 条件表达式; 后续语句 {
// 循环体
}
示例:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
4.3.1.1 省略初始化语句的 for
循环:
for ; 条件表达式; 后续语句 {
// 循环体
}
示例:
j := 0
for ; j < 5; j++ {
fmt.Println(j)
}
4.3.1.2 省略初始化语句和后续语句的 for
循环:
for ; 条件表达式; {
// 循环体
}
示例:
k := 0
for k < 5 {
fmt.Println(k)
k++
}
4.3.1.3 无限循环:
for {
// 无限循环体
}
示例:
for {
fmt.Println("无限循环")
}
4.3.2 range
循环
range
循环用于迭代数组、切片、映射(map)、字符串等数据结构的元素。它的基本语法如下:
for index, value := range 集合 {
// 循环体
}
示例:
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
fmt.Printf("索引:%d,值:%d\n", index, value)
}
上述示例中,range
循环会依次将切片 numbers
中的元素的索引和值赋值给 index
和 value
变量,并执行循环体。
这就是 Go 语言中两种常见的循环方式,我们可以根据具体的需求选择合适的循环结构。
4.4 声明
Go 语言支持多种声明方式,用于声明变量、常量、类型等。以下是 Go 语言中常见的声明方式:
-
变量声明:
var variableName dataType // 声明一个变量
示例:
var age int // 声明一个整数变量
或者使用简短声明方式:
variableName := value // 使用简短声明方式初始化并声明一个变量
示例:
name := "Alice" // 声明一个字符串变量并初始化为 "Alice"
-
常量声明:
const constantName dataType = value // 声明一个常量
示例:
const pi = 3.1415926 // 声明一个浮点数常量
-
类型声明:
type typeName typeLiteral // 声明一个自定义类型
示例:
type Celsius float64 // 声明一个自定义的摄氏度类型
-
多个变量或常量同时声明:
var ( variable1 dataType1 = value1 variable2 dataType2 = value2 // ... )
示例:
var ( name = "Bob" age = 30 email = "bob@example.com" )
-
短声明方式声明多个变量:
var ( variable1, variable2, variable3 = value1, value2, value3 )
示例:
var ( x, y, z = 1, 2, 3 )
-
声明并初始化映射(map)、切片(slice)、通道(channel)等复合类型:
mapVariable := make(map[keyType]valueType) sliceVariable := make([]dataType, length, capacity) channelVariable := make(chan dataType)
示例:
scores := make(map[string]int) names := make([]string, 0, 10) dataChannel := make(chan int)
这些是 Go 语言中常见的声明方式,我们可以根据需要选择适合的方式来声明变量、常量和自定义类型。
4.5 类型
Go 语言具有多种基本数据类型和复合数据类型。以下是 Go 语言中常见的数据类型:
4.5.1 基本数据类型
-
整数类型(Integer Types):
int
:有符号整数类型,根据计算机架构可以是 32 位或 64 位。int8
、int16
、int32
、int64
:有符号整数类型,分别占 8、16、32、64 位。uint
:无符号整数类型,根据计算机架构可以是 32 位或 64 位。uint8
、uint16
、uint32
、uint64
:无符号整数类型,分别占 8、16、32、64 位。
-
浮点数类型(Floating-Point Types):
float32
:单精度浮点数类型。float64
:双精度浮点数类型。
-
复数类型(Complex Types):
complex64
:包含 32 位实部和 32 位虚部的复数类型。complex128
:包含 64 位实部和 64 位虚部的复数类型。
-
布尔类型(Boolean Type):
bool
:表示布尔值,只有两个可能的值:true
和false
。
-
字符串类型(String Type):
string
:表示文本字符串。
-
字符类型(Character Type):
byte
:uint8
的别名,通常用于表示 ASCII 字符。rune
:int32
的别名,通常用于表示 Unicode 字符。
4.5.2 复合数据类型
-
数组类型(Array Types):
array
:具有固定长度的数组,元素类型相同。
-
切片类型(Slice Types):
slice
:可变长度的切片,基于数组创建。
-
映射类型(Map Types):
map
:键值对集合,用于实现关联数组。
-
结构体类型(Struct Types):
struct
:用户自定义的复合数据类型,可以包含不同类型的字段。
-
通道类型(Channel Types):
chan
:用于在 Go 协程之间传递数据的通信机制。
-
函数类型(Function Types):
func
:函数类型,可以作为参数传递给其他函数。
-
接口类型(Interface Types):
interface
:一组方法的抽象集合,用于定义对象的行为。
-
指针类型(Pointer Types):
pointer
:指向某个变量内存地址的指针类型。
这些是 Go 语言中的常见数据类型。每种类型都有其用途和特点,你可以根据需求选择合适的类型来定义变量和数据结构。
4.6 并发
Go 语言(也称为 Golang)是一种支持并发编程的编程语言,它内置了许多特性和工具,使并发编程更加容易和高效。Go 语言的并发模型基于 Goroutines 和 Channels。
以下是一些关于 Go 语言并发编程的基本概念和技术:
-
Goroutines(协程):Goroutine 是 Go 语言中的轻量级线程,由 Go 语言的运行时系统管理。与传统的线程相比,Goroutines 的创建和销毁成本非常低,因此可以轻松创建成千上万个 Goroutines。你可以使用
go
关键字来创建一个新的 Goroutine。go someFunction() // 创建一个新的Goroutine来执行someFunction
-
Channels(通道):通道是 Goroutines 之间用于通信的安全机制。它们可以用来传递数据或信号。通道有两种类型:带缓冲的通道和非缓冲通道。通道可以用于同步和协调 Goroutines 之间的操作。
ch := make(chan int) // 创建一个整数类型的非缓冲通道 ch <- 42 // 向通道发送数据 value := <-ch // 从通道接收数据
-
并发控制:Go 语言提供了一些工具,如
sync
包中的WaitGroup
和Mutex
,用于控制并发程序的执行流程和共享资源的访问。WaitGroup
用于等待一组 Goroutines 完成,而Mutex
用于实现互斥锁,以确保共享数据的安全访问。var wg sync.WaitGroup var mu sync.Mutex
-
Select 语句:
select
语句用于处理多个通道操作,允许你选择首先准备好的通道来执行操作,从而避免 Goroutines 在等待某个通道操作时被阻塞。select { case result := <-ch1: // 处理ch1的数据 case result := <-ch2: // 处理ch2的数据 }
-
并发模式:Go 语言支持多种并发模式,包括生产者-消费者、工作池、并行迭代等。你可以根据具体的应用场景选择合适的模式。
下面是一个简单的 Go 语言并发编程示例,演示了如何使用 Goroutines 和 Channels 来实现并发执行:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
// 启动两个Goroutines
go produce(ch)
go consume(ch)
// 等待一段时间,以便Goroutines完成
time.Sleep(10 * time.Second)
}
func produce(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
time.Sleep(1 * time.Second)
}
close(ch)
}
func consume(ch chan int) {
for val := range ch {
fmt.Println("Received:", val)
}
}
这个示例中,一个 Goroutine 用于生成数据,另一个 Goroutine 用于消费数据,它们通过通道进行通信。这种模式可以应用于各种并发编程场景,如并行处理任务、数据流处理等,结果如下:
4.7 简单 web 编程
使用 Go 语言编写简单的 Web 应用程序通常涉及使用 Go 的标准库中的 net/http
包。以下是一个创建简单 Web 服务器的示例:
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
}
func main() {
http.HandleFunc("/hello", helloHandler)
// 启动Web服务器,监听端口8080
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
在上面的示例中,我们首先导入 net/http
包,然后定义了一个 helloHandler
函数,该函数会在用户访问 /hello
路径时执行。http.HandleFunc
函数用于将 helloHandler
与指定的 URL 路径关联起来。
最后,我们使用 http.ListenAndServe
函数启动一个 Web 服务器,监听端口 8080。当你访问 http://localhost:8080/hello
时,服务器会响应并显示 "Hello, World!"。
这只是一个简单的示例,Go 的 net/http
包提供了更多的功能,允许你构建更复杂的 Web 应用程序,包括路由、中间件、模板渲染等。你还可以考虑使用外部的 Web 框架,如 Gin 或 Echo,来简化 Web 应用程序的开发。
5 小结
当你决定学习 Go 语言时,你正在迈出一步通向编程世界的精彩旅程。Go 是一门强大而富有表现力的编程语言,它具备一系列独特的特性,让你能够构建高效、可维护、并发安全的应用程序。
编程是一项持之以恒的工作,就像是长途跋涉。它不一定总是轻松的,但在你坚持不懈的过程中,你将逐渐见证自己的成长。每一行代码都是你通向成功的一步,即使它们是缓慢而小小的一步。