GO語言基礎必知必會
阿新 • • 發佈:2021-01-25
博主學習時總結的一些基礎知識點。拿捏!
1. 應用程式入口
- 必須是 main 包: package main
- 必須是 main 方法: func main()
- 檔名不一定是 main.go
2. 命令列引數接收
- 使用 os 包下面的 os.Args 引數
- 且下標從 1 開始
3.編寫測試程式
- 檔案包名為 xxx_test
- 檔名為 xxx_test.go
- 方法名為首字元大寫 Test 開頭,且形參為 t *testing.T
4.變數命名和賦值
一個名字必須以一個字母(Unicode字母)或下劃線開頭,後面可以跟任意數量的字母、數字或下劃線。大寫字母和小寫字母是不同的
var a int = 1
var b int = 2
// 也可以依賴自動型別判斷
var a = 1
var b = 2
// 也可以批量賦值
var (
a int = 1
b int = 2
c = 1
d = 2
)
// 還可以更簡單的下
a:=1
b:=2
// 還可以這樣批量賦值【元組賦值】
a:=1
b:=2
// 交換兩個的值
a,b = b,a
5.常量定義
const (
one = 1
two = 2
three = 3
)
// 等效於
const(
one = ital + 1
two
three
)
const(
Readable = 1 << iota //0001
Writeable //0010
Executable // 0100
)
6.資料型別
Go語言將資料型別分為四類:
6.1 基礎型別
- 數字
- 字串
- 布林型
6.2 複合型別
- 陣列
- 結構體
6.3 引用型別
- 指標
- 切片
- 字典
- 函式
- 通道
6.4 介面型別
bool string int int8 int16 int32 int64 unit unit8 unit16 unit32 unit64 unitptr byte //等同於unit8 rune // 等同 int32 unitcode編碼指標 float32 float64 complex64 complex128
- go 語言不支援隱式的型別轉換
- 別名和原有型別也不能進行隱式的轉換
- 字串的預設值是空字串不是nil
- 指標不支援運算(比如說+1)
string 是隻讀的 byte slice ,len 函式可以計算它所包含的 byte 數, string 的 byte 陣列可以存放任何資料
7.運算子
- 沒有前置++ 和前置–
- 陣列的比較(==)必須是相同型別且相同元素個數,否則會報錯
8.條件和迴圈
// 普通迴圈
n:= 0
for n < 5 {
n++
fmt.PrintLn(n)
}
//普通迴圈三次
for n:=0; n < 3; n++ {
}
// 無限迴圈
n := 0
for {
...
}
// 條件語句
if n==5 {
}
//多判斷,函式支援多個返回值
if result,err := someThing(); err == nil {
}
//switch 使用,支援case 後面接多個值,且不用帶 break
i:=32
switch i {
case 1:
t.Log(1)
case 2,32:
t.Log(32)
default:
t.Log(3)
}
9. 函式
// 支援多返回值
func test(a string, b *T) (result string, err error) {
return "ok", nil
}
a, err := test("a", &b);
if (err != nil) {
return err;
}
// 匿名函式
func squares() func() int {
var x int
return func() int {
x++
return x * x
}
}
//可變引數
func sum(vals ...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
// 延遲函式,執行順序和宣告相反
defer resp.Body.Close()
func timeSpent(inner func (op int) int) func(op int) int {
return func (n int) int {
start := time.Now()
ret := inner(n)
fmt.Println("time spent", time.Since(start).Seconds())
return ret
}
}
func slowFun(op int) int {
time.Sleep(time.Second * 1)
return op
}
ts := timeSpent(slowFun)
ts(10)
10 介面
// 定義介面
type Programmer interface {
Say() string
}
// 定義類
type GoProgrammer struct {
}
// 定義方法,方法定義和介面相同就是實現了這個介面
func (g *GoProgrammer) Say() string {
return "hello"
}
func main() {
// 定義介面變數
var p Programmer
// 實現介面
w = new(GoProgrammer)
// 呼叫介面方法
w.Say()
}
11.重寫
type Pet struct {
}
type Dog struct {
Pet
}
func (p *Pet) Say() {
fmt.Println("pet is saying")
}
func (p *Pet) Eat() {
fmt.Println("pet is eating")
}
func (d *Dog) Say() {
fmt.Println("dog is saying")
}
func TestExtension(t *testing.T) {
dog := new(Dog)
dog.Eat() //pet is eating
dog.Say() // dog is saying
}
12.錯誤機制
errors.New("test")
func TestRecover(t *testing.T) {
defer func() {
if err := recover() ; err != nil {
fmt.Println(err)
}
fmt.Print("over")
}()
panic(errors.New("problem"))
}
13.協程
// 如果main協程退出,那麼所有協程都會退出
func TestMyGo(t *testing.T) {
for i:=0 ; i < 10 ; i++ {
go func(i int) {
fmt.Println(i)
}(i)
time.Sleep(100 * time.Microsecond)
}
}
// 使用鎖機制避免資源競爭
var mut sync.Mutex
counts := 0;
for i:=0 ; i < 5000 ; i++ {
go func() {
defer func() {
mut.Unlock() //解鎖
}()
mut.Lock() // 鎖住
counts++
}()
}
time.Sleep(time.Second)
println(counts)
}
14.通道
- 通道是協程通訊使用的媒介
- 不帶緩衝區的通道會有阻塞作用
// 建立一個 channel (帶緩衝區和不帶緩衝區的)
ch1 := make(chan string)
ch2 := make(chan string, 2)
// 往通道里面寫東西
go func() {
ch1 <- "go"
ch2 <- "php"
}()
// 讀通道
println(<-ch1)
for ret := range ch2
// 關閉channel
close(ch1)
close(ch2)
15.多路選擇
// 多渠道的選擇
select {
case ret := <-retCh1:
t.Log(ret)
case ret := <-retCh2:
t.Log(ret)
default:
t.Error("No one returned")
}
// 超時控制
select {
case ret := <-retCh:
t.Log(ret)
case <-time.After(time.Second):
t.Log("time out")
}
16.使用通道進行非同步消費
func TestChannelClose(t *testing.T) {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(1)
dataProducer(ch, &wg)
wg.Add(1)
dataReceiver(ch, &wg)
wg.Wait()
}
// 生產者
func dataProducer(ch chan int , wg *sync.WaitGroup) {
go func() {
for i:=0; i < 20; i++ {
ch <-i
}
close(ch) // 如果不關閉通道,獲取的時候一直返回了的是 零值, true 。只有關閉的時候才會返回 nil, false
wg.Done()
}()
}
// 消費者
func dataReceiver(ch chan int , wg *sync.WaitGroup) {
go func() {
for{
if data, ok := <-ch; ok {
fmt.Println(data)
}else {
break
}
}
wg.Done()
}()
}
17.關閉通道
- 關閉一個未初始化(nil) 的 channel 會產生 panic
- 重複關閉同一個 channel 會產生 panic
向一個已關閉的 channel 中傳送訊息會產生 panic - 從已關閉的 channel 讀取訊息不會產生 panic,且能讀出 channel 中還未被讀取的訊息,若訊息均已讀出,則會讀到型別的零值。
- 從一個已關閉的 channel 中讀取訊息永遠不會阻塞,並且會返回一個為 false 的 ok-idiom,可以用它來判斷 channel 是否關閉
- 關閉 channel 會產生一個廣播機制,所有向 channel 讀取訊息的 goroutine 都會收到訊息
func TestCloseChannel(t *testing.T) {
ch := make(chan int)
for i:=0 ; i < 5 ; i++ { // 建立五個協程
go func(i int, ch chan int) {
for {
if isCancel(ch) { // 能消費到就會退出
break
}
time.Sleep(time.Microsecond * 5)
}
fmt.Println(i, "cancel")
}(i, ch)
}
cancle1(ch)
time.Sleep(time.Second)
}
func cancle1(ch chan int) {
// 關閉有通道廣播的作用,所有消費者都能收到通知,即 <-ch, 拿到對應的型別的零值
close(ch)
}
func cancle2(ch chan int) {
// 只塞了一個值,那麼只會有一個消費者拿到資料
ch <- 1
}
func isCancel(ch chan int) bool{
select {
case ret := <-ch:
fmt.Println(ret)
return true
default:
return false
}
}
18.Context
- 根 Context : 通過 context.Background() 建立
- 子 Context:context.WithCancel(parentContext) 建立, ctx, cancel := context.WithCancel(context.Background())
- 當前 Context 被取消時,基於他的子 context 都會被取消
- 接收取消通知 <-ctx.Done()
func TestMyContext(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) // 返回上下文和取消上下文的方法
for i:=0 ; i < 5; i++ {
go func(i int, ctx context.Context) {
for {
if isCancelled(ctx) {
break
}
time.Sleep(time.Microsecond * 5)
}
fmt.Println(i, "Cancelled")
}(i, ctx)
}
cancel() // 關閉上下文
time.Sleep(time.Second)
}
func isCancelled(ctx context.Context) bool {
select {
case <-ctx.Done(): // 上下文結束
return true
default:
return false
}
}
19.sync once 使用
func TestSyncOnce(t *testing.T) {
for i:=0; i<3; i++ {
go doOnce() //只會有一個協程執行成功
}
}
func doOnce() {
once.Do(func() {
println("我只能執行一次")
})
}
20.sync pool 使用
- 嘗試從私有物件獲取(只有一個)
- 私有物件不存在,嘗試從當前的 processor的共享池獲取(會有鎖的開銷)
- 如果當前processor共享池也是空的,那麼就嘗試去其他 processor的共享池獲取
- 如果所有子池都是空的,最後就用使用者指定的 new 函式產生一個新的物件返回
- GC會清除 sync.pool 快取的物件
- 物件的快取有效期為下一次GC之前
- 適合通過複用,降低複雜物件的建立和GC代價
- 協程安全,會有鎖的開銷
- 生命週期受 GC 影響,不適合於做連線池等,需自己管理生命週期的資源的池化
func TestSyncPoolTest(t *testing.T) {
pool := &sync.Pool{
New: func() interface{}{
return 0
}}
v := pool.Get().(int)
fmt.Println(v)// 0 預設呼叫自定義的 new 方法
pool.Put(3) // 優先放在私有變數中
w := pool.Get().(int) // 拿池裡面的東西
fmt.Println(w) //3
}
21.反射
- reflect.TypeOf 返回型別(reflect.Type)
- reflect.ValueOF 返回值(reflect.Value)
- 可以從 reflect.Value 獲得型別
- 通過 kind 來判斷型別
func TestReflect(t *testing.T) {
var s int32
fmt.Println(getType(s))// int
}
func getType(v interface{}) string {
t := reflect.TypeOf(v) // 返回值是 reflect.Type
switch t.Kind() { // 獲得型別
case reflect.Int32, reflect.Int64:
return "int"
case reflect.Float32, reflect.Float64:
return "float"
default:
return "Unknown"
}
}
22.內建 json 解析
type Man struct {
Name string `json:"name"`
Age int64 `json:"age"`
Sons [2]Son
}
type Son struct {
SonName string `json:"son_name"`
SonAge int64 `json:"son_age"`
}
func main() {
man := Man{}
man.Age = 32
man.Name = "china"
son := Son{}
son.SonName = "son"
son.SonAge = 2
man.Sons[0] = son
rs, _ := json.Marshal(man)
fmt.Println(string(rs)) // {"name":"china","age":32,"address":"xxx","Sons":[{"son_name":"son","son_age":2},{"son_name":"","son_age":0}]}
}
23.構建HTTP服務
func TestMyHttp(t *testing.T) {
http.HandleFunc("/", sayHelloName) // 路由
err := http.ListenAndServe(":9000", nil) // 監聽服務
if err != nil {
log.Fatal("err", err)
}
}
func sayHelloName(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm() //先解析才可以獲取格式化好的引數, 否則下面打印出來是空map
fmt.Println(r.Form)
}