golang快速入门(练习)
1.打包和工具链
1.1 包
|
1
2
3
4
5
6
7
8
9
10
|
net/http/ cgi/ cookiejar/ testdata/ fcgi/ httptest/ httputil/ pprof/ testdata/在 http 目录下的所有文件都属于 http 包 |
1.2 导入
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import ( "fmt" "strings")编译器先查找$GOROOT下的包,然后查找$GOPATH下的包1.推荐所有自定义包不放在$GOROOT下,该目录下一般是标准库,自定义包放在$GOPATH下,如果升级go版本,直接替换/usr/local/go相关文件,而不用重新替换自定义包2.go语言中字符串使用双引号"",注意与Python习惯区别go支持远程导入,如果import内的包名是一个地址,如"github.com/spf13/viper",在go run之前使用go get命令,程序会下载对应的包的$GOPATH的包目录下(该功能需要git支持)命名导入和未使用包标注import { myfmt "mylib/fmt" //重命名包名,在有多个包重名的情况下使用,同Python中的import package as pkg _ "fmt" //go语言不允许导入包而不使用,所以用下划线标注没有使用的包,实际不会导入} |
1.3 go工具介绍
- go build file.go //编译文件
- go clean file.go //删除编译生成的可执行文件
- go vet file.go //检查常见错误Printf类型匹配错误的参数,定义函数时方法签名错误,错误结构变量等
- go fmt file.go //自动整理文件格式,对齐
- go doc pkg //在终端查看包相关的文档
- godoc -http=:80 //启动一个go文档web服务器,如果开发人员按照godoc规则写代码,能自动包含在文档中
2.数组、切片和映射
2.1 数组
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
// 声明一个包含5个元素的整型数组// 一旦声明,数组里存储的数据类型和数组长度就都不能改变var array [5]int //数组默认值为0array := [5]int{10, 20, 30, 40, 50}array := [...]int{10, 20, 30, 40, 50}array := [5]int{1: 10, 2: 20} //指定索引为1元素为10,索引为2元素为20// 声明包含 5 个元素的指向整数的数组// 用整型指针初始化索引为 0 和 1 的数组元素array := [5]*int{0: new(int), 1: new(int)}*array[0] = 10 // 为索引为0的元素赋值var array_ [5]intarray_ = array// 数组变量的类型包括数组长度和每个元素的类型。只有这两部分都相同的数组,才是类型相 同的数组,才能互相赋值// 声明一个二维整型数组,两个维度分别存储 4 个元素和 2 个元素var array [4][2]int// 使用数组字面量来声明并初始化一个二维整型数组array := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}// 声明并初始化外层数组中索引为 1 个和 3 的元素array := [4][2]int{1: {20, 21}, 3: {40, 41}}// 声明并初始化外层数组和内层数组的单个元素array := [4][2]int{1: {0: 20}, 3: {1: 41}}// 声明一个需要 8 MB 的数组var array [1e6]int// 将数组传递给函数foo foo(array)//函数 foo 接受一个 100 万个整型值的数组func foo(array [1e6]int){ ... }每次函数 foo 被调用时,必须在栈上分配 8 MB 的内存。之后,整个数组的值(8 MB 的内 存)被复制到刚分配的内存里。虽然 Go 语言自己会处理这个复制操作,不过还有一种更好且更 有效的方法来处理这个操作。可以只传入指向数组的指针,这样只需要复制 8 字节的数据而不是 8 MB 的内存数据到栈上,优化如下// 分配一个需要 8 MB 的数组var array [1e6]int// 将数组的地址传递给函数foo foo(&array)// 函数 foo 接受一个指向 100 万个整型值的数组的指针func foo(array *[1e6]int){ ... }这个操作会更有效地利用内存,性能也更好。不过要意识到,因为现在传递的是指针, 所以如果改变指针指向的值,会改变共享的内存。使用切片能更好地处理这类共 享问题。 |
2.2 切片
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 创建一个字符串切片// 其长度和容量都是 5 个元素slice := make([]string, 5)// 创建一个整型切片// 其长度为 3 个元素,容量为 5 个元素,容量小于长度的切片会编译出错slice := make([]int, 3, 5)slice := []int{10, 20, 30}// 使用空字符串初始化第 100 个元素slice := []string{99: ""}// 创建 nil 整型切片var slice []intslice := make([]int, 0)slice := []int{}<strong>如果在[]运算符里指定了一个值,那么创建的就是数组而不是切片。只有不指定值 的时候,才会创建切片</strong> |
切片的使用
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
// 其长度和容量都是 5 个元素slice := []int{10, 20, 30, 40, 50}// 其长度为 2 个元素,容量为 4 个元素,该切片不能看见底层数组第0号元素newSlice := slice[1:3]// 使用原有的容量来分配一个新元素// 将新元素赋值为 60newSlice = append(newSlice, 60)因为 newSlice 在底层数组里还有额外的容量可用,append 操作将可用的元素合并到切片 的长度,并对其进行赋值。由于和原始的 slice 共享同一个底层数组,slice 中索引为 3 的元 素的值也被改动了如果切片的底层数组没有足够的可用容量,append 函数会创建一个新的底层数组,将被引用的现有的值复制到新数组里,再追加新的值综上:创建切片的时候尽量使长度和容量一致,如果增加append新值是新开一个底层数组,而不是直接修改e.g: source := []int{0, 1, 2, 3} slice1 := source[0:3] fmt.Println(slice1) // [0 1 2] slice2 := append(slice1, 100) slice2[0] = 99 fmt.Println(slice1) fmt.Println(slice2) // [99 1 2] // [99 1 2 100]迭代切片slice := []int{10, 20, 30, 40}// 迭代每一个元素,并显示其值for index, value := range slice { fmt.Printf("Index: %d Value: %d\n", index, value)}如果需要忽略index值,使用下划线占位当迭代切片时,关键字 range 会返回两个值。第一个值是当前迭代到的索引位置,第二个 值是该位置对应元素值的一份副本slice := []int{10, 20, 30, 40}for index, value := range slice { // 输出值和地址 fmt.Printf("Value: %d Value-Addr: %X ElemAddr: %X\n", value, &value, &slice[index])}//第二种迭代方式for index := 0; index < len(slice); index++ { fmt.Printf("Index: %d Value: %d\n", index, slice[index])}关键字 range 总是会从切片头部开始迭代。如果想对迭代做更多的控制,依旧可以使用传 统的 for 循环 |
多维切片
|
1
2
3
4
|
// 创建一个整型切片的切片slice := [][]int{{10}, {100, 200}}// 为第一个切片追加值为 20 的元素slice[0] = append(slice[0], 20 |
切片属于引用类型,在函数间传递开销很小
举例理解切片和底层数组
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 长度为3, 容量为5slice1 := make([]int, 3, 5) // 修改5为3,创建一个长度与容量一致的切片// 切片所有默认值都是0slice1[1] = 1slice1[2] = 2// 切片2与1共享一个底层数组slice2 := slice1[0:3]// 在切片2上面增加一个数据slice3 := append(slice2, 200)// 在切片1上面增加一个数据slice4 := append(slice1, 100)fmt.Println(slice1) // [0, 1, 2]fmt.Println(slice2) // [0, 1, 2]fmt.Println(slice3) // [0, 1, 2, 100]fmt.Println(slice4) // [0, 1, 2, 100] |
2.3 映射
映射是一个集合,可以使用类似处理数组和切片的方式迭代映射中的元素。但映射是无序的 集合,意味着没有办法预测键值对被返回的顺序。即便使用同样的顺序保存键值对,每次迭代映 射的时候顺序也可能不一样。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
// 创建一个映射,键的类型是 string,值的类型是int dict := make(map[string]int)// 创建一个映射,键和值的类型都是 string// 使用两个键值对初始化映射dict := map[string]string{"a": "1", "b": "2"}// 重新赋值dict["a"]="3"// 删除delete(dict, "a")// 判断key是否存在value, exists := dict["a"]if exists{ fmt.Println(value)}// 使用ranged迭代for key, value := range dict{ fmt.Printf("Key: %s Value: %s\n", key, value)}映射的键可以是任何值。这个值的类型可以是内置的类型,也可以是结构类型,只要这个值 可以使用==运算符做比较。切片、函数以及包含切片的结构类型这些类型由于具有引用语义, 不能作为映射的键,使用这些类型会造成编译错误e.g1:dict := map[[]string]int{}fmt.Println(dict)// # command-line-arguments// ./main.go:9:10: invalid map key type []stringdict := map[[3]string]int{}// map[]e.g2:func main() { // 创建一个映射, 字母与对应的10进制ascii码 dict := map[string]int{"a": 97, "b": 98, "c": 99} for key, value := range dict { fmt.Printf("key: %s Value: %s\n", key, value) } removeDict(dict, "b") fmt.Println(dict)}func removeDict(OneDict map[string]int, key string) { delete(OneDict, key)}1.Print以ln结尾的是直接输出,类似Python的print,可以输出各种类型(包括自定义结构体)变量并换行,一行输出多个值使用逗号隔开;以f结尾的是结构化输出,类似c语言中的printf2.在函数间传递映射并不会制造出该映射的一个副本。实际上,当传递映射给一个函数,并对 这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改。这个特性和切片类似,保证可以用很小的成本来复制映射 |
3.GO语言的类型系统
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
// user 在程序里定义一个用户类型// 属性的类型也可以是用户自定义类型,用法类似c语言结构体structtype user struct { name string email string ext int privileged bool}// 声明 user 类型的变量var bill user// 声明变量并赋值lisa := user{ name: "Lisa", email: "lisa@email.com", ext: 123, privileged: true,}// 顺序必须与声明一致lisa := user{"Lisa", "lisa@email.com", 123, true}// 声明一个新类型type Duration int64// int64 类型叫作 Duration 的基础类型。不过,虽然 int64 是基础 类型,Go 并不认为 Duration 和 int64 是同一种类型。这两个类型是完全不同的有区别的 类型。e.g1:var dur Durationdur = int64(1000)./main.go:12:6: cannot use int64(1000) (type int64) as type Duration in assignment |
e.g2:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
// 这个示例程序展示如何声明,并使用方法package mainimport ( "fmt")// user 在程序里定义一个用户类型type user struct { name string email string}// notify 使用值接收者实现了一个方法func (u user) notify() { fmt.Printf("Sending User Email To %s<%s>\n", u.name, u.email)}// changeEmail 使用指针接收者实现了一个方法// 这个方法使用指针接收者声明。这个接收者的类型是指向 user 类型值的指针,而不是 user 类型的值。当调用使用指针接收者声明的方法时,这个方法会共享调用方法时接收者所指向的值func (u *user) changeEmail(email string) { u.email = email}// main 是应用程序的入口func main() { // user 类型的值可以用来调用 // 使用值接收者声明的方法 bill := user{"Bill", "bill@email.com"} bill.notify() // 指向 user 类型值的指针也可以用来调用 // 使用值接收者声明的方法 lisa := &user{"Lisa", "lisa@email.com"} lisa.notify() //指针被解引用为值, 这样就符合了值接收者的要求。notify 操作的是一个副本,只不过这次操作的是 从 lisa 指针指向的值的副本。 // (*lisa).notify() // user 类型的值可以用来调用 // 使用指针接收者声明的方法 bill.changeEmail("bill@newdomain.com") bill.notify() //变量 bill,以及之后使用这个变量调用使用指针接收者声明的 changeEmail 方法。Go 语言再一次对值做了调整,使之符合函数的接收者,进行调用 // (&bill).changeEmail ("bill@newdomain.com") // 指向 user 类型值的指针可以用来调用 // 使用指针接收者声明的方法 lisa.changeEmail("lisa@newdomain.com") lisa.notify()}// Go 语言既允许使用值,也允许使用指针来调用方法,不必严格符合接收者的类型。 |
golang快速入门(练习)的更多相关文章
- Golang快速入门
Go语言简介: Golang 简称 Go,是一个开源的编程语言,Go是从2007年末由 Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian L ...
- Golang快速入门:从菜鸟变大佬
最近写了不少Go代码,但是写着写着,还是容易忘,尤其是再写点Python代码后.所以找了一篇不错的Golang基础教程,翻译一下,时常看看. 原文链接: 「Learning Go - from zer ...
- golang快速入门(四)
提示:本系列文章适合有其他语音基础并对Go有持续冲动的读者 一.golang获取HTTP请求 1.在golang标准库中提供了net包来处理网络连接,通过http.Get创建http请求并返回服务器响 ...
- golang快速入门(五)初尝web服务
提示:本系列文章适合对Go有持续冲动的读者 初探golang web服务 golang web开发是其一项重要且有竞争力的应用,本小结来看看再golang中怎么创建一个简单的web服务. 在不适用we ...
- golang快速入门(六)特有程序结构
提示:本系列文章适合对Go有持续冲动的读者 阅前须知:在程序结构这章,更多会关注golang中特有结构,与其他语言如C.python中相似结构(命名.声明.赋值.作用域等)不再赘述. 一.golang ...
- Golang Module快速入门
前言: 在Golang1.11之前的版本中,官方没有提供依赖和包管理工具.开发者通常会使用vendor或者glide的方式来管理依赖(也有直接使用GOPATH多环境方式),而在Golang1.11之后 ...
- Docker快速入门(二)
上篇文章<Docker快速入门(一)>介绍了docker的基本概念和image的相关操作,本篇将进一步介绍image,容器和Dockerfile. 1 image文件 (1)Docker ...
- 版本控制工具Git工具快速入门-Windows篇
版本控制工具Git工具快速入门-Windows篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 最近在学习Golang语言,之前的开发环境在linux上开发的,后来由于办公用的是w ...
- Go 快速入门
入门 Go 语言需要多久?答案是 -- 读完这篇文章的时间!不妨找一个周末的下午,踏上 Go 之旅吧! 更新记录: 2016.12.12: 完成重制 2016.11.02: 增加重点理解和参考链接 2 ...
随机推荐
- 右键菜单→新建→BAT 批处理文件
目的:以前编写BAT,通常新建一个文本,然后另存为 .bat,比较麻烦,那么如何右键新建菜单里添加新建批处理文件呢? 代码如下: @echo offcd /d %temp%echo Windows R ...
- python项目实战-小游戏1
项目规则: 1.玩家和敌人分别从现有的角色中选择3个角色 2.随机生成目前的血量,和攻击量 3.游戏规则:当玩家向敌人发起攻击,敌人当前的血量=之前的血量-玩家的血量,同理 4.3局两胜 5.自定义玩 ...
- 【原创】面向对象版本地CPU资源占用监控脚本
前期准备: 1.python2.7环境 2.相关第三方库下载安装 脚本工作过程: 1.根据输入的进程名判断进程是否存在,如果不存在则进行等待,直到检测到进程PID,中途进程退出抛出异常,键入enter ...
- Loading half a billion rows into MySQL---转载
Background We have a legacy system in our production environment that keeps track of when a user tak ...
- MySQL和Python交互
与Python交互 python3模块名:pymysql conda install pymysql conda install sqlalchemy python2模块名:MySQLdb impor ...
- 【Codeforces Round #433 (Div. 2) A】Fraction
[链接]h在这里写链接 [题意] 在这里写题意 [题解] 枚举分子从高到低就好. 这样得到的一定是最大的. (可以约分没错,但是约分过后和就不是n了,所以不会有错的) [错的次数] 0 [反思] 在这 ...
- 就目前市面上的面试整理来说,最全的BAT大厂面试题整理在这
原标题:就目前市面上的面试整理来说,最全的BAT大厂面试题整理在这 又到了面试求职高峰期,最近有很多网友都在求大厂面试题.正好我之前电脑里面有这方面的整理,于是就发上来分享给大家. 这些题目是网友去百 ...
- 对生产者和消费者问题的另一个解决办法是使用QWaitCondition(封装好了wakeOne,wakeAll,而且与QReadWriteLock对接,几乎是万能的办法)
对生产者和消费者问题的另一个解决办法是使用QWaitCondition,它允许线程在一定条件下唤醒其他线程.其中wakeOne()函数在条件满足时随机唤醒一个等待线程,而wakeAll()函数则在条件 ...
- vue-cli 构建vue项目
师父说,咱们还是要用vue-cli 去构建前端项目.然后我就开始了 懵逼之旅. 今天上午主要就是搞懂用webpack和vue-cli怎么搭建 运行项目 首先找到了咱们博客园 园友的博客,提供了大概五个 ...
- ZOJ List the Books 水~
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1727 题目大意: 给你书名.出版时间.价格,让你按照一定的顺序排序.. 其中题 ...