开始学习GO,主要原因是现在使用的一些组件如Docker,K8s,Etcd都是用GO开发的,希望能对使用的东西多一些了解,读懂简单的源码。
作为“未来的服务端语言”,代码优雅,天生并发,可以直接在机器上运行,支持多方范式编程等等很多优点。不废话了,开始吧。
一、GO开发环境
windows
- 下载安装,安装到默认路径。安装完成后默认会在环境变量 Path 后添加 Go 安装目录下的 bin 目录 C:\Go\bin\,并添加环境变量 GOROOT,值为 Go 安装根目录 C:\Go\。安装后,在cmd中运行
go env
测试。 - 配置 安装go,安装sublime,安装插件gosublime 插件user-setting
{
"env":{
"GOPATH":"E:/GO",
"GOROOT":"C:/Program Files (x86)/Go"
}
}
Linux
wget https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz
sudo tar -xzf go1.8.3.linux-amd64.tar.gz -C /usr/local
# vim /etc/profiles
export GOROOT=/usr/local/go
export GOBIN=$GOROOT/bin
export PATH=$PATH:$GOBIN
export GOPATH=$HOME/gopath (可选设置)
source /etc/profiles
go 程序结构
结构和java差不多,主要是这些:声明包,导入包,函数,变量,语句,注释。但也有区别,比如main
包作为执行程序的入口,导入的包未使用会编译出错等。
编译执行
比如有文件hello.go
go build hello.go # 编译成可执行文件
go run hello.go # 运行
二、数据结构
go 中都是大写字母开头作为模块的使用。 函数可以没有参数或接受多个参数。当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。函数可以返回任意数量的返回值。
package main
import "fmt"
func add(x, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
命名返回值 Go 的返回值可以被命名,并且就像在函数体开头声明的变量那样使用。
返回值的名称应当具有一定的意义,可以作为文档使用。没有参数的 return 语句返回各个返回变量的当前值。这种用法被称作“裸”返回。
2.1 变量
var 语句定义了一个变量的列表;跟函数的参数列表一样,类型在后面。就像在这个例子中看到的一样, var 语句可以定义在包或函数级别。
var a, b int = 1, 2 // 多个值声明
var c int // 没初始化,默认为0
d := "short" // 赋值简写
常量
const s string = "constant"
2.2 循环
for 是go中唯一的循环结构
func main() {
i :=1
for i<=3 {
fmt.Println(i)
i = i+1
}
for j := 7;j <=9 ;j++ {
fmt.Println(j)
}
for{// 不带条件会一直循环
fmt.Println("loop")
break
}
}
2.3分支
if 7%2 == 0{
fmt.Println("7 is even")
}else {
fmt.Println("7 is odd")
}
if num := 9; num <0 {
fmt.Println("is negative")
}
Go: 你可以不用圆括号,但需要花括号;go没有三目运算符
func main() {
i := 2
fmt.Print("write ", i, " as ")
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
}
}
三、数组、切片
3.1 数组声明
var a [5]int // 声明不初始化,默认为0,0,0....
var twoD [2][3]int
array := [5]int{10, 20, 30, 40, 50}
array := [...]int{10, 20, 30, 40, 50} // 容量由初始化时确定
array := [5]int{1: 10, 2: 20} // [0 10 20 0 0]
len(a), a[3]
3.2 使用数组
相同长度和类型的数组才能赋值。
var array1 [5]string
array2 := [5]string{"Red","Blue", "Green", "Yello", "Pink"}
array1 = array2 // 复制
array1[1] = "Orther Color" // 修改
// for 遍历 arr
for index, value := range array2{
fmt.Printf("arr[%d]=%s\n", index, value)
}
3.3 切片
切片是一种数据结构,这种数据结构便于使用和管理数据集合。切片是围绕动态数组的概念构建的,可以按需自动增长和缩小。切片的动态增长是通过内置函数 append 来实现的.
切片有3个字段: 指针,长度,容量。
// 其长度和容量都是 5 个元素
slice1 := make([]string, 5)
// 其长度为 3 个元素,容量为 5 个元素
slice2 := make([]int, 3, 5)
// 另一种直接初始化的方式创建切片,和数组不同的是,使用[]
// 其长度和容量都是 5 个元素
slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"}
// 其长度和容量都是 3 个元素
slice := []int{10, 20, 30}
// 使用空字符串初始化第 100 个元素
slice := []string{99: ""}
// 创建 nil 整型切片
var slice []int
// 使用 make 创建空的整型切片
slice := make([]int, 0)
// 使用切片字面量创建空的整型切片
slice := []int{}
注意:如果在[]运算符里指定了一个值,那么创建的就是数组而不是切片。只有不指定值 的时候,才会创建切片
相对于数组而言,使用切片的一个好处是,可以按需增加切片的容量。Go 语言内置的 append 函数会处理增加长度时的所有操作细节。 函数 append 会智能地处理底层数组的容量增长。在切片的容量小于 1000 个元素时,总是 会成倍地增加容量。一旦元素个数超过 1000,容量的增长因子会设为 1.25,也就是会每次增加 25% 的容量
slice := []int{10, 20, 30, 40, 50}
newSlice := slice[1:3]
newSlice = append(newSlice,60)
fmt.Println(newSlice)//[20 30 60]
alice := slice[2:3:4]// index=2,lenth=3-2;cap=4-2
// 创建两个切片,并分别用两个整数进行初始化 s1 := []int{1, 2}
s2 := []int{3, 4}
// 将两个切片追加在一起,并显示结果
fmt.Printf("%v\n", append(s1, s2...))//[1 2 3 4]
四、映射
映射是一种数据结构,用于存储一系列无序的键值对。如下图所示,键通过散列函数得到散列值,找到对应存储桶,所以它的存储并不是有序的。
其实说白了,映射这种结果,在java中叫map,在python中对应字典。使用简单,算法经典。
示例
package main
import (
"fmt"
)
func main() {
// 创建map,string:int
//dict := make(map[string]int)
// 创建map,string:string
dict := map[string]string{"name":"thoreau","age": "27"}
// 添加修改
dict["addr"] = "0.0.0.0"
// 判断key是否存在
value, exists := dict["addr"]
if exists {
fmt.Println(value)
}
// delete
delete(dict,"addr")
// 遍历
for key, value := range dict {
fmt.Printf("Key: %s ,Value: %s \n",key,value)
}
}
上面是一个完整的示例,包括初始化、修改、添加、删除和遍历等常用操作。
待丰富完善