1. 程式人生 > 其它 >GO語言基礎必知必會

GO語言基礎必知必會

技術標籤:Go大法go語法

博主學習時總結的一些基礎知識點。拿捏!

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)
}