Go语言从入门到实战(一)

Go语言从入门到实战(一)

一、为什么要学习Go语言

在开始我们的学习旅程之前,我们先来了解一下Go语言,通过建立一个宏观的印象,来熟悉一下Go这门语言,消除陌生的感觉。

首先要强调的是,Go是一门通用语言,这也就意味着,不管是JavaC#Python还是PHP,这些语言可以做到的事情,理论上Go都可以做;Go可以做到的事情,理论上这些语言也都可以做到。

因此,我们在选择一门技术的时候,都是选择去看他最擅长的方面。

但是先别急,我们不妨先去探究一下,Go为什么会被发明?或者说,Go是为了解决什么样的问题而诞生的呢?当我们搞清楚这个问题,Go语言擅长做什么这个问题,也就不言自明了。

2007年前后,软件开发迎来了新挑战,主要包括:

  • 摩尔定律逐渐失效,多核硬件架构的广泛应用
  • 超大规模分布式计算集群
  • Web模式导致的前所未有的开发规模和更新速度

而为了解决这些问题,Google的三位大牛合作创造了Go语言:Rob Pike、Ken Thompson、Robert Griesemer。

  • Rob Pike:Unix早期开发者、UTF-8创始人、Plan9操作系统创始人

  • Ken Thompson:Unix创始人、B语言创始人、UTF-8创始人、参与设计正则表达式、1983年图灵奖获得者

  • Robert Griesemer:Google V8 JS Engine开发者、Java Hot Spot开发者

所以我们学习Go语言,其最主要的应用场景就是上面所提到的:多核架构下,超大规模集群的项目的开发。

而天生目标明确的Go语言,毫无疑问的具备鲜明的特点,让我们来一一了解。

二、Go语言的特点

2.1 简约

Go语言所倡导的思想是“Less is more”,他的创造者们希望能用尽量简单的方法,去完成我们的工作,但是Go语言的简约不等于其简单,他的威力丝毫没有因为其简约的设计而弱于其他的编程语言。

Go语言设计的简约性可以体现在以下几个方面:

首先,就是关键字的数量:

C Go C++ 11
37 25 84

我们都知道C是一门简单而强大的系统级语言,而Go甚至做到了关键字的数量比他还要少;而C++是与Go语言的理念截然相反的,C++几乎吸收了所有他能吸收的特性,导致了他虽然功能强大,但是过于复杂。

2.2 性能

Go语言的特点不仅仅在于简约的设计,更少不了的是他强大的执行速度。

大家都知道Python也是一门上手迅速的编程语言,深受很多程序员的喜爱,然而他的问题就在于作为一门解释性语言性能较弱,因此,Python常用于一些对性能要求不高的地方,如充当“胶水语言”。

Go作为一门编译型语言,其拥有超越Java的执行速度;又因为他的简约的设计,他还拥有接近Python的开发速度。同时具备这两个特性,使得Go语言充满了吸引力。

2.3 组合的编程思想

组合是一种比起传统面向对象编程语言当中的继承更加灵活的编程思想。

组合has-a的关系,而继承is-a的关系。这种设计的优越性在于可以进一步的降低耦合,实现更小的封装从而保证功能的专注和单一。我们会在后面的学习中更加深入地感受到这种设计的优越性。

当然,没有最好的设计,只有更适合的设计。组合并不能全盘否定继承的价值。不过对于Go语言来说,组合更加接近其设计思想。

因此,我们在学习和使用Go的过程中,需要训练自己使用组合的方式来思考问题,而不是把Go的代码,写成了Go语法版本的Java

三、Go语言的Killer Application

对于一门技术来说,最为重要的除了其即将能创造的价值以外,还有就是已经创造的价值。对一门编程语言来说,如果它曾经实现过杀手级应用,那么毫无疑问,对于该语言的推动与发展都是大有裨益的。

因为,这一方面杀手级应用证明了该语言开发复杂系统的能力,而不仅仅是一个大牛开发的玩具;另一方面,杀手级应用可以将部分应用自身的用户转化为开发该应用的技术的使用者。

杀手级应用Killer application)是指一个极具价值的计算机程序或服务,消费者愿意为这个程序或服务购买特定硬件、软件产品或服务。——中文维基

Go语言就拥有数个杀手级应用:

用于云计算的两个关键应用是Go语言开发:

  • Docker
  • Kubernetes

用于区块链的杀手级应用和Go兼容最好:

  • Ethereum
  • Hyperledger

以及不得不提的:Go语言自身的编译器也是使用Go语言实现。这样的方式被称为:自举

四、Go语言的安装和准备工作

4.1 下载Go安装程序

因为Google退出中国大陆的原因,我们正常无法直接从官方网站下载Go安装程序,推荐前往地址:Go语言中文网下载。

如果是Mac OS的用户,可以使用home brew安装。

4.2 官网与文档

Golang的官网是https://golang.org,不过因为上面提到的原因,国内不能正常访问,那么替代方案有这么几种:

  • 方案一:Google CN提供的镜像网站:该网站最大的好处就是原版英文,可以获得等同于原官网的体验

  • 方案二:中文版官网:该网站已经部分汉化了,可以帮助阅读英文有困难的同学们

  • 方案三:使用Golang起一个本地服务:安装Golang后,在命令行里输入godoc -http=:8080,并在浏览器访问http://localhost:8080/即可访问本地官网(英文),8080是端口号,可以更改,这种方式优点在于不依赖网络

  • 方案四:https://tip.golang.org/是golang.org的完全限定域名,由Google提供,目前中国大陆可以正常访问

提醒:针对上面的【方案三】,需要补充说明一点:新版的Go安装程序已经不在包含godoc,必须自行安装godoc(中国大陆需要代理)。

4.3 Go环境配置

在此之前,我们先学习两个命令:

  1. 查看当前Go语言版本:go version
  2. 查看当前Go语言环境设置:go env

好,那我们开始吧:

配置环境变量GOPATH是至关重要的一个环节。所谓GOPATH,就是我们存放代码、编译程序的工作目录。

可以自己随意选择一个目录作为“GoPath”,然后将其配置到环境变量当中(具体配置方法可以参考【4.4设置代理】)

GoPath路径下的目录结构应该是这样的:

1
2
3
4
GOPATH
|-src(必要,请自行创建)
|-pkg
|-bin

其中,我们的代码就存放在src目录下,包括我们从网络上拉取的第三方包,也会存放在这里。

建议:你的GoPath路径/bin【追加】到环境变量PATH里(请自行解决),以确保可以调用Go语言开发的工具

提示:从版本1.11开始,我们并不一定要将自己的代码写在GoPath目录下,具体请参见后面介绍“Go Mod”的文章

4.4 设置代理

Go为我们提供了非常好用的工具go get,可以直接从网络拉取依赖包(例如:github),但是由于下载速度并不理想,且部分存放在Google服务器上的包文件无法正常获取(中国大陆),我们还是通过配置代理来解决吧。

推荐方法:

如果你的Go版本大于等于1.13,可以直接使用以下命令配置代理:

1
$ go env -w GOPROXY=https://goproxy.cn,direct

通用方法:

如果上述推荐方法不适用,或者没有效果,那么我们可以手工配置:

  • Windows:添加系统环境变量,GOPROXY,值为https://goproxy.cn;如果你的版本大于等于1.13,值则为https://goproxy.cn,direct
  • Mac:在目录~/.bash_profile文件下添加:export GOPROXY=https://goproxy.cn,保存并退出;如果你的版本大于等于1.13,则为export GOPROXY=https://goproxy.cn,direct
  • Mac(补充):如果你的shell不是bash而是zsh,那么则是在该文件下操作:~/.zshrc。或者,依然在~/.bash_profile文件下操作,但是必须将source ~/.bash_profile配置添加入~/.zshrc
  • Mac(提醒):你可以执行source ~/.bash_profile~/.zshrc使环境变量配置立刻生效

至此,你的go get命令可以正常工作了。

4.5 开发工具

Go语言可以使用的开发工具还是比较丰富的,这里稍微列举几种:

  1. GoLand:Jetbrains家的专业IDE,和大名鼎鼎的Java IDE工具“intellij idea”同门。缺点:收费
  2. liteide:由中国人自己开发的Go语言IDE工具,免费。下载说明在此,建议下载二进制文件,而不是手动编译
  3. Eclipse + Goclipse:经典IDE Eclipse通过安装插件支持Go语言开发,免费
  4. VsCode、Atom、Sublime等 + 插件:代码编辑器(Editor)相比IDE更加轻量,同样免费
  5. Vim、Emacs + 插件

五、Go快速上手

在第一篇文章的最后,我们一起来快速上手一下Go代码的编写吧。

5.1 Hello World

  1. 在你的GoPath目录下的src目录内,创建项目目录(名称自取)。

  2. 在项目目录下,创建文件hello.go(名称自取)

1
2
3
4
5
6
7
8
9
10
11
12
13
// 声明包
package main

// 导入包
import (
"fmt"
)

// 主函数
func main() {
// 向控制台输出文本内容
fmt.Println("Hello World")
}

运行代码:

.go文件所在目录下,执行命令:go run hello.go

控制台输出:

1
Hello World

5.2 解释说明

第1行:

Go语言的单行注释,不参与程序执行

多行注释则为:

1
2
3
/*
fmt.Println("Hello World")
*/

第2行:

Go和其他很多语言一样,使用来管理和组织代码,其中规则如下:

  1. 同一个目录下,所有.go文件的包名必须一致
  2. main包下,必须有且只能有一个main函数
  3. 目录的名称不一定非要和包名一致
  4. main方法所在的文件的文件名不要求是main.go

第5、6、7行:

导入依赖包,其中规则如下:

  1. 当需要导入的包仅有一个时,可以简写成一行:import "fmt"
  2. 包可以设置别名,如import f "fmt"
  3. 导入的包,一般必须使用,否则编译不通过
  4. 如果不使用导入的包,且想要通过编译,需要将下划线_作为包别名:import _ "fmt"

第10行:

main函数是整个程序的入口,具体规则如下:

  1. 该示例中,main函数声明为唯一写法。即:没有参数列表、没有返回值
  2. main包下必须有一个main函数,并且只能有一个main函数
  3. Go语言中,所有函数声明均是使用func关键字
  4. Go语言中,函数体使用花括号包围,且左花括号不能另起一行

第12行:

函数的调用,具体规则如下:

  1. 函数&表达式结尾均不需要添加分号
  2. 一般情况下,调用包函数必须写上包名.。如果需要省略这个部分,可以使用点.作为包别名:import . "fmt"
  3. Go语言中,函数名&变量名的首字母大小写均代表包外可见性。大写代表可见,反之不可见

5.3 程序退出码

我们可以用程序退出码来表示程序的执行结果,如:返回0代表执行成功,其他则代表出现异常

由于Go语言main函数不支持返回值,所以我们需要给os包下的Exit()函数传值实现

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
"os"
)

func main() {
fmt.Println("Hello World")
// 设置程序退出码
os.Exit(0)
}

注意:这里的退出码范围为0~255,如果传递-1,则会当作255

5.4 传递命令行参数

实用的命令行工具都需要传递参数给我们的程序,由于Gomain函数没有参数列表,所以需要使用os包下的Args()函数去接收命令行参数

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"
"os"
)

func main() {
// 获取并打印命令行参数
fmt.Println(os.Args[1])
}

此处Args()函数返回的是一个字符串切片,关于字符串切片我们后面会学习到。这里我们只需要明白,[0]代表取出列表中的第一个元素,而[1]就是取出第二个元素

Args()函数返回的第一个元素(即下标0)永远是程序本身,而第二个元素(下标1)就是我们传入的第一个参数,以此类推…

执行上面的代码:

1
$ go run Hello.go haha

控制台输出:

1
haha