语言特性
自动垃圾回收(GC)
所谓垃圾回收,即所有的内存分配动作都会被在运行时记录,同时任何对该内存的使用也都会被记录,然后垃圾回收器会对所有已经分配的内存进行跟踪监测,一旦发现有些内存已经不再被任何人使用,就阶段性地回收这些没人用的内存。更丰富的内置类型
字典类型(map)和数组切片(Slice)。可以认为数组切片是一种可动态增长的数组。函数多返回值
错误处理: defer, panic, recover
匿名函数和闭包
类型和接口
并发编程
goroutine是一种比线程更加轻盈、更省资源的协程。Go语言通过系统的线程来多路派遣这些函数的执行,使得每个用go关键字执行的函数可以运行成为一个单位协程。当一个协程阻塞的时候,调度器就会自动把其他协程安排到另外的线程中去执行,从而实现了程序无等待并行化运行。此外,由于一个进程内创建的所有goroutine运行在同一个内存地址空间中,因此如果不同的goroutine不得不去访问共享的内存变量,访问前应该先获取相应的读写锁。Go语言标准库中的sync包提供了完备的读写锁功能。
package main
import "fmt"
func sum(values []int, resultChan chan int) {
sum := 0
for _, value := range values {
sum += value
}
resultChan <- sum // 将计算结果发送到channel中
}
func main() {
values := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
resultChan := make(chan int, 2)
go sum(values[:len(values)/2], resultChan)
go sum(values[len(values)/2:], resultChan)
sum1, sum2 := <-resultChan, <-resultChan // 接收结果
fmt.Println("Result:", sum1, sum2, sum1+sum2)
}
- 反射
- 语言交互性
在Go代码中,可以按Cgo的特定语法混合编写C语言代码。
Go语言还提供了一个关键字range,用于便捷地遍历容器中的元素。
for i, v := range array {
fmt.Println("Array element[", i, "]=", v)
}
mySlice = append(mySlice, 1, 2, 3)
mySlice2 := []int{8, 9, 10}
// 给mySlice后面添加另一个数组切片
mySlice = append(mySlice, mySlice2...)
需要注意的是,第二个参数mySlice2后面加了三个点,如果没有这个省略号的话,会有编译错误,因为按append()的语义,从第二个参数起的所有参数都是待附加的元素。因为mySlice中的元素类型为int,所以直接传递mySlice2是行不通的。加上省略号相当于把mySlice2包含的所有元素打散后传入。
在有返回值的函数中,不允许将“最终的”return语句包含在if...else...结构中,否则会编译失败:
function ends without a return statement
失败的原因在于,Go编译器无法找到终止该函数的return语句。编译失败的案例如下:
func example(x int) int {
if x == 0 {
return 5
} else {
return x
}
}
可以不设定switch之后的条件表达式,在此种情况下,整个switch结构与多个if...else...的逻辑作用等同。
switch {
case 0 <= Num && Num <= 3:
fmt.Printf("0-3")
case 4 <= Num && Num <= 6:
fmt.Printf("4-6")
case 7 <= Num && Num <= 9:
fmt.Printf("7-9")
}
func myfunc(args ...int) {
for _, arg := range args {
fmt.Println(arg)
}
}
这段代码的意思是,函数myfunc()接受不定数量的参数,这些参数的类型全部是int。形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。
闭包
package main
import (
"fmt"
)
func main() {
i := 1
a := func() func() {
j := 2
return func() {
fmt.Println("i,j:", i, j)
}
}
a()()
i = 10
a()()
}
在变量a指向的闭包函数中,只有内部的匿名函数才能访问变量j,而无法通过其他途径访问
到,因此保证了i的安全性。
在Go语言中数组是一个值类型(value type)。所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。如果将数组作为函数的参数类型,则在函数调用时该参数将发生数据复制。
所有的Go语言类型(指针类型除外)都可以有自己的方法。
在Go语言中,未进行显式初始化的变量都会被初始化为该类型的零值,例如bool类型的零值为false,int类型的零值为0,string类型的零值为空字符串。
要使某个符号对其他包(package)可见(即可以访问),需要将该符号定义为以大写字母开头。需要注意的一点是,Go语言中符号的可访问性是包一级的而不是类型一级的。
Mutex
所有在 Lock 和 Unlock 之间的代码,都只能由一个 Go 协程执行。
package main
import (
"fmt"
//"sync"
"time"
)
//var lock sync.Mutex
var x int = 0
func main() {
for i := 0; i < 1000; i++ {
go add()
}
time.Sleep(1e9)
fmt.Println(x)
}
func add() {
//lock.Lock()
x += 1
//lock.Unlock()
}
输出:
970
用channel实现Mutex
package main
import (
"fmt"
"time"
)
var x int = 0
func main() {
ch := make(chan bool, 1)
for i := 0; i < 1000; i++ {
go add(ch)
}
time.Sleep(1e9)
fmt.Println(x)
}
func add(ch chan bool) {
ch <- true
x += 1
<-ch
}
输出:
1000
全局唯一性操作
package main
import (
"fmt"
"sync"
"time"
)
var x int = 0
var once sync.Once
func main() {
for i := 0; i < 1000; i++ {
go ADD()
}
time.Sleep(1e9)
fmt.Println(x)
}
func add() {
x += 1
}
func ADD() {
once.Do(add)
}
输出:
1
Marshal and Unmarshal
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
type student struct {
ID int
Name string
Colors []string
}
jthe := student{
ID: 21116,
Name: "Hejtao",
Colors: []string{"blue", "red", "black"},
}
b, err := json.Marshal(jthe)
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
}
输出:
{"ID":21116,"Name":"Hejtao","Colors":["blue","red","black"]}
package main
import (
"encoding/json"
"fmt"
)
func main () {
var jsonBlob = []byte ( ` [
{ "Name" : "Platypus" , "Order" : "Monotremata" } ,
{ "Name" : "Quoll" , "Order" : "Dasyuromorphia" }
] ` )
type Animal struct {
Name string
Order string
}
var animals []Animal
err := json. Unmarshal (jsonBlob , &animals)
if err != nil {
fmt. Println("error:" , err)
}
fmt. Printf("%+v" , animals)
}
输出:
[{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]