### go特性
- 1.自動垃圾回收。
- 2.更豐富的內置類型。
- 3.函數多返回值。
- 4.錯誤處理。
- 5.匿名函數和閉包。
- 6.類型和接口。
- 7.并發編程。
- 8.反射。
- 9.語言交互性。
### 路徑
- GOPATH 項目路徑
- GOROOT go安裝路徑
設置路徑如下:
```bash
vi ~/.bashrc
export GOROOT=/usr/lib/go
export GOPATH=/data/wwwroot/go
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
export PATH=$PATH:/opt/php7/bin
```
### 命令
- go run:go run 編譯并直接運行程序,它會產生一個臨時文件(但不會生成 .exe 文件),直接在命令行輸出程序執行結果,方便用戶調試。
- go build:go build 用于測試編譯包,主要檢查是否會有編譯錯誤,如果是一個可執行文件的源碼(即是 main 包),就會直接生成一個可執行文件。
- go install:go install 的作用有兩步:第一步是編譯導入的包文件,所有導入的包文件編譯完才會編譯主程序;第二步是將編譯后生成的可執行文件放到 bin 目錄下($GOPATH/bin),編譯后的包文件放到 pkg 目錄下($GOPATH/pkg)。
### go數據類型
- channel
- map
- slice
- struct
### 聲明變量可見性
在 Go 中,包中成員以名稱首字母大小寫決定訪問權限。首字母大寫的名稱是被導出的。如下變量的聲明為例子:
- 函數內部,僅函數可見
- 函數外部,對當前包可見
- 函數外部且首字母是大寫,對所有包可見
### 首行代碼 package <name>
表示當前文件屬于哪個包,如果是package main表示當前文件是編譯后是一個可執行文件,編譯后可執行文件存放在bin目錄
> 但是同一目錄下的文件包名必須一致
### import 導入包
```bash
import "os/exec" -> /usr/local/go/pkg/darwin_amd64/os/exec.a
import "fmt" 最常用的一種形式(系統包)
import "shorturl/model 加載gopath/src/shorturl/model模塊(絕對路徑)
import f "fmt" 導入fmt,并給他啟別名f
import . "fmt" 將fmt啟用別名".",這樣就可以直接使用其內容,而不用再添加fmt。
如fmt.Println可以直接寫成Println
import _ "fmt" 表示不使用該包,而是只是使用該包的init函數,并不顯示的使用該包的其他內容。
注意:這種形式的import,當import時就執行了fmt包中的init函數,而不能夠使用該包的其他函數。
```
Note: 包路徑為src目錄下,不要用相對路徑
### 項目構建和編譯
- src: 源碼文件
- pkg: 包文件
- bin: 相關執行文件
### 下劃線
```go
import _ "fmt"
// 表示不使用fmt包,只使用fmt的init函數
f, _ := os.Open("xxxxxx")
// 返回句柄和錯誤,“_”表示忽略error錯誤
```
### 常量值省略
在常量組中,如不提供類型和初始化值,那么視作與上一個常量相同。一般只要第一個常量初始化值即可
```go
const (
s = "abc"
x // x = "abc"
)
```
### iota
在每一個const關鍵字出現時,被重置為0,然后再下一個const出現之前,每出現一次iota,其所代表的數字會自動增加1
```go
const (
Sunday = iota
Monday //通常省略后續行表達式
Tuesday
Wednesday
Thursday
Friday
Saturday
)
func main() {
fmt.Println(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
}
// 輸出:0 1 2 3 4 5 6
```
### struct類型
類型C語言的結構體,用來定義復雜的數據類型
### array類型
和C語言不一樣,go數組是值類型,這意味著賦值,傳參會復制數組,而不是指針
#### 初始化數組
初始化數組是可以使用索引號的
```go
var arr = [長度]類型{值,值,值}
var arr1 = [5]int{1, 2, 3, 4, 5}
var str = [5]string{3: "hello world", 4: "tom"}
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
var users = [...]struct {
name string
age uint8
}{
{"user1", 10}, // 可省略元素類型。
{"user2", 20}, // 別忘了最后一行的逗號。
}
a := [2]int{} // 默認值為0
```
長度可以用...代替,表示通過初始化值確定數組長度
#### 多維數組初始化
```go
a := [2][3]int{{1, 2, 3}, {4, 5, 6}} // 2行3列,每一個為一個花括號
b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 緯度不能用 "..."
```
指針數組 ??
數組指針 ??
### new和make的區別
- make和new用于分配內存
- make 用來創建map、slice、channel,返回對象非指針
- new 用來創建值類型,返回指向類型值的指針
### slice用于變長數組
slice切片,引用數組,引用數組,引用數組(切片修改的值,原數組也會改變),初始化如下:
```go
arr := arr[start:end]
arr := [10]int{0,1,2,3,4,5,6,7,8,9}
arr2 := arr[1:3] // 1,2
```
- start為0時是可以省略,表示從頭開始切切
- end省略表示切切到結尾結束
- start表示從下標start開始,end表示從下標end-1結束
#### make創建slice
```go
arr := make([]int, len, cap)
```
- len 可以使用的長度
- cap 容量,append擴展長度時,如果新的長度小于容量,不會更換底層數組,否則,go 會新申請一個底層數組,拷貝這邊的值過去,把原來的數組丟掉
#### append追加切片
append追加切片,并返回新slice對象;可以用兩種用法;
```go
slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...) // 拼接兩個切片
```
具體如下:
```go
var a = []int{1, 2, 3, 8:10} // 聲明并初始化一個切片,可以使用索引號
fmt.Printf("slice a : %v\n", a)
var b = []int{4, 5, 6}
fmt.Printf("slice b : %v\n", b)
c := append(a, b...)
fmt.Printf("slice c : %v\n", c)
d := append(c, 7)
fmt.Printf("slice d : %v\n", d)
e := append(d, 8, 9, 10)
fmt.Printf("slice e : %v\n", e)
// 輸出結果:
slice a : [1 2 3]
slice b : [4 5 6]
slice c : [1 2 3 4 5 6]
slice d : [1 2 3 4 5 6 7]
slice e : [1 2 3 4 5 6 7 8 9 10]
```
#### 切片結構 [x:y:z]
a[x:y:z] 切片內容 [x:y] 切片長度: y-x 切片容量:z-x
x是可以忽略的,即a[:y:z]的長度為y,切片容量為z
### 容器map
聲明和初始化: map[keyType]valueType,這塊相當于一個類型
```go
var m1 map[string]float32 = map[string]float32{"c":5, "go":5.5}
// 或者
m2 := map[string]float32{"c":5, "go":5.5}
```
#### 通過make創建map
```go
// 創建了一個鍵類型為string,值類型為int的map
m1 := make(map[string]int)
```
#### map增刪查改
```go
m1 := map[string]string{"key2": "value2", "key3": "value3"}
m1["key4"] = "value4" // 新增
fmt.Printf("增加key4:%v\n", m1)
m1["key2"] = "new_value2"
fmt.Printf("修改key2:%v\n", m1)
if val, ok := m1["key3"]; ok {
fmt.Println("key3 has found", val)
}
delete(m1, "key3")
fmt.Println("刪除key3", m1)
len := len(m1)
fmt.Printf("m1 len is %v\n", len)
```
#### map遍歷
map遍歷不能保證迭代返回次序,通常是隨機結果
```go
m := make(map[int]int)
for i := 0; i < 10; i++ {
m[i] = i
}
for j := 0; j < 2; j++ {
fmt.Println("---------------------")
for k, v := range m {
fmt.Printf("key -> value : %v -> %v\n", k, v)
}
}
```
### map和slice使用
```go
len := 10
items := make([]map[int]int, len)
for i := 0; i < len; i++ {
items[i] = make(map[int]int)
items[i][0] = i
items[i][1] = i + 1
}
fmt.Println(items)
```
### channel管道
- 類似于unix管道(pipe)
- 線程安全,多個goroutine同時訪問,不需要加鎖
channel聲明和初始化: chan type,這塊相當于一個類型
```go
var ch0 chan int // 一個只能存放整數的名字叫ch0的管道channel
var ch1 chan int = make(chan int) // 通過make創建一個channel類型
```
### channel緩沖
通過make第二個參數可以指定channel緩沖大小,這個意義在于:
- 對于發送者來說,直到channel滿時會阻塞,直到被接收者接受;
- 對于接收者來說,channel為空時,接收會阻塞,直到channel有數據
### channel發送和接受,關閉
```go
var ch chan int = make(chan int)
ch <- 1 // 發送數據到channel
x := <- ch // 從接受channel數據
close(ch) // 向關閉的channel發送數據會引起panic,接收數據會得到零值
```
- 執行關閉的channel,此時如果channel還有數據,則會在channel接收完畢后返回零值
```go
var ch0 chan int
ch0 = make(chan int, 11)
ch0 <- 99
for i := 0; i < 10; i++ {
ch0 <- i
}
frist_ch, ok := <-ch0
if ok {
fmt.Printf("fist ch is %v\n", frist_ch)
}
ch0 <- 10
close(ch0) // 關閉channel,若channel有值,則可以繼續接收channel,但是不能在向channel發送新數據
for {
var num int
num, ok := <-ch0
if ok == false {
fmt.Println("has close")
break
}
fmt.Println(num)
}
fmt.Println("all done")
```
或者
```go
close(ch0)
for num := range ch0 {
fmt.Println(num)
}
```
#### 單向channel
```go
c := make(chan int, 3)
var send chan<- int = c // send-only
var recv <-chan int = c // receive-only
send <- 1
// <-send // Error: receive from send-only type chan<- int
val, ok := <-recv
if ok {
fmt.Println(val)
}
// recv <- 2 // Error: send to receive-only type <-chan int
```
- chan<- 只發送數到channel
- <-chan 只從channel接收數據
#### channel demo
```go
package main
import "fmt"
type Request struct {
data []int
ret chan int
}
func NewRequest(data ...int) *Request {
return &Request{data, make(chan int, 1)}
}
func Process(req *Request) {
x := 0
for _, i := range req.data {
x += i
}
req.ret <- x
}
func main() {
req := NewRequest(10, 20, 30)
Process(req)
fmt.Println(<-req.ret)
}
```
### 函數
聲明方式: func 函數名稱(參數 參數類型) (返回類型) {}
```go
func test(x, y int, s string) (int, string) {}
func test(x int) int {}
```
- 合并同類型參數,用逗號隔開,類型放后面
- 沒有返回類型可以省略
#### 匿名函數
- 沒有函數名
- 可以賦值給變量
- 可以作為一種類型,例如func() string
```go
package main
import (
"fmt"
"math"
)
func main() {
// 普通使用
getSprt := func(a float64) float64 {
return math.Sqrt(a)
}
fmt.Println(getSprt(4))
// 作為管道
fc := make(chan func() string, 2)
fc <- func() string { return "hello world" }
fmt.Println((<-fc)())
// 作為結構體的一個字段
d := struct {
fn func() string
name string
}{
fn: func() string { return "struct function" },
name: "good name",
}
fmt.Println(d.fn(), d.name)
}
```
#### 函數閉包
一個函數嵌套另一個函數,閉包中變量始終存在,以下面為例:
```go
package main
import "fmt"
func test() func() int {
i := 0
fn := func() int {
i++
fmt.Println(i)
return i
}
return fn
}
func main() {
a := test()
a() // 1
a() // 2
a() // 3
b := test()
b() // 1
b() // 2
b() // 3
}
```
- a實際上指向了test()函數中的fn函數,每一次a()調用是直接指向fn函數,所以說,test函數中的i:=0只會在a:=test()執行一次
- a和b屬于兩個不同的環境
### defer延遲調用
- 最后執行
- 多個defer按照先進后出的方式執行
- 閉包中會先執行值,最后再調用結果
```go
package main
import (
"fmt"
// "time"
)
func main() {
v := 1
fn1 := func() {
fmt.Println("fn1", v)
}
fn2 := func() {
fmt.Println("fn2", v)
}
fn3 := func() func() {
fmt.Println("閉包")
return func() {
fmt.Println("fn3")
}
}
defer fn1()
defer fn2()
defer fn3()() // 當代碼到這一步時,不會調用,但是會執行閉包內的值
v = 2 // 改變了v的值,輸出結果也跟著改變
fmt.Println("runing")
}
```
輸出結果:
```go
閉包
runing
fn3
fn2 2
fn1 2
```
### 異常處理
- panic拋出錯誤
- recover捕獲錯誤
go通過panic拋出一個異常,然后在defer中通過recover捕獲這個異常
```go
package main
import (
"errors"
"fmt"
)
var ErrDivByZero = errors.New("division by zero")
func div(x, y int) (int, error) {
if y == 0 {
return 0, ErrDivByZero
}
return x / y, nil
}
func main() {
defer func() {
fmt.Println(recover())
}()
switch z, err := div(10, 0); err {
case nil:
println(z)
case ErrDivByZero:
panic(err)
}
}
```
### 錯誤處理
error是一個類型,類似int,float64
### 接口
一個類型實現了所有接口中定義的方法
實現的接口的類型,其i.(type)是接口名字
### 類型斷言
- 參數是任意類型,如i interface{}
- i.(type) ,其中i是接口值,type是類型
type兩種情況,具體類型和接口類型
- 具體類型:斷言成功,可以獲得i的具體值,失敗則panic
- 接口類型:...
### 類型轉換
顯示 靜態類型 底層類型 底層類型相同,也需要強制轉換
- 普通類型向接口類型的轉換是隱式的。
- 接口類型向普通類型轉換需要類型斷言。
### nil
nil可以和channel,func,interface,map,slice作比較
### 方法
類型, 該類型擁有的方法 叫方法的接受者是類型
類型只能是T或*T
值類型調用方法, 指針類型調用方法
無視類型調用方法,根據接受者類型操作內部
匿名字段
### 終端讀取
從os.Stdin讀取
fmt.Scanln(&a, &b) 終端多個值空格隔開,直到換行
fmt.SScan(string, format, &a, &b, &c)從字符串string按照format格式分別讀取a,b,c三個值
### 緩沖IO
緩沖的IO的作用避免大數據塊讀寫帶來的開銷
### windown和linux,mac交叉編譯
```go
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build gofile.go // mac
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build gofile.go // window
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build gofile.go // window
```
- GOOS:目標可執行程序運行操作系統,支持 darwin,freebsd,linux,windows
- GOARCH:目標可執行程序操作系統構架,包括 386,amd64,arm
### 反射reflect
反射機制就是在運行時動態的調用對象的方法和屬性,官方自帶的reflect包就是反射相關的,reflect可以識別interface{}底層具體類型和具體值
- reflect.Type // 具體類型
- reflect.Value // 具體值
- reflect.TypeOf(obj) // 返回具體類型
- reflect.ValueOf(obj) // 返回具體值
- reflect.TypeOf(obj).Kind() // 返回具體類型的類別
- reflect.ValueOf(obj).Numfield() // 具體值有多少個字段
- reflect.ValueOf(obj).Field(num) // 第num個字段的值
- Int() // 將值轉為int
- String() // 將值轉為string
```go
package main
import (
"fmt"
"reflect"
)
type Order struct {
ID int
Name string
Age int
}
func (o Order) Create() {
fmt.Println("生成訂單")
}
func main() {
order := Order{1, "Allen.Wu", 25}
t := reflect.TypeOf(order)
v := reflect.ValueOf(order)
k := t.Kind()
fmt.Println("reflect.Type=", t)
fmt.Println("reflect.Value=", v)
fmt.Println("t.Kind()=", k)
fmt.Println("reflect.ValueOf().NumField()=", v.NumField())
fmt.Println("reflect.ValueOf().Field(i)=", v.Field(0))
fmt.Println("reflect.ValueOf().Field(i).Interface()=", v.Field(0).Interface())
fmt.Println("reflect.TypeOf().NumField()=", t.NumField())
fmt.Println("reflect.TypeOf().Field(i)=", t.Field(0))
fmt.Println("reflect.TypeOf().Field(i).Name=", t.Field(0).Name)
fmt.Println("reflect.TypeOf().Field(i).Type=", t.Field(0).Type)
fmt.Println("reflect.TypeOf().NumMethod()=", t.NumMethod())
fmt.Println("reflect.TypeOf().Method(i)=", t.Method(0))
fmt.Println("reflect.TypeOf().Method(i).Name=", t.Method(0).Name)
fmt.Println("reflect.TypeOf().Method(i).Type=", t.Method(0).Type)
if reflect.TypeOf(order).Kind() == reflect.Struct {
v := reflect.ValueOf(order)
fmt.Println("number of fields:", v.NumField())
for i := 0; i < v.NumField(); i++ {
fmt.Printf("Field:%d type:%T value:%v v:%v \n", i, v.Field(i), v.Field(i), v.Field(i).Interface())
}
}
}
```
結果如下:
```
reflect.Type= main.Order
reflect.Value= {1 Allen.Wu 25}
t.Kind()= struct
reflect.ValueOf().NumField()= 3
reflect.ValueOf().Field(i)= 1
reflect.ValueOf().Field(i).Interface()= 1
reflect.TypeOf().NumField()= 3
reflect.TypeOf().Field(i)= {ID int 0 [0] false}
reflect.TypeOf().Field(i).Name= ID
reflect.TypeOf().Field(i).Type= int
reflect.TypeOf().NumMethod()= 1
reflect.TypeOf().Method(i)= {Create func(main.Order) <func(main.Order) Value> 0}
reflect.TypeOf().Method(i).Name= Create
reflect.TypeOf().Method(i).Type= func(main.Order)
number of fields: 3
Field:0 type:reflect.Value value:1 v:1
Field:1 type:reflect.Value value:Allen.Wu v:Allen.Wu
Field:2 type:reflect.Value value:25 v:25
```
- Order的字段首字母需要大寫,否則v.Field(0).Interface()報錯
- Field是struct結構才有的,像float64,int是沒有這個的
- 類型轉換,struct結構可以通過v.Field(0).Interface()獲得結果,而float64則需要通過v.Interface().(float64)獲得結果
- reflect.TypeOf(n)等效于reflect.ValueOf(n).Type()
#### reflect類型
- reflect.Int
- reflect.String
- reflect.Struct
- reflect.Func 可以調用.Call()
#### reflect具體類型轉換
通過reflect.ValueOf(t interface{})可以獲得類型為reflect.Value的值,如果需要進一步使用該值,需要進行轉換,如下:
強制類型轉換用Interface().(type);格式為:reflect.ValueOf(t).Interface().(type)
```go
package main
import (
"fmt"
"reflect"
)
func main() {
var n float64 = 1.23456
value := reflect.ValueOf(n)
fmt.Printf("類型是%T,值是%v \n", value, value)
var v float64
v = value.Interface().(float64)
fmt.Printf("類型是%T,值是%f \n", v, v)
}
```
輸出結果:
```
類型是reflect.Value,值是1.23456
類型是float64,值是1.234560
```
需要注意的是,轉換的時候需要區分指針和值,如value.Interface().(float64)和value.Interface().(*float64)是不一樣的
#### 通過反射調用方法
reflect.ValueOf(n).MethodByName("funcName").Call([]reflect.Value)流程如下:
- 獲得reflect.Value反射類型對象
- 通過reflect.Value調用MethodByName()獲得reflect.Value方法名
- 參數格式[]reflect.Value
- 調用Call
```go
package main
import (
"fmt"
"reflect"
)
type Order struct {
Id int
Name string
}
func (o Order) Handle(code string) {
fmt.Println("處理訂單號:", code)
}
func (o Order) Create() {
fmt.Println("創建訂單成功")
}
func main() {
var order = Order{1, "淘寶"}
getValue := reflect.ValueOf(order)
// 沒有參數
createMethod := getValue.MethodByName("Create")
args := make([]reflect.Value, 0)
createMethod.Call(args)
// 有參數
handleMethod := getValue.MethodByName("Handle")
args2 := []reflect.Value{reflect.ValueOf("xxxxdeewew33242342343333")}
handleMethod.Call(args2)
}
```
- php
- 編譯安裝
- 基本概念
- 垃圾回收機制
- 生命周期
- zval底層實現
- c擴展開發
- gdb調試工具
- 自定義擴展簡單demo
- 鉤子函數
- 讀取php.ini配置
- 數組
- 函數
- 類
- yaf擴展底層源碼
- swoole擴展底層源碼
- memoryGlobal內存池
- swoole協程使用記錄
- 單點登錄sso原理
- compser使用
- session實現機制
- c & linux
- gcc
- 指針
- 結構體,聯合和位字段
- 宏定義井號說明
- printf家族函數和可變參數
- 共享函數
- 靜態庫和動態庫
- makefile自動化構建
- 信號一
- 信號二
- inotify監控文件事件
- socket編程
- 簡介
- UNIX DOMAIN
- Internet DOMAIN
- TCP/IP
- 文件IO多路復用
- 內存管理
- 進程組,會話和控制終端
- daemon守護進程
- 多進程
- 多線程
- 常用進制轉換
- go
- 入門知識
- 字節和整數裝換
- python
- redis
- 應用場景
- 消息隊列
- 熱點數據
- 掃碼登錄
- 訂閱發布
- 次數限制
- 搶購超賣
- 持久化機制
- mysql
- 工作流程
- MyISAM和InnoDB區別
- 用戶和權限管理
- 執行計劃
- sql優化
- 事務和鎖
- 慢查詢日志
- case...when...then...end用法
- sql
- 參考
- linux
- 內核參數優化
- 防火墻設置
- docker
- docker入門知識
- 算法
- 多維數組合
- DFA算法
- 紅包金額分配