本文旨在最短的时间内了解Go语言,适用于有其他语言基础,想迅速入门的同学。
1. 下载安装 到Go官网:https://golang.org/ 下载安装包:
或者Mac的同学可以直接使用brew
命令来安装:
1 2 brew update brew install golang
创建一个workspace folder
,通常放在$Home
目录下
1 mkdir $HOME /<your_go_workspace_folder_name>
添加环境变量:
编辑~/.zshrc
,添加如下内容:
1 2 3 4 export GOPATH=$HOME /<your_go_workspace_folder_name>export GOROOT=/usr/local/opt/go/libexecexport PATH=$PATH :$GOPATH /binexport PATH=$PATH :$GOROOT /bin
配置VSCode 支持Go Command + Shift + P 后,输入 Go:Install/Update Tools
,弹出的全选安装
这里需要注意如果你使用Mac的话,可能会弹出Permission denined
错误, 原因是不存在/usr/local/pkg
目录,或者这个目录权限不足。执行下面命令修复:
1 2 sudo mkdir /usr/local/pkg sudo chown -R $(whoami ) /usr/local/pkg
Hello World 新建一个hello.go
文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { fmt.Println("Hello Go!" ) }
1 2 $ go run hello.go Hello, Go!
可以使用go build hello.go
来生成可执行的二进制文件。
1 2 3 4 5 $ go build hello.go $ ls hello hello.go $ ./hello Hello, go!
语法 变量 Go语言使用var identifier type
的方式定义变量。 可以一次声明多个变量:
1 var identifier1, identifier2 type
1 2 3 4 5 var a string = "Geek Go" fmt.Println(a) var b, c int = 1 , 2 fmt.Println(b, c)
可以使用:=
声明同时赋值变量
1 2 int_val := 100 fmt.Println(int_val)
也可以多变量声明
1 2 3 4 var vname1, vname2, vname3 = v1, v2, v3 vname1, vname2, vname3 := v1, v2, v3
常量 1 const identifier [type ] = value
数组 1 var variable_name [SIZE] type
例如:
1 var balance [10 ] float32
初始化
1 var balance = [5 ]int {1 ,2 ,3 ,4 ,5 }
for循环 Go语言的For循环有3种形式,
1 2 3 4 5 for init; condition; post { }
栗子:
1 2 3 4 var balance = [5 ]int {1 , 2 , 3 , 4 , 5 }for i := 0 ; i < len (balance); i++ { fmt.Println(balance[i]) }
指针 指针使用流程:
定义指针变量。
为指针变量赋值。
访问指针变量中指向地址的值。
栗子:
1 2 var ip *int var fp *float32
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
结构体 结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:
1 2 3 4 5 6 type struct_variable_type struct { member definition member definition ... member definition }
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
1 2 3 variable_name := structure_variable_type {value1, value2...valuen} variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type Books struct { title string author string price float32 } func main () { book1 := Books{"Go语言教程" , "GeekHall" , 9.9 } book2 := Books{title: "Python语言教程" , author: "GeekHall" , price: 8.8 } var book3 Books book3.title = "西游记" book3.author = "吴承恩" book3.price = 19.9 fmt.Println(book1) fmt.Println(book2.title) fmt.Println(book3) }
结构体也可以作为函数参数或者结构体指针来使用
1 2 3 4 5 6 7 8 func printBook ( book *Books ) { fmt.Printf( "Book title : %s\n" , book.title) fmt.Printf( "Book author : %s\n" , book.author) fmt.Printf( "Book price : %f\n" , book.price) } printBook(&book3)
切片(Slice) Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
可以声明一个未指定大小的数组来定义切片:
或使用 make() 函数来创建切片:
1 2 3 4 5 var slice1 []type = make ([]type , len )slice1 := make ([]type , len , [capacity])
len为数组的长度,并且也是切片的初始长度,可以使用可选参数capacity来指定容量
类似Python列表的切片操作
1 2 3 4 5 6 7 8 9 10 s := []int {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } s1 := make ([]int , 8 , 16 ) fmt.Println(s[0 :4 ]) fmt.Println(s[3 :]) fmt.Println(s[:3 ]) fmt.Println(s[:]) fmt.Println(len (s)) fmt.Println(cap (s)) fmt.Println(len (s1)) fmt.Println(cap (s1))
可以使用append
和copy
方法来拷贝和追加元素
make和new 的区别
make和new都是golang用来分配内存的內建函数,且在堆上分配内存,make 即分配内存,也初始化内存。new只是将内存清零,并没有初始化内存。
make返回的还是引用类型本身;而new返回的是指向类型的指针。
make只能用来分配及初始化类型为slice,map,channel的数据;new可以分配任意类型的数据。
范围(Range) 在数组上使用range将传入index和值两个变量,可使用空白符”_”省略.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 nums := []int {1 , 2 , 3 , 4 } sum := 0 for _, num := range nums { sum += num } fmt.Println("sum: " , sum) nums = append (nums, 5 , 6 , 7 ) fmt.Println(nums) for i, num := range nums { if num%3 == 0 { fmt.Println("index: " , i) } }
range也可以用在map的键值对上。
1 2 3 4 cars := map [string ]string {"A" : "Audi" , "B" : "BMW" , "T" : "Tesla" } for k, v := range cars { fmt.Printf("%s -> %s\n" , k, v) }
输出结果:
1 2 3 T -> Tesla A -> Audi B -> BMW
range也可以用来枚举Unicode字符串。
第一个参数是字符的索引,
第二个是字符(Unicode的值)本身。
1 2 3 for i, c := range "ABCabc" { fmt.Println(i, c) }
输出:
1 2 3 4 5 6 0 65 1 66 2 67 3 97 4 98 5 99
集合(Map) Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
定义 Map 可以使用内建函数 make 也可以使用 map 关键字来定义 Map:
1 2 3 4 5 var map_variable map [key_data_type]value_data_typemap_variable := make (map [key_data_type]value_data_type)
栗子:
1 2 3 4 5 6 7 8 9 10 11 var bookAuthorMap map [string ]string bookAuthorMap = make (map [string ]string ) bookAuthorMap["三国演义" ] = "罗贯中" bookAuthorMap["西游记" ] = "吴承恩" bookAuthorMap["水浒传" ] = "施耐庵" bookAuthorMap["红楼梦" ] = "曹雪芹" for book := range bookAuthorMap { fmt.Println(book, "作者是:" , bookAuthorMap[book]) }
打印结果:
1 2 3 4 三国演义 作者是: 罗贯中 西游记 作者是: 吴承恩 水浒传 作者是: 施耐庵 红楼梦 作者是: 曹雪芹
查看元素是否在集合中存在:
1 2 3 4 5 6 jpm, ok := bookAuthorMap["金瓶梅" ] if ok { fmt.Println("金瓶梅的作者是:" , jpm) } else { fmt.Println("金瓶梅不是四大名著" ) }
delete()函数用于删除集合的元素,参数为map和其对应的key:
1 delete (bookAuthorMap, "水浒传" )
递归函数 1 2 3 func recursion () { recursion() }
类型转换
例:
1 2 3 4 5 6 7 8 9 10 var sum int = 17 var count int = 5 var mean float32 var org int org = sum / count mean = float32 (sum) / float32 (count) fmt.Printf("org 的值为: %d\n" , org) fmt.Printf("mean 的值为: %f\n" , mean)
接口 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 28 29 30 31 32 33 34 35 36 37 38 type ICar interface { drive() } type Tesla struct {} type Toyota struct {} type Audi struct {} func (tesla Tesla) drive() { fmt.Println("I am a Tesla, I can take you for a ride." ) } func (toyota Toyota) drive() { fmt.Println("I am a Toyota, I can take you for a ride." ) } func (audi Audi) drive() { fmt.Println("I am an Audi, I can take you for a ride." ) } func main () { var car ICar car = new (Tesla) car.drive() car = new (Audi) car.drive() car = new (Toyota) car.drive() }
错误处理 Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义:
1 2 3 type error interface { Error() string }
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
并发 Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式:
如:
例子:
1 2 3 4 5 6 7 8 9 10 11 func concurrent_test (s string ) { for i := 0 ; i < 5 ; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } go concurrent_test("Audi" )go concurrent_test("BMW" )go concurrent_test("Tesla" )concurrent_test("Toyota" )
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Audi Toyota BMW Tesla Tesla Audi BMW Toyota Audi Toyota BMW Tesla Audi Tesla BMW Toyota Toyota
通道(channel) 通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:
1 ch := make (chan int , [buffer])
make 的第二个参数可以指定缓冲区的大小:
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func sum (s []int , c chan int ) { sum := 0 for _, v := range s { sum += v } c <- sum } func main () { s := []int {7 , 2 , 8 , -9 , 4 , 0 } c := make (chan int ) go sum(s[:len (s)/2 ], c) go sum(s[len (s)/2 :], c) x, y := <-c, <-c fmt.Println(x, y, x+y) }
下面的代码分配了三个缓冲区,但是同时使用4个造成了deadlock
1 2 3 4 5 6 7 8 9 c1 := make (chan int , 3 ) c1 <- 100 c1 <- 200 c1 <- 300 c1 <- 400 fmt.Println(<-c1) fmt.Println(<-c1) fmt.Println(<-c1) fmt.Println(<-c1)
可以通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:
如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func fibonacci (n int , c chan int ) { x, y := 0 , 1 for i := 0 ; i < n; i++ { c <- x x, y = y, x+y } close (c) } func main () { c := make (chan int , 10 ) go fibonacci(cap (c), c) for i := range c { fmt.Println(i) } }
Go常用数据结构和算法 Queue 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 queue := make ([]int , 5 ) for i := range queue { queue[i] = i } queue := make ([]int , 0 ) for i := 0 ; i < 5 ; i++ { queue = append (queue, i) } var queue []int for i := 0 ; i < 5 ; i++ { queue = append (queue, i) } queue = append (queue, 5 ) fmt.Println(queue) res := queue[0 ] fmt.Println(res) queue = queue[1 :] fmt.Println(queue)
Stack