链接:https://medium.freecodecamp.org/learning-go-from-zero-to-hero-d2a3223b3d86
开始
想到刚开始学习 Go 的时候,也是不清不楚地本着拿来能用就行的心态,没有系统学习,导致学习过程中踩坑无数。今天发现一篇文章写的很好,Go 语言的特征讲得很细,翻译给需要的初学 Go 的同学。
前言
让我们从 Go(或者称为 Golang)的一个小介绍开始。Go 由 Google 工程师 Robert Griesemer,Rob Pike 和 Ken Thompson 设计。它是一种静态类型的编译语言。第一个版本于 2012 年 3 月作为开源发布。
“Go 是一种开源编程语言,可以轻松构建简单,可靠,高效的软件”
在许多语言中,有许多方法可以解决给定的问题。程序员可以花很多时间思考解决问题的最佳方法。
另一方面,Go 相信更少的功能 — 只有一种正确的方法来解决问题。
这节省了开发人员的时间,并使大型代码库易于维护。
“功能越多,成本越高” — Rob Pike
入门
Go 由package
组成。名为main
的包告诉 Go 编译器被编译为可执行文件,而不是作为 library 被引用。它是应用程序的主入口。主包定义为:
package main
让我们在 Go 工作区中创建一个简单的 hello world 示例。
工作区
Go 中的工作空间由环境变量定义,称为 GOPATH
。
你的任何代码都将写在工作区内。Go 将搜索GOPATH
目录中的任何包,或者GOROOT
在安装 Go 时默认设置的目录。注:GOROOT
是安装 go 的路径。
设置GOPATH
为所需的目录。现在,让我们将其添加到文件夹中~/workspace
。
# 定义GOPATH目录
export GOPATH=~/workspace
# 进入工作区目录
cd ~/workspace
在我们刚刚创建的工作区文件夹中创建main.go
文件并写入以下代码。
Hello World!
package main
import (
"fmt"
)
func main(){
fmt.Println("Hello World!")
}
在上面的示例中,Go 中的官方包fmt
实现了格式化I/O的函数。
我们使用import
关键字在 Go 中导入fmt
包。func main
是代码执行的主入口。Println
是fmt
包内的一个函数,我们用它来打印“hello world”。
让我们先运行这个文件看看。可以通过两种方式运行 Go 命令。众所周知,Go 是编译型语言,所以我们要在执行之前编译它。
go build main.go
这将创建一个二进制可执行文件main
,现在我们可以运行:
./main
# Hello World!
还有另一种更简单的方式来运行程序。使用go run
命令有助于抽象编译步骤。只需运行以下命令即可编译并执行该程序。
go run main.go
# Hello World!
注意: 你可以在 https://play.golang.org/ 练习以上代码
变量
Go 中的变量是明确声明的。Go 是一种静态类型语言。这意味着在变量声明时检查变量类型。
声明变量方法如下:
var a int
在这种情况下,a
将自动设默认值为 0。
使用以下语法声明变量并赋值:
var a = 1
这里变量自动指定为 int 类型。我们可以使用变量声明的简写方式:
message := "hello world"
还可以在同一行中声明多个变量:
var b, c int = 2, 3
数据类型
与任何其他编程语言一样,Go 支持各种不同的数据结构。让我们继续学习:
数字,字符串和布尔值
Go 支持的整数类型包括
int, int8, int16, int32, int64,uint, uint8, uint16, uint32, uint64, uintptr ...
字符串类型存储一系列字节。它用关键字string
声明
布尔值使用关键字bool
声明
Go 还支持复数类型数据类型,可以使用complex64
和complex128
声明。
var a bool = true
var b int = 1
var c string = 'hello world'
var d float32 = 1.222
var x complex128 = cmplx.Sqrt(-5 + 12i)
Array, Slice, 和 Map
数组array
是相同数据类型的元素序列。数组具有在声明中定义的固定长度,因此不能进行扩展。数组声明为:
var a [5]int
数组也可以是多维的。我们可以使用以下格式创建多维数组:
var multiD [2][3]int
数组的值在运行时无法更改,也不提供获取子数组的能力。为此,Go 有一个名为切片slices
的数据类型。
切片slices
存储一系列元素,可以随时扩展。切片声明类似于数组声明
未定义容量的 slice:
var b []int
这将创建一个零容量和零长度的切片。
切片也可以定义容量和长度。我们可以使用以下语法:
numbers := make([]int,5,10)
上面定义的切片的初始长度为 5,容量为 10。
切片是数组的抽象。切片使用数组作为底层结构。切片包含三个组件:容量,长度和指向底层数组的指针,如下图所示:
图片源:https://blog.golang.org/go-slices-usage-and-internals
通过使用append()
或copy()
函数可以增加切片的容量。append 函数可以为数组的末尾增加值,并在需要时增加容量:
numbers = append(numbers, 1, 2, 3, 4)
增加切片容量的另一种方法是使用copy()
函数。只需创建另一个具有更大容量的切片,并将原始切片复制到新创建的切片:
// 创建新的切片
number2 := make([]int, 15)
// 将原始切片复制到新创建的切片
copy(number2, number)
我们可以创建切片的子切片。这可以使用以下命令完成:
// 声明一个内含{1,2,3,4}共 4 个元素的切片
number2 = []int{1,2,3,4}
fmt.Println(numbers) // -> [1 2 3 4]
// 创建子切片
slice1 := number2[2:]
fmt.Println(slice1) // -> [3 4]
slice2 := number2[:3]
fmt.Println(slice2) // -> [1 2 3]
slice3 := number2[1:4]
fmt.Println(slice3) // -> [2 3 4]
映射表map
是 Go 中的数据类型,它将键映射到值。我们可以使用以下代码定义键值对映射:
var m map[string]int
我们声明了一个名为m
的map
类型的变量,其键是string
类型,值是int类型
。我们可以轻松地将键和值添加到map
中:
// 添加 key/value
m['clearity'] = 2
m['simplicity'] = 3
// 打印输出 value
fmt.Println(m['clearity']) // -> 2
fmt.Println(m['simplicity']) // -> 3
类型转换
可以使用类型转换将一种类型的数据类型转换为另一种类型。我们来看一个简单的类型转换:
a := 1.1
b := int(a)
fmt.Println(b)
//-> 1
并非所有类型的数据类型都可以转换为其他类型。确保数据类型与转换类型兼容。
流程控制
if else
我们可以使用 if-else 语句,如下例所示。确保花括号与条件位于同一行。
if num := 9; num < 0 {
fmt.Println(num, "小于 0")
} else if num < 10 {
fmt.Println(num, "是一位数")
} else {
fmt.Println(num, "是多位数")
}
switch case
switch-case 有助于组织多个条件语句。以下实例表示一个简单的 switch case 语句:
i := 2
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
default:
fmt.Println("none")
}
循环
Go 使用for
来实现不同类型的循环:
i := 0
sum := 0
for i < 10 {
sum += 1
i++
}
fmt.Println(sum)
上面的示例类似于 C 中的 while 循环。对于 for 循环,还可以使用相同的 for 语句:
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
Go 中的无限循环:
for {
}
指针
Go 提供指针。指针是保存值的内存地址的地方。指针由*
定义。
根据数据类型定义指针,例如:
var ap *int
变量ap
是指向整数类型的指针。使用&
操作可用于获取变量的地址。
a := 12
ap = &a
可以使用*
运算符访问指针指向的值:
fmt.Println(*ap)
// 12
在将结构作为参数传递或者为已定义类型声明方法时,通常首选指针。
- 传递值时,实际复制的值意味着更多的内存
- 传递指针后,函数更改的值将反映在方法/函数调用者中
例如:
func increment(i *int) {
*i++
}
func main() {
i := 10
increment(&i)
fmt.Println(i)
}
// 11
注意: 在博客中尝试示例代码时,不要忘记将其包含在 main 包中,并在需要时导入 fmt 或其他包,如上面第一个 main.go 示例所示。