该笔记参考 《Go语言学习笔记》
学习go语言之前需要了解的内容:
GO语言的代码文件以.go
命名,语句结束分号被默认省略,C样式的注释,入口函数main()
没有参数,注意入口函数必须放在main
包中。
package main
func main(){
println("hello world")
}
包导入
import (
'fmt'
)
流程控制
方式一:
x := 100
if x > 0 {
print(x)
} else if x == 0 {
print(x)
} else {
print(x)
}
方式二:
func main() {
x := 100
switch {
case x > 0:
print("x")
case x == 0:
print("0")
case x < 0:
print("-x")
}
}
循环遍历
方式一
func main() {
for i := 0; i < 5; i++ {
println(i)
}
for i := 4; i >= 0; i-- {
println(i)
}
}
方式二
func main() {
x := 0
for x < 5 { // 相当于 while(x < 5)
println(x)
x++
}
}
// for 可以当作while来使用
func main() {
x := 0
for { // equals to while(True)
println(x)
x++
if x >= 5 {
break
}
}
}
函数
// div is the function name, and a, b are parameters
// this functions has two returns, one is int type and the other is error type
func div(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
a, b := 10, 0
c, err := div(a, b)
fmt.Println(c, err)
}
// 函数作为返回值
func test(x int) func() {
return func() {
println(x)
}
}
func main() {
x := 100
f := test(x)
f()
}
// 用defer 定义延迟调用,无论函数是否出错,它都确保结束前被调用
func test_defer(a, b int) {
defer println("dispose...") // 通常用来释放资源,解除锁定,或者执行一些清理操作
// 可以定义多个defer, 按照FIFO顺序执行
println(a / b)
}
func main() {
test_defer(10, 5)
}
切片 slice 可以实现类似动态数组的功能
func main() {
// test_defer(10, 5)
x := make([]int, 0, 5) // 创建容量为5的切片
for i := 0; i < 8; i++ {
x = append(x, i) //追加数据
}
fmt.Println(x)
}
map 字典, 将字典类型内置,可以直接从运行时层面获得性能优化
func main() {
// test_defer(10, 5)
// x := make([]int, 0, 5) // 创建容量为5的切片
// for i := 0; i < 8; i++ {
// x = append(x, i) //追加数据
// }
// fmt.Println(x)
m := make(map[string]int) // 创建字典对象
m["a"] = 1 // 添加或设置
x, ok := m["b"] //使用ok-idiom(习惯用语) 获取值,可以知道key/value是否存在
y, ok := m["a"]
fmt.Println(x, ok)
fmt.Println(y, ok)
delete(m, "a") // 删除字典中key="a"
z, ok := m["a"]
fmt.Println(z, ok)
}
结构体 struct 可以匿名嵌入其他类型
type user struct{ // 结构体类型 user
name string
age byte
}
type manager struct{ // 匿名嵌入其他类型
user
title string
}
func main() {
var m manager
m.name = "Alice"
m.age = 29
m.title = "CTO"
fmt.Println(m)
}
// output
// {{Alice 29} CTO}
可以为当前包内的任意类型定义方法
type X int
func (x *X) inc() {
*x++
}
func main() {
var x X
x.inc()
println(x)
}
还可以直接调用匿名字段的方法,这种方式可以实现与继承类似的功能
import (
"fmt"
)
type user struct {
name string
age byte
}
func (u user) toString() string {
return fmt.Sprintf("%+v", u)
}
type manager struct {
user
title string
}
func main() {
var m manager
m.name = "Alice"
m.age = 29
println(m.toString()) // 调用 user.toString()方法
}
接口采用了duck type 方式,也就是说无须在实现类型上添加显式声明
import (
"fmt"
)
type user struct {
name string
age byte
}
func (u user) toString() string {
return fmt.Sprintf("%+v", u)
}
func (u user) Print() {
fmt.Printf("%+v\n", u)
}
type Printer interface { // 接口类型
Print()
}
func main() {
var m user
m.name = "Alice"
m.age = 29
// 声明一个变量p 类型为Printer,
//该类型为一个接口类型, 因为m 实现了这个方法
// 所以 m可以赋值给p
var p Printer = m
p.Print()
}
另外,go语言有空接口类型interface{}
,用途类似OOP里面的system.Object
, 可接收任意类型的对象。
整个运行时完全并发化设计,凡是所见的,几乎都在以goroutine
方式运行,这是一种比普通协程或线程更加高效的并发设计,能轻松创建和运行成千上万的并发任务。
package main
import (
"fmt"
"time"
)
func task(id int) {
for i := 0; i < 5; i++ {
fmt.Printf("id = %d: %d\n", id, i)
time.Sleep(time.Second)
}
}
func main() {
go task(1)
go task(2) // 创建 goroutine
time.Sleep(time.Second * 6)
}
通道 channel 与 goroutine 搭配,实现用通信代替内存共享的CSP模型
package main
import (
"fmt"
"time"
)
func task(id int) {
for i := 0; i < 5; i++ {
fmt.Printf("id = %d: %d\n", id, i)
time.Sleep(time.Second)
}
}
// 消费者
func consumer(data chan int, done chan bool) {
for x := range data { // 接收数据
println("recv:", x)
}
done <- true // 通知main, 消费结束
}
// 生产者
func producer(data chan int) {
for i := 0; i < 4; i++ {
data <- i // 发送数据
}
close(data) // 生产结束,关闭通道
}
func main() {
done := make(chan bool) // 用于接收消费结束通道
data := make(chan int) // 数据管道
go consumer(data, done) // 启动消费者
go producer(data) // 启动生产者
<-done // 阻塞,直到消费者发回结束信号
}