admin管理员组文章数量:1794759
Go语言新手入门浅谈
Go语言新手入门浅谈
- 一、 前言
- 二、 Go语言行业应用
- 三、 IDE介绍
- 1. Goland
- 2. VSCode
- 四、 语法介绍
- 1. Hello World
- 2. 变量声明
- 3. 函数与方法
- Go语言中的函数
- Go语言中的方法
- 4. 结构体
- 结构体的访问
- 结构体的比较
- 5. 循环控制
- 6. 内建容器
- 数组
- 切片
- 从数组或切片中生成新的切片
- 声明切片以生成新切片
- 使用make()函数构造新切片
- 映射(map)
- map的遍历
- 五、 总结
- 参考资料:
由于工作中存在与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特性以及为服务端服务。
图1 Go语言核心开发团队 得益于社区中丰富的第三方库,Python被广泛应用于机器学习、数据分析、数据整理等项目。同样的,得益于强大的并发能力、完美跨平台能力、高效的开发效率等因素,Go语言已逐渐被业界广泛应用于云上开发,诞生出许多优秀项目,如下表所示: 表1 使用Go语言编写的项目案例 [2]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语言。
图2 Go语言业务案例 三、 IDE介绍 1. GolandJetBrains作为一家推出过许多功能强大IDE的公司,迄今为止已推出包括:Java的Intellj IDEA、PHP的PHPStorm、 Python的Pycharm、前端的WebStorm在内的集成开发环境。它旗下推出了一款专门面向Go语言的集成开发环境Goland,功能强大,使用十分便捷。Goland社区的插件丰富,功能齐全,使用过程中碰到的问题少,开箱即食,对新手十分友好。可在jetbrains官网下载Goland产品,下载页面:www.jetbrains/go/。与IDEA等存在面向个人用户的免费版本不同的是,Goland仅有收费版本,使用者在免费试用30天后需购买以获取使用权限。
2. VSCodeVSCode是目前比较流行的轻量型IDE,集成了众多语言的插件,以支持多种语言的开发,其中包括Go语言插件。本段将介绍如何使用VSCode搭建Go语言的开发环境。
安装gocode:go get -u -v github/nsf/gocode 安装gopkgs:go get -u -v github/tpng/gopkgs 安装go-outline:go get -u -v github/lukehoban/go-outline 安装godef:go get -u -v github/rogpeppe/godef 安装golint: cd $GOPATH/src/golang/x git clone github/golang/tools.git git clone github/golang/lint.git go get golang/x/lint/golint
在安装过程中,由于GFW的原因,可能会导致部分工具源不可用,此问题可通过从Github使用git clone下载到本地再使用本地安装解决
图5 VSCode Go环境配置-工具安装创建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的类型。值得注意的是:=左边必须是未定义过的变量名,已定义过变量使用:=赋值会在编译时出错
函数是基本的代码块,用于执行不同的业务任务流程。函数定义时指定函数名称、参数以及返回类型,具体格式如下所示:
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语言原生支持一种灵活的内置类型:切片。切片是动态分配大小的连续空间,切片本身可以不储存数据,仅产生对底层数组的一个视图,从而灵活地对切片进行数据处理(类似“动态数组”),并通过此方法对底层数组的数据进行处理。此处视图的概念与数据库中的视图概念类似。 切片的内部结构包含地址、大小与容量。其中: 地址:即此段连续内容空间的起始地址 大小:即切片可取值的范围长度。长度可扩展。 容量:即切片所对应底层数组中,从起始地址开始的可用长度。容量可向后扩展,不可向前扩展。
图6 slice结构 从数组或切片中生成新的切片切片默认指向一段连续内存区域,可以是数组和切片本身,在一段连续内存区域生成切片是一种生成新切片的典型方式。格式代码如下:
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( []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 语言的优势在哪里? - 波罗学的回答。点击跳转
版权声明:本文标题:Go语言新手入门浅谈 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1686889252a114761.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论