Go语言新手入门浅谈
一、 前言
由于工作中存在与Go语言相关的内容,因此最近花费部分时间对Go语言进行了解,从基础语法开始对Go语言开始学习。Go语言语法简单,类C语法的特性导致学习Go语言学习容易,能够极快上手,然而若是希望深入理解Go语言仍需在项目实践中不断锤炼。
本篇文章首先浅谈我对Go语言诞生环境、语言特色等内容的了解,并且总结Go语言的基础语法,以作交流。本篇文章从一个新手学习Go语言的角度编写,若文中存在需修正之处,欢迎评论留言指正。
二、 Go语言行业应用
Go语言是2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人。Go语言于2009年11月开源,在2012年3月发布了Go 1.0稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区[1]。
Go语言的诞生存在其特殊的氛围背景。2007年,Google的首席软件工程师Rob Pike在等待C++编译的漫长等待过程中,拉上另外两位大佬,Robert Griesemer和Ken Thompson,期望能够创造出另外一门能够拥有接近C的执行性能、接近解析性语言的开发效率以及高效的编译速度的开发语言取代C++。三位大佬将此项目命名为Golang,并将它作为自由时间的实验项目。在大约一年后,Google发现了Go语言的巨大潜力并且全力支持,最终发展到目前的地步。此诞生背景最终决定了Go语言的两大特色:类C特性以及为服务端服务。
项目名称 | 项目简介 |
---|---|
Docker | golang头号优秀项目,通过虚拟化技术实现的操作系统与应用的隔离,也称为容器; |
Kubernetes | 由google开发,简称k8s,k8s和docker是当前容器化技术的重要基础设施; |
Etcd | 一种可靠的分布式KV存储系统,有点类似于zookeeper,可用于快速的云配置; |
TiDB | 国内PingCAP团队开发的一个分布式SQL数据库,国内很多互联网公司在使用; |
InfluxDB | 时序型DB,着力于高性能查询与存储时序型数据,常用于系统监控与金融领域; |
CockroachDB | 云原生分布式数据库,继NoSQL之后出现的新的概念,称为NewSQL数据库; |
Beego | 国人开发的一款及其轻量级、高可伸缩性和高性能的web应用框架; |
go-kit | Golang相关的微服务框架,这类框架还有go-micro、typhon; |
go-ethereum | 官方开发的以太坊协议实现; |
除了上表中展示的项目之外,国内外各大互联网巨头也逐渐将Go语言作为云上服务的技术栈使用。例如:以今日头条为例,早在2017年,今日头条使用Go语言构建了承载了超过80%流量的后端服务,高峰QPS超过700万,日处理请求量超过3000亿。此外京东的消息推送与分布式存储、知乎的推荐系统、网易云信的调度服务等均开始使用Go语言。
三、 IDE介绍
1. Goland
JetBrains作为一家推出过许多功能强大IDE的公司,迄今为止已推出包括:Java的Intellj IDEA、PHP的PHPStorm、 Python的Pycharm、前端的WebStorm在内的集成开发环境。它旗下推出了一款专门面向Go语言的集成开发环境Goland,功能强大,使用十分便捷。Goland社区的插件丰富,功能齐全,使用过程中碰到的问题少,开箱即食,对新手十分友好。可在jetbrains官网下载Goland产品,下载页面:https://www.jetbrains.com/go/。与IDEA等存在面向个人用户的免费版本不同的是,Goland仅有收费版本,使用者在免费试用30天后需购买以获取使用权限。
2. VSCode
VSCode是目前比较流行的轻量型IDE,集成了众多语言的插件,以支持多种语言的开发,其中包括Go语言插件。本段将介绍如何使用VSCode搭建Go语言的开发环境。
- 下载VSCode,下载链接为:https://code.visualstudio.com
- 进入VSCode,安装Golang插件,如下图所示,依据次序点击执行: 选择插件市场->搜索Go插件->认准微软出品->点击安装。
- 在VSCode的Setting中配置User Settings中的GOROOT及GOPATH,其他配置默认即可。如图4所示:
其中:GOROOT是Go的安装路径,GOPATH是编译后二进制的存放目的地和import包时的搜索路径,也是你的工作目录。
- 此时新建HelloWorld.go,编写代码,点击运行后,控制台会返回提示,有工具需要安装,可点击右下角弹出的提示框install All进行一键安装,如图5所示。也可打开命令提示符输入如下命令进行工具安装(本地需安装git):
安装gocode:go get -u -v github.com/nsf/gocode
安装gopkgs:go get -u -v github.com/tpng/gopkgs
安装go-outline:go get -u -v github.com/lukehoban/go-outline
安装godef:go get -u -v github.com/rogpeppe/godef
安装golint:
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/tools.git
git clone https://github.com/golang/lint.git
go get golang.org/x/lint/golint
在安装过程中,由于GFW的原因,可能会导致部分工具源不可用,此问题可通过从Github使用git clone下载到本地再使用本地安装解决
图5 VSCode Go环境配置-工具安装- 安装完毕后重启VSCode,即可完成Go语言插件的安装。
四、 语法介绍
1. Hello World
创建hello.go,并且键入如下代码,运行Hello World:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!");
}
在此代码中,包含三块主要信息:package、import、func。其中,一个独立go程序需要至少一个main package。此外,还需通过func main声明主函数入口,通过import引入 标准化输入输出包,以实现Hello Wolrd 字符串的打印。
关于package的使用,细节如下所示:
a. Go程序是由一个或多个package组成,如果独立程序,则需要一个main package
b. 每一个package由若干个源文件组成,每个go源文件均需头部声明自身所属的package。
c. 其中同属于一个package的源文件应当存放于同一个目录之下
d. 在使用其他package的包内容时,可以通过import packagename “[package path]”来实现,代码如下:
package study
import packageName "path/to/package"
2. 变量声明
Go语言的变量声明方式非常简单且易于阅读,最常规的方式为通过var关键字声明变量: var variable_name variable_type,关键字、变量名、变量类型之间以空格分隔,实例代码如下所示:
func main() {
var a int
a = 20
fmt.Println(a)
}
值得注意的是,Go语言的规范十分严格,设计人员将编码规范融入Go语言的编译规则中。以此实例为例,若仅仅定义、赋值了变量a而未使用,编译时会报错。
除此之外,变量的声明及初始化赋值还存在其他方式,如下所示:
func main() {
var a, b int = 20, 30
var c = "HelloWorld"
str := ""
fmt.Println(a, b, c, str)
}
代码中各变量声明方式含义如下所示:
- 定义int型的两个变量a和b,并且分别赋值为20和30
- 定义新变量c,并且赋予初始值,由编译器编译时自动决定变量c的类型
- str := “” 的作用 等同于 var str = “”,表示定义新变量、初始化赋值,并且由编译器编译时自动决定变量str的类型。值得注意的是:=左边必须是未定义过的变量名,已定义过变量使用:=赋值会在编译时出错
3. 函数与方法
Go语言中的函数
函数是基本的代码块,用于执行不同的业务任务流程。函数定义时指定函数名称、参数以及返回类型,具体格式如下所示:
func function_name( [parameter list] ) [return_types] {
函数体
}
函数由func关键字开始声明,并依次包含函数名、参数列表、返回值列表。值得注意的是,Go语言中的函数可有无返回值、仅返回一个值和返回多个值三种情况可选。其中,返回多个值的实例代码如下所示:
func swap(x, y int) (int, int) {
return y, x
}
func main() {
a, b := 1, 2
a, _ = swap(a, b)
fmt.Println(a, b)
}
当调用多返回值的函数时,需要有对应数量的变量对象接收函数返回值。若不需要多个返回值中的某几个,可以使用Go语言中的空白标识符 _ 占位。
Go语言中的方法
方法也是函数,它带有一个特殊的接收器类型,它是在func关键字和方法名之间编写的。可以在方法内部访问接收器。方法能给用户自定义的类型添加新的行为。它和函数的区别在于方法有一个接收者,给一个函数添加一个接收者,那么它就变成了方法。实例代码如下所示:
type Person struct {
age int
name string
gender string
}
func (p Person) Growup1() Person {
p.age++
return p
}
func Growup2(p Person) Person {
p.age++
return p
}
func main() {
p := Person{name: "张三",age: 25, gender: "男"}
p = p.Growup1()
fmt.Println(p)
}
在此代码中,Groupup1与Groupup2两者实现的功能及效果一致。
4. 结构体
结构体的类型在java、c等语言之中均存在,同时也是十分重要的类型。通过结构体的使用将不同类型的数据组合形成新的数据类型。Go语言中同样存在结构体的类型。结构体的定义如下代码所示:
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
当定义了结构体后,即可将结构体应用于变量的声明。普通变量的声明及赋值方法同样适用于结构体变量的声明及初始化,如下列实例所示:
type Person struct {
name string
age int
gender string
}
func main() {
var p Person
fmt.Println(p)
}
此处应值得注意的是,Go语言通过首字母大小写来控制访问权限,无论是变量、常量、方法还是自定义类型,首字母大写即可被外部包访问,反之则不行。以此实例为例,此处Person类型可被外部包访问,若类型名为person则不行。
除此之外结构体拥有多种初始化方法,包括:
func main() {
p1 := Person{"张三", 25, "男"}
p2 := Person{ gender: "女",name: "李四", age: 25}
p3 := new(Person)
}
代码中各方式含义如下:
- 按照结构体中成员变量定义的顺序依次提供初始化值。
- 通过field:value的方式初始化,此方式无需按照结构体中成员变量定义的顺序提供值,可任意改变顺序。
- 通过new方式初始化结构体,结构体中各成员变量会赋予类型默认的初始值。
结构体的访问
在上述初始化方式之后,p1与p2可通过.点操作符用于访问结构的各个字段,以获取或更改字段的值。P3通过new方式初始化,所得的结果为结构体指针。
// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type
在C语言中此情况需要使用来访问对象并操作成员变量,而在Go语言中无需使用,可直接通过点.操作符访问指针对象以及结构中的各个字段。具体代码如下所示:
func main() {
p1 := Person{"张三", 25, "男"}
p2 := Person{ gender: "女",name: "李四", age: 25}
p3 := new(Person)
p3.age = 18
p3.name = "Cristina"
}
结构体的比较
结构体是值类型,如果结构体中的成员变量是可以比较的,那结构体是可以比较的。如果结构体中对应的字段均相等,则认为两个结构体变量相等。实例代码如下所示:
func main() {
p1 := Person{"张三", 25, "男"}
p2 := Person{"张三", 25, "男"}
if p1 == p2 {
fmt.Println("Person1 and Person2 are equal")
}
}
运行结果如下:
Person1 and Person2 are equal
5. 循环控制
Go语言中循环过程控制主要通过for实现循环执行操作。和java语言类似,For语句的基本语法结构为:
For init; condition; post {}
其中:初始化语句仅被执行一次,在初始化语句执行之后,循环体执行之前将检查condition的条件,如果condition的值为true则执行循环体,否则结束循环。在一次循环体结束后则执行post语句,在此之后再次检查condition状态,并依据结果决定是否继续循环。
在Go语言中没有while关键字,但是可以通过for语句实现while的效果,while效果的for语句的基本语法结构为:
For condition {}
此外,可以使用for语句配合range关键字对slice、map、数组进行遍历,此实例代码如下所示:
func main() {
array := [...]int{1,2,3,4,5,6,7,8}
for i,v := range array {
fmt.Printf("数组中第 %d 个值为 %d \n", (i + 1), v)
}
}
6. 内建容器
Go语言中,除了数字类型、字符类型等基本类型外,还包括数组、切片、Channel等派生类型。本篇文章中主要记录数组、切片两种数据类型的使用。
数组
与C语言中数组的概念一致,Go语言中数组也是一段固定长度的连续内存区间。数组的声明需要指定元素类型以及元素个数,语法格式如下:var variable_name [SIZE] variable_type
以此格式为标准,可得如下实例:
var arr1 [5]int
此实例中,定义了长度为5的整数型数组,并且为所有元素赋予初始值为0。除此之外,Go语言提供了其他声明并初始化数组的方式,具体如实例所示:
func main() {
var arr1 [5]int
arr2 := [4]int{1,2,3,4}
arr3 := [...]int{1,2,3,4,5,6,7,8}
fmt.Println(arr1, arr2, arr3)
}
在第二种方式中,指定数组长度并使用 := 而非var关键字声明,此方法要求声明的同时必须赋予初始化的值,并且初始化数组内元素的数量必须等于指定的数组长度。
在第三种方式中,可无需指定数组长度,Go语言会根据元素的个数自动设置数组的大小。但需要注意:“[ ]”中必须使用“…”,若无“…” 得到的结果则不是数组而是切片。
切片
Go语言的数组定义完成后长度不可变,在某些特定场景下数组不太适用。Go语言原生支持一种灵活的内置类型:切片。切片是动态分配大小的连续空间,切片本身可以不储存数据,仅产生对底层数组的一个视图,从而灵活地对切片进行数据处理(类似“动态数组”),并通过此方法对底层数组的数据进行处理。此处视图的概念与数据库中的视图概念类似。
切片的内部结构包含地址、大小与容量。其中:
地址:即此段连续内容空间的起始地址
大小:即切片可取值的范围长度。长度可扩展。
容量:即切片所对应底层数组中,从起始地址开始的可用长度。容量可向后扩展,不可向前扩展。
从数组或切片中生成新的切片
切片默认指向一段连续内存区域,可以是数组和切片本身,在一段连续内存区域生成切片是一种生成新切片的典型方式。格式代码如下:
slice[startindex : endindex]
其中:
- slice表示切片目标对象
- startindex表示切片对象的起始索引
- endindex表示切片对象的结束索引
值得注意的是,此方法生成的切片仅仅是源连续内存区域的视图,本身不存储数据,因此对此方法生成切片的数据的更改会直接反应到内存区域,即:被切片的源切片和数组对应值也会更改。以如下实例代码为例:
func main() {
a := []int{1,2,3,4}
b := a[0:]
b[0] = 10
fmt.Println(a, b)
}
运行结果为:
[10 2 3 4] [10 2 3 4]
声明切片以生成新切片
每种类型,包括自定义类型,都可以拥有其切片类型,以表示多个该类型元素的连续集合,此方法声明格式如下:
var slice_name []type_name
其中:
- Slice_name表示切片类型的变量名
- Type_name 表示该切片中对应的元素类型
使用make()函数构造新切片
可以使用make()函数动态地创建一个新的切片,该方法格式如下:
make( []type_name, len, cap )
其中:
- Type_name:创建的新切片的元素类型
- Len:为该切片分配多少初始元素
- Cap:提前分配的内存区域大小。Cap的设计主要为了提前分配空间,以防止扩展时可能的再次分配空间而影响性能。
当slice扩展至超过容量边界时,系统会自动扩展容量,容量扩展策略是扩展至当前容量的双倍,以下列实例代码为例:
func testslice() {
a := make([]int, 3)
fmt.Println(len(a), cap(a))
a = append(a, 3)
fmt.Println(len(a), cap(a))
}
func main() {
testslice()
}
运行结果为:
3 3
4 6
映射(map)
map是十分重要的数据结构,用以存储任意类型数据的关联关系。Go语言中,map类型定义格式如下所示:
map[KeyType]ValueType
实例代码如下所示:
func testmap() {
m := make(map[string]int)
m["id1"] = 10001
fmt.Println(m["id1"])
}
func main() {
testmap()
}
此外,map支持在声明时初始化填充内容,实例代码如下:
m := map[string]int{
"id1":10001,
"id2":10002,
"id3":10003,
}
map的遍历
map的遍历可使用range关键字完成循环,range关键字可依次返回map中的key与value,同时获得键与值。实例代码如下:
func getall() {
m := map[string]int{
"id1":10001,
"id2":10002,
"id3":10003,
}
for k,v := range m {
fmt.Println("key : " , k, " value : " , v)
}
}
func main() {
getall()
}
运行结果为:
key : id1 value : 10001
key : id2 value : 10002
key : id3 value : 10003
五、 总结
Go语言语法简单,通过以上基础语法学习后,可以使用Go语言实现简易功能,然而Go语言中依然存在许多特性,需要长期练习实践、阅读源码才可深入理解。此外Go语言中还可利用goroutine、 channel实现高性能并发,在闲暇及项目中多多思考以熟练使用。
参考资料:
[1]. 为什么要使用 Go 语言?Go 语言的优势在哪里? - 腾讯云技术社区的回答。点击跳转
[2]. 为什么要使用 Go 语言?Go 语言的优势在哪里? - 波罗学的回答。点击跳转
来源:oschina
链接:https://my.oschina.net/u/4349274/blog/4295984