Golang 入门:初学者教程
Golang,也称为 Go,是一种开源编程语言,由 Google 开发人员 Robert Griesemer、Ken Thompson 和 Rob Pike 于 2007 年创建。它以简单为目的,许多开发人员称赞它可以构建简单、可靠的程序。
自发布以来,Golang 的受欢迎程度日益提升。2009 年和 2016 年,它被评为年度语言。2018 年,它位列第十,并持续在各大组织中崭露头角。
这门语言有很多优势。任何想在谷歌工作的人都应该了解这门语言。所以今天,我想带你深入学习 Go 编程语言。
今天我们将讨论:
- Golang 特性概述
- Go 的基本术语和概念
- Go 的中级概念
- Go 的高级概念
- 资源
Golang 特性概述
这种通用编程语言包含许多其他编程语言的优秀特性。它具有编译性、简洁性、并发性、静态类型和高效性。Go 改进了编程语言的这些方面,并简化了开发人员的工作环境。
Go 本质上是一种兼容并发概念的命令式语言。它保留了面向对象编程的一些优秀特性,例如接口,但也避免了一些陷阱。Go 的设计初衷是摒弃面向对象编程中一些“重量级”的特性。
从这个方面来说,Go 是混合型的,它利用多种语言的最佳特性,具有清晰、富有表现力的类型系统,同时保持轻量级且易于学习。
Go 可用于各种软件开发解决方案,例如系统编程语言、通用编程语言或通用支持。它可以处理以服务器为中心的重型 Web 服务、文本处理问题以及重型分布式应用程序。
为什么要学习 Golang?
熟悉且易于学习。Go属于 C 语言家族,因此它与 Java 和 C++ 等语言拥有许多类似的语法,但 Go 的语法更简洁,因此更易于学习和阅读。与 Python 和 Ruby 类似,它还集成了许多动态编程的特性。
满足开发者需求。Go致力于满足开发者面临的一些常见需求。它加快了软件开发流程,同时又不牺牲效率。Go 旨在通过网络通信、内存管理和速度来支持发展中的市场。
服务端简洁。Go语言让代码的服务器端操作变得简单。Go 标准库提供了标准的 HTTP 协议。
现在我们已经了解了 Go 是什么以及它能带来什么,让我们开始学习基础知识吧。今天,我们将介绍 Go 编程语言的主要概念和核心结构,帮助你入门。和往常一样,我们需要一个更完善的课程来全面讲解 Go 的方方面面。
让我们开始吧。
Go 的基本术语和概念
文件名、关键字、标识符
Go 源代码存储在.go
文件中。所有文件名均为小写,可以使用_
分隔多个单词。与大多数文件名一样,不能使用空格或特殊字符。
Go 中的关键字功能与大多数编程语言类似。这些关键字是保留字,在代码中使用时具有特殊含义。与 Java 或 C++ 不同,Go 的关键字数量要少得多,这使得它更易于使用和学习。这些关键字包括:
标识符类似于关键字,但标识符是由程序员创建的。您可以为变量、模板等元素指定名称。与大多数编程语言一样,标识符区分大小写。它们必须以字母或下划线开头,后跟数字。空白标识符_
可用于声明或变量赋值。此外,还有 36 个预声明标识符,分别是:
基本结构
Go 程序由关键字、运算符、类型、函数和常量组成。代码由语句构成,但不像;
许多其他 C 语言那样需要以 结尾。如果一行中有多个语句,则必须用 分隔;
。
Go 使用与其他语言类似的标点符号,包括.
,
;
:
和...
。
Go 在其代码中使用三个分隔符:( )
[ ]
和{ }
。
数据类型和变量
与许多编程语言一样,变量包含不同类型的数据,这些数据定义了一组值或可对这些值进行操作的操作。在 Go 中,您可以使用四种主要数据类型:
- 基本(又称原始):
int
,,,float
bool
string
- 结构(又称复合材料):
struct
,,,,,slice
map
array
channel
- 接口:描述类型的行为
在 Go 中,结构化类型没有固有值,而是有默认值nil
。
变量是可以在执行过程中更改的值。要声明变量,我们使用关键字var
。
var identifier type = value
在这个例子中,identifier
是变量的名称,type
是变量的类型。与其他 C 语言不同,我们type
在变量后面写identifier
。在 Go 中,当我们声明一个变量时,内存会被初始化。我们还必须使用 运算符为变量赋值=
。这个过程称为赋值变量。
还有一种声明变量的简写方式。
f := "fruit"
fmt.Println(f)
}
运算符
与许多编程语言一样,运算符是执行逻辑或数学运算的内置符号。Golang 中有三种类型的运算符:算术运算符、逻辑运算符和位运算符。
逻辑运算符与其他编程语言类似。然而,Go 对于可比较的值非常严格。这些运算符包括:
- 等号运算符
==
- 不等于运算符
!=
- 小于运算符
<
- 大于运算符
>
- 小于等于运算符
<=
- 大于等于运算符
>=
位运算符适用于具有相等长度位模式的整数变量。一些位运算符如下:
- 按位与运算符
&
- 按位或运算符
|
- 按位异或运算符
^
- 位清除操作符
&^
- 按位补码运算符
^
算术运算符包括+
/
%
和*
。
它们执行常见的算术运算,甚至还有一些快捷方式。例如,
b = b + a
可以缩写为
b += a
字符串
字符串实现了操作 UTF-8 编码字符串的函数。它们UTF-8
默认进行编码,因此可以包含任何语言的字符。它们被定义在双引号之间“ “
,可以包含可变宽度的字符序列,并且是不可变的。
Go 字符串通常比其他语言中的字符串更好,因为它们占用的内存更少,并且由于 UTF-8 标准,您不需要对它们进行解码。
Golang 中有两种字符串字面量:解释型字符串和原始型字符串。解释型字符串用引号括起来,而原始型字符串用反引号括起来。
要声明一个字符串,我们使用string
关键字。查看下面的示例来了解如何操作。
package main
import "fmt"
func main() {
var s string = "Hello, World"
fmt.Printf(s)
}
输出: Hello, World
你可以循环遍历字符串中的字符来访问单个元素。我们使用for
循环,稍后我们会详细讨论。
package main
import "fmt"
func main() {
var s string = "Hello, World"
for index, character := range(s){
fmt.Printf("The character %c is in position %d \n", character, index)
}
}
输出:
字符 H 位于位置 0
字符 e 位于位置 1
字符 l 位于位置 2 字符
l 位于位置 3 字符
o 位于位置 4 字符
, 位于位置 5 字符位于位置 6
字符
W 位于位置 7
字符 o 位于位置 8
你也可以使用 string 将字节值切片转换为字符串。查看示例了解如何操作。
package main
import "fmt"
func main() {
myslice := []byte{0x48, 0x65, 0x6C, 0x6C, 0x6f}
mystring := string(myslice)
fmt.Printf(mystring)
}
输出:你好
时间和日期
在 Golang 中, 包time
提供了测量和显示时间的功能。例如,我们可以使用time.Now( )
来显示当前时间,并t.Day ( )
获取更小的部分。Go 的包有很多有用的功能time
,例如 函数Since(t Time)
,它返回自 以来经过的时间t
。
您也可以创建自己的时间格式。
t := time.Now()
fmt.Printf("%02d.%02d.%4d\n", t.Day(), t.Month(), t.Year()) // e.g.: 29.10.2019
有关 Gotime
包的更多信息,请查看文档。
继续学习。
无需浏览视频或文档即可学习 Golang。> Educative 的基于文本的课程易于浏览并具有实时编码环境 - 使学习变得快速而高效。
Go 的中级概念
控制结构
Go 的控制结构与 C 语言类似,但总体上更加简化和灵活。Go 没有do
orwhile
循环,而是使用灵活的for
andswitch
循环。
还有新的控制结构,例如类型开关和select
。我们不使用括号,并且代码主体使用大括号分隔。让我们深入了解 Go 的控制结构。
if-else
:此结构测试条件语句,可以是逻辑值或布尔值。如果语句为true
,则执行 { } 之间的语句主体。如果为false
,则忽略后面的语句,并执行 之后的语句if
。请记住,即使语句主体中只有一个语句,也必须使用括号。
switch-case
:此结构用于替代if
将变量与值进行比较的长语句。此语句可轻松在代码中转移执行流程。
switch
通常比其他语言更灵活。它采用以下通用形式。
switch var1 {
case val1:
...
case val2:
...
default:
...
}
与构造一样if
,aswitch
也可以包含初始化语句。
switch initialization; {
case val1:
...
case val2:
...
default:
...
}
select
:这个语句意味着我们可以等待多个通道操作,我们稍后会详细讨论。
for-range
:在 Go 中,此语句允许我们迭代一个表达式,该表达式的值可以是数组、切片、映射、字符串或通道。基本语法如下。
for index, value := range mydatastructure {
fmt.Println(value)
}
index
:我们想要访问的值的索引。value
:每次迭代的值。mydatastructure
:保存我们在循环中访问其值的数据结构。
请记住,此示例仅作概括。要了解更多具体案例,请点击此处
for-range
查看 EdPresso循环播放的截图。
功能
函数是 Golang 的基本构建块,因为它与函数式语言共享许多特性。正如我之前提到的,函数是数据,因为它们具有值和类型。一个 Go 程序由多个函数组成。最好从main( )
函数开始,然后按调用顺序(即逻辑顺序)编写它们。
函数将问题分解成更小的任务,使我们能够重用代码。Go 中有三种类型的函数。它们都会在执行完其之前的最后一个语句}
或执行完 return 语句时结束。
- 使用标识符的普通函数
- 匿名函数或 lambda 函数
- 方法
我们使用这种语法编写函数,并使用这种通用格式调用它们。
func g() { // VALID
...
}
我们用这种通用格式来称呼它们。
pack1.Function(arg1,arg2,...,argn)
这function
是一个函数pack1
,arg1
是参数。当我们调用一个函数时,它会复制参数,并将其传递给被调用的函数。
让我们看一个 Go 函数的示例来了解它的实际用法。在这里,我们将深入研究printf( )
Golang 中的这个函数。该print
函数允许您打印格式化的数据。它接受一个模板字符串,其中包含我们将要格式化的文本,以及一些用于指示fmt
函数如何格式化的注解动词。
fmt.printf("Sample template string %s",Object arg(s))
转换字符告诉 Golang 如何格式化数据类型。一些常见的说明符包括:
- v – 以默认格式格式化值
- d – 格式化十进制整数
- g – 格式化浮点数
- b – 格式化二进制数字
假设我们想要打印一个字符串。%s
对话字符可以在模板字符串中使用,以打印字符串值。请看下面的代码。
还有很多其他情况可以使用打印函数。想了解更多,请查看 EdPresso 上关于Golang 打印函数的截图。
package main
import "fmt"
func main() {
var mystring = "Hello world"
fmt.Printf("The string is %s", mystring)
}
输出:字符串是 Hello World
地图
映射 (Map),在其他编程语言中也称为哈希 (Hash) 或字典 (Dict),是 Go 语言中的内置数据类型。其名称解释了它们的用途:映射将键映射到值。可以将映射视为一种存储键值对的方式。
你可以使用这些方法根据键快速查找、检索或删除数据。
我们使用以下语法声明一个映射
var m map[KeyType]ValueType
m
是地图变量的名称KeyType
是映射中键的可选数据类型。这也可以在初始化时声明。ValueType
是键值对中值的数据类型。
映射的长度在声明时无需确定,因此可以动态增长。未初始化映射的值为nil
。
让我们看一个 Golang 中映射的具体示例,了解它们是如何创建的:
package main
import "fmt"
func main() {
var mapLit map[string]int // making map
var mapAssigned map[string]int
mapLit = map[string]int{"one": 1, "two": 2} // adding key-value pair
mapCreated := make(map[string]float32) // making map with make()
mapAssigned = mapLit
mapCreated["key1"] = 4.5 // creating key-value pair for map
mapCreated["key2"] = 3.14159
mapAssigned["two"] = 3 // changing value of already existing key
fmt.Printf("Map literal at \"one\" is: %d\n", mapLit["one"])
fmt.Printf("Map created at \"key2\" is: %f\n", mapCreated["key2"])
fmt.Printf("Map assigned at \"two\" is: %d\n", mapLit["two"])
fmt.Printf("Map literal at \"ten\" is: %d\n", mapLit["ten"])
}
输出:
“one” 处的映射文字为:1
在“key2”处创建的映射为:3.141590
在“two”处分配的映射为:3
在“ten”处的映射文字为:0
数组和切片
Go 中的数组与 Python 类似,但由于数组不够灵活且大小固定,在 Go 代码中并不常见。相比之下,切片更为常见,功能也更加强大。Go 中的切片基于数组构建,因为它是 Go 数组类型的抽象。
要声明数组,我们使用以下语法:
var identifier [len]type
数组的长度是其类型的一部分,因此其大小是固定的。例如,[5]int
表示一个包含五个整数的数组。切片使我们能够克服数组的一些挑战,并在不占用额外内存的情况下处理类型数据序列。
切片是对数组中连续部分的引用,该部分称为底层数组。切片的大小是动态且灵活的。当我们指定两个索引(用冒号分隔)时,就形成了一个切片。我们使用类型规范[ ]T
。 T 是切片中元素的类型。我们使用以下语法声明一个切片:
letters := []string{"a", "b", "c", "d"}
要声明带有切片的变量的类型,我们使用[ ]
切片的元素类型。
package main
import (
"fmt"
"reflect"
)
func main() {
var intSlice []int
var strSlice []string
fmt.Println(reflect.ValueOf(intSlice).Kind())
fmt.Println(reflect.ValueOf(strSlice).Kind())
}
与数组不同,切片在执行过程中可以更改。此外,切片自带内置方法append
,可以返回包含一个或多个新值的切片。该append
方法的语法如下:
slice = append(slice, elem1, elem2, ...)
看看它是怎么做的。
package main
import "fmt"
// Helper function to. print slices
func printSlice(s []int) {
fmt.Printf("length=%d capacity=%d %v\n", len(s), cap(s), s)
}
func main() {
var slice []int // Create an empty slice of type int.
printSlice(slice)
// Append works on nil slices.
slice = append(slice, 0)
printSlice(slice)
// Slices can be appended to as many times.
slice = append(slice, 1)
printSlice(slice)
// We can add more than one element at a time.
slice = append(slice, 2, 3, 4)
printSlice(slice)
}
输出:
长度=0 容量=0 []
长度=1 容量=1 [0]
长度=2 容量=2 [0 1]
长度=5 容量=6 [0 1 2 3 4]
现在我们已经了解了一些 Go 的中级概念,接下来让我们来看看 Golang 带来的一些重要的高级内容。请记住,还有很多内容需要学习。其他一些中级概念包括:
- 递归函数
- 高阶函数
- 结构和方法
- 接口和反射
- 以及更多
Go 的高级概念
错误处理
Go 没有异常处理机制。我们使用内置接口类型error
。它的零值是nil
,所以如果它返回 ,我们就知道没有错误nil
。处理错误的最常见方法是将error
类型作为函数调用的最后一个返回值返回,以检查nil
。让我们看一些代码来了解它是如何实现的。
package main
import "fmt"
import "errors" // Import the errors package.
func divide(x int, y int) (int, error) {
if y == 0 {
return -1, errors.New("Cannot divide by 0!")
}
return x/y, nil
}
func main() {
answer, err := divide(5,0)
if err != nil {
// Handle the error!
fmt.Println(err)
} else {
// No errors!
fmt.Println(answer)
}
}
输出:不能除以 0!
Goroutine
Go 内置了对并发应用程序的支持。并发程序可以同时执行不同的代码片段。构建并发程序的基本模块是goroutine和channel。
与 Java 不同,Go 语言内置了对并发的支持,具体包括特定类型(chan)、关键字(go
、select
)和结构(goroutine)。Go 更强调并发而非并行,因为 Go 程序默认可能并非并行。Go 程序只使用单个核心或处理器,与运行的 goroutine 无关。
那么,什么是 goroutine?它们是与其他方法或函数一起运行的方法或函数。它们取决于我们如何调用它们。你可以把它们想象成线程,但它们更简单、更轻量级。
我们使用关键字go
来创建一个 goroutine,因此当我们调用带有该前缀的函数或方法时,就会执行一个 goroutine。
如果您想要更详细地介绍 Goroutines,请参阅文章《Go 中的 goroutines 剖析》。
你可以使用变量GOMAXPROCS
来告诉运行时有多少个 goroutine 可以执行。GOMAXPROCS
必须将其设置为大于默认值 1,否则所有 goroutine 将共享同一个线程。我们来看一个例子。
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("In main()")
go longWait()
go shortWait()
fmt.Println("About to sleep in main()")
time.Sleep(10 * 1e9) // sleep works with a Duration in nanoseconds (ns) !
fmt.Println("At the end of main()")
}
func longWait() {
fmt.Println("Beginning longWait()")
time.Sleep(5 * 1e9) // sleep for 5 seconds
fmt.Println("End of longWait()")
}
func shortWait() {
fmt.Println("Beginning shortWait()")
time.Sleep(2 * 1e9) // sleep for 2 seconds
fmt.Println("End of shortWait()")
}
输出:
在 main() 中
即将在 main() 中睡眠
开始 longWait()
开始 shortWait()
结束 shortWait()
结束 longWait() 结束
在 main() 结束时
这里的程序表示程序所处的执行阶段的部分。函数main( )
、、shortWait( )
和longWait( )
作为独立的处理单元启动,然后同时工作。
通道与 Goroutine 一起使用,以实现它们之间的通信。它们是用于传输数据的类型化消息队列。可以将其视为一个可以发送类型化值的管道。这样,我们可以避免 Goroutine 之间共享内存。一个通道只能传输一种数据类型,但我们可以将其设置为任意类型。
要声明一个频道,我们使用以下格式
var identifier chan datatype
通道也是引用类型,因此,我们使用make( )
函数来分配内存。下面,看看如何声明一个字符串通道及其实例化。
var ch1 chan string
ch1 = make(chan string)
标准库和包
Go 发行版包含 250 多个内置软件包,并且所有系统的 API 均相同。每个软件包都会为您的 Go 代码引入不同的功能。请参阅此处的文档。
让我们介绍一些常见的软件包,看看它能提供什么。
os/exec
:可以运行外部操作系统命令和程序。syscall
:这是低级的外部包,它为底层操作系统的调用提供了原始接口。archive/tar
和/zip – compress
:包含压缩(解压缩)文件的功能。fmt
:包含格式化输入输出的功能。io
:提供基本的输入输出功能,主要作为操作系统函数的包装器。bufio
:环绕 io 以提供缓冲输入输出功能。path/filepath
:包含针对所使用的操作系统来操作文件名路径的例程。strconv
:将字符串转换为基本数据类型。unicode
:Unicode 字符的特殊功能。regexp
:用于字符串模式搜索功能。
该工具还可以安装外部第三方 Go 包go get
。您需要验证是否已设置 GOPATH 变量,否则,这些包将被下载到$GOPATH/src目录中。点击此处查看。
有超过 500 个实用项目可以添加到您的 Go 程序中。在向现有项目引入新功能时,最好合并现有的 Go 库。这需要您了解库的 API,这会限制调用库的方法。了解 API 后,即可调用库的函数并开始使用。
我们来看一下导入外部库的完整代码。
package main
import (
"fmt"
"time"
"github.com/inancgumus/myhttp"
)
func main() {
mh := myhttp.New(time.Second)
response, _ := mh.Get("https://jsonip.com/")
fmt.Println("HTTP status code: ", response.StatusCode)
}
现在我们对 Go 中的一些高级概念有了一定的了解。还有很多内容需要学习,包括:
- 接口和反射
- 错误测试
- 匿名频道关闭
- 网络、模板和 Web 应用程序
- 最佳实践和陷阱
- 以及更多
资源
Golang 是一门激动人心的语言,它能够加速开发速度并满足您的实际需求。幸运的是,这里有数十个实用的资源可供您学习、练习 Go 语言并与世界分享。快来看看下面的资源,开启您的 Go 之旅吧!
课程
- 《Go 之道》:通过实践学习 Go 的核心结构和技术的权威场所
- Go 编程入门:初学者的详细说明
- 掌握 Go 中的并发性:适合希望提升技能的中级 Go 学习者
文档和指南
- Go 之旅: Go 官方教程
- Golang 官方文档:深入研究代码
- GitHub Go Bootcamp:面向初学者,边练习边学习