Go
没有分号
1:类型后置。(方便阅读)
2:大写字母开头,标识,是导出的
3:返回值可以命名, 没有参数的 return 语句返回已命名的返回值,
4:var 声明变量列表,有初始值时,可以省略类型,会自动判断类型
5:在函数中!,简洁赋值语句 := 可在类型明确的地方代替 var 声明。
6:没有明确初始值的变量声明会被赋予它们的 零值。0,false,""
7: fmt.Printf %v %q对于字符串会输出"", %T 类型
8:const Pi = 3.14 常量使用const,常量不能用 := 语法声明, 常量可以自动推导类型
9:导入 import 与 const 都是用()
10:注释// /**/都可以
11:数值常量是高精度的 值。 const Big = 1 << 100
12: for i := 0; i < 10; i++ { } 初始化与后置语句可选
13:while没有,有for for sum < 100 { }
14:省略循环条件,无限循环 for { }
15:if x < 0 { } ,
同 for 一样, if 语句可以在条件表达式前执行一个简单的语句。else作用于同样有效
16:switch case 不必为常量,默认有break,除非加上fallthrough,才会向下执行
没有条件的 switch 同 switch true 一样。相当于一堆if-else 更清晰
17:defer 语句会将函数推迟到外层函数返回之后执行。
推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。
推迟的函数调用会被压入一个栈中。
18:var arr [2]int = [2]int{1,2} , Go 没有指针运算
19:结构体是一组字段:type Vertex struct { ... },unsafe.Sizeof可以取大小/偏移,内部没有逗号
结构体字段使用点来访问((*p).X,或p.X指针也是用点,隐式间接引用。)
20:var 可以批量定义
type Vertex struct {
X, Y int
}
var (
v0 Vertex
v1 = Vertex{1, 2} // 创建一个 Vertex 类型的结构体
v2 = Vertex{X: 1} // Y:0 被隐式地赋予
v3 = Vertex{} // X:0 Y:0
p = &Vertex{1, 2} // 创建一个 *Vertex 类型的结构体(指针)fmt.Println打印 &{1,2}
)
21:遍历数组
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)
fmt.Printf("%v\n", primes);
for i:= 0;i
}
fmt.Println()
for key, value := range primes {
fmt.Println(key, value)
}
22:切片(上下界,只代表下标,前闭后开),表示数组的引用,默认上界是len,下界是0
切片还可以继续取切片
var s []int = primes[1:4]
fmt.Println(s)
[]bool{true, true, false} 不带长度,就会创建一个带长度的数组,然后构建一个引用了它的切片
s := []struct { // 匿名结构体类型
i int
b bool
}{
{2, true},
{3, false},
{5, true},
{7, true},
{11, false},
{13, true},
}
23:
切片的长度就是它所包含的元素个数。
切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
切片 s 的长度和容量可通过表达式 len(s) 和 cap(s) 来获取。
你可以通过重新切片来扩展一个切片,给它提供足够的容量。
24:
切片的零值是 nil。
nil 切片的长度和容量为 0 且没有底层数组。
var s []int
有底层数组的不会是nil
25:
用make创建切片b := make([]int, 0, 5) // len(b)=0, cap(b)=5
26:切片可以包含任意类型,可以嵌套
strings.Join([]string, "")
27:切片可以追加元素:func append(s []T, vs ...T) []T
底层数组太小会重新分配更大的数组[类似vector]
也可以追加切片
sl_b = append(sl_b, sl_c...)
27:for 循环的 range 形式可遍历切片或映射。
当使用 for 循环遍历切片时,每次迭代都会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。
for i, v := range pow {
28:映射
映射的零值为 nil 。nil 映射既没有键,也不能添加键。var m1 map[int]string
make 函数会返回给定类型的映射,并将其初始化备用。
var m map[string]Vertex
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
29:
映射的文法与结构体相似,不过必须有键名。
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
若顶级类型只是一个类型名,你可以在文法的元素中省略它。
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
30:
删除元素:
delete(m, key)
通过双赋值检测某个键是否存在:
elem, ok = m[key]
31:
函数也是值。它们可以像其它值一样传递。
函数值可以用作函数的参数或返回值。
支持 匿名函数 对象
32:函数的闭包
Go 函数可以是一个闭包。闭包是一个函数值,它引用了其函数体之外的变量。该函数可以访问并赋予其引用的变量的值,换句话说,该函数被这些变量“绑定”在一起。
例如,函数 adder 返回一个闭包。每个闭包都被绑定在其各自的 sum 变量上。
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
0 0
1 -2
3 -6
6 -12
10 -20
15 -30
21 -42
28 -56
36 -72
45 -90
33:
Go 没有类。不过你可以为结构体类型定义方法。
方法就是一类带特殊的 接收者 参数的函数。
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
34:就是接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法。
type MyFloat float64 (间接)
func (f MyFloat) Abs() MyFloat {
你可以为指针接收者声明方法。
func (v *Vertex) Scale(f float64) {
35:函数参数 指针与值的传递有明确限制
而方法 调用者 指针与值 可以通用(go会自己转化)
36:指针作为接受者,可以避免值的复制,值与指针接受者不应该混用。
37:接口类型 是由一组方法签名定义的集合。
接口类型的变量可以保存任何实现了这些方法的值。
type Abser interface {
Abs() float64
}
38:在内部,接口值可以看做包含值和具体类型的元组:
(value, type)
fmt.Printf("(%v, %T)\n", i, i)
(3.141592653589793, main.F)
39:底层值为 nil 的接口值
即便接口内的具体值为 nil,方法仍然会被 nil 接收者调用。
在一些语言中,这会触发一个空指针异常,但在 Go 中通常会写一些方法来优雅地处理它(如本例中的 M 方法)。
注意: 保存了 nil 具体值的接口其自身并不为 nil。
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
i = &T{"hello"}
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
(
(&{hello}, *main.T)
hello
40:interface{}
空接口可保存任何类型的值
空接口被用来处理未知类型的值。例如,fmt.Print 可接受类型为 interface{} 的任意数量的参数。
package main
import "fmt"
func main() {
var i interface{}
describe(i)
i = 42
describe(i)
i = "hello"
describe(i)
}
func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}
(
(42, int)
(hello, string)
41:类型断言 提供了访问接口值底层具体值的方式。
var i interface{} = "hello"
s, ok := i.(string)
fmt.Println(s, ok)
用两个参数接收不会产生恐慌panic,s := i.(float64) 会产生错误
42:类型选择:
package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
case bool:
fmt.Printf("%v \n", v)
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}
43:fmt 包中定义的 Stringer 是最普遍的接口之一。
type Stringer interface {
String() string
}
fmt.Println 会用到
44:error 类型是一个内建接口:
type error interface {
Error() string
}
同样Print时会用到
i, err := strconv.Atoi("42-")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
45:io 包指定了 io.Reader 接口,它表示从数据流的末尾进行读取。
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
n = 8 err =
b[:n] = "Hello, R"
n = 6 err =
b[:n] = "eader!"
n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
b[:n] = ""
并发:Go 程(goroutine)是由 Go 运行时管理的轻量级线程。
1:
time.Sleep(100 * time.Millisecond)
2:sync 包提供了同步能力
3:信道是带有类型的管道,你可以通过它用信道操作符 <- 来发送或者接收值。
ch <- v // 将 v 发送至信道 ch。
v := <-ch // 从 ch 接收值并赋予 v。
(“箭头”就是数据流的方向。)
和映射与切片一样,信道在使用前必须创建:
ch := make(chan int)
默认情况下,发送和接收操作在另一端准备好之前都会阻塞。这使得 Go 程可以在没有显式的锁或竞态变量的情况下进行同步。
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 将和送入 c
}
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 // 从 c 中接收
fmt.Println(x, y, x+y)
}
4:
信道可以是 带缓冲的。将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道:
ch := make(chan int, 100)
满空都会阻塞。
5:发送者可通过 close 关闭一个信道来表示没有需要发送的值了。接收者可以通过为接收表达式分配第二个参数来测试信道是否被关闭:若没有值可以接收且信道已被关闭,那么在执行完
v, ok := <-ch
之后 ok 会被设置为 false。
循环 for i := range c 会不断从信道接收值,直到它被关闭。
*注意:* 只有发送者才能关闭信道,而接收者不能。向一个已经关闭的信道发送数据会引发程序恐慌(panic)。
*还要注意:* 信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有需要发送的值时才有必要关闭,例如终止一个 range 循环。
6:select 语句使一个 Go 程可以等待多个通信操作。
select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。
7:生产者消费者
package main
import (
"fmt"
"time"
)
func Proc(c, quit chan string, name string) {
for i := 0; i< 1000; i++{
s := name + "-" + strconv.FormatInt(int64(i), 10)
fmt.Printf("product %s \n", s)
select {
case c <- s:
}
// 不能带default,带了default就不会阻塞,会丢失数据
}
quit <- ""
}
func Cust(c chan string, name string) {
for i := range c {
fmt.Printf("%s custome %s \n", name, i)
}
fmt.Println(name, "over")
}
func main(){
c := make(chan string, 50)
quit := make(chan string, 50)
go Cust(c, "customer1")
go Cust(c, "customer2")
go Proc(c, quit, "proc1")
go Proc(c, quit, "proc2")
go Proc(c, quit, "proc3")
for i := 0; i < 3; i++ {
<- quit
}
fmt.Println("main over")
close(c)
time.Sleep(1 * time.Second)
return
}
8:有default就不会阻塞!!!
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
// default:
// fmt.Println(" .")
// time.Sleep(50 * time.Millisecond)
}
}
9:sync.Mutex
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter 的并发使用是安全的。
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
// Inc 增加给定 key 的计数器的值。
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
c.v[key]++
c.mux.Unlock()
}
// Value 返回给定 key 的计数器的当前值。
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
defer c.mux.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}