溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

寫給大忙人看的Go語言

發(fā)布時(shí)間:2020-07-19 09:58:34 來源:網(wǎng)絡(luò) 閱讀:3609 作者:BlueMiaomiao 欄目:編程語言

Tips

寫給大忙人看的Golang教程(一)
閱讀本文之前,我認(rèn)為你已經(jīng)掌握其他語言基礎(chǔ)并寫出一個(gè)簡(jiǎn)單的項(xiàng)目。

(1)Golang編程注意事項(xiàng)

  • 源文件必須以.go為擴(kuò)展名.
  • Go應(yīng)用程序d額執(zhí)行入口是main()方法.
  • Go代碼嚴(yán)格區(qū)分大小寫.
  • Go代碼不需要分號(hào).
  • Go代碼不允許多條語句在同一行出現(xiàn).
  • Go語言重定義的變量和導(dǎo)入的包如果沒有被使用不會(huì)編譯通過.
  • Go語言大括號(hào)是成對(duì)出現(xiàn)的.

(2)Golang中的常用轉(zhuǎn)義字符

  • \t 制表符
  • \n 換行符
  • \\ 一個(gè)斜杠
  • \" 一個(gè)引號(hào)
  • \r 一個(gè)回車

(3)注釋方式

  • // 注釋內(nèi)容行注釋
  • /* 注釋內(nèi)容 */多行注釋
  • 多行注釋不可以嵌套多行注釋

(4)Golang的代碼規(guī)范

  • 盡量使用行注釋注釋整個(gè)方法或語句
  • 使用Tab縮進(jìn)
  • 使用gofmt -w格式化代碼
  • 運(yùn)算符兩側(cè)加空格
  • Golang的代碼風(fēng)格:

    // HelloWorld.go
    
    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("Hello World")
    }
  • 一行代碼最好不要超過80個(gè)字符

(5)官方編程指南

  • Go的官方網(wǎng)站 https://golang.org
  • Go的標(biāo)準(zhǔn)庫英文官方文檔 https://golang.org/pkgdoc
  • Go的標(biāo)準(zhǔn)庫中文官方文檔 https://studygolang.com/pkgdoc

(6)變量

  • 使用var關(guān)鍵字定義變量:var 變量名稱 數(shù)據(jù)類型
  • 使用類型推導(dǎo):var 變量名稱 = 值
  • 省略var關(guān)鍵字:變量名稱 := 值, 變量名稱不應(yīng)該是已經(jīng)定義過的
  • 變量名稱 := 值等同于var 變量名稱 數(shù)據(jù)類型 = 值
  • 多變量聲明:var 變量名稱, 變量名稱... 數(shù)據(jù)類型
  • 多變量賦值:var 變量名稱, 變量名稱, ... = 值, 值, ...
  • 省略var關(guān)鍵字多變量聲明和賦值: 變量名稱, 變量名稱, ... := 值, 值, ...
  • 聲明全局變量:
    var (
        變量名稱 = 值
        ...
    )

(7)Golang支持的數(shù)據(jù)類型

寫給大忙人看的Go語言

  • 使用unsafe.Sizeof()查看變量占用的空間

    package main
    
    import (
    "fmt"
    "unsafe"
    )
    
    func main() {
    var x int = 10
    fmt.Println("The x size: ", unsafe.Sizeof(x))
    // The x size:  8
    }
  • float32表示單精度,float64表示雙精度

  • 字符常量使用單引號(hào)

  • Go中的字符編碼使用UTF-8類型

  • bool類型只能取truefalse

  • 在Go中字符串是不可變的

  • Go支持使用反引號(hào)輸出原生字符串

  • 字符串拼接使用加號(hào)時(shí)最后一個(gè)加號(hào)需要保留在行尾

(8)基本數(shù)據(jù)類型轉(zhuǎn)換

  • 數(shù)值類型互相轉(zhuǎn)換:目標(biāo)數(shù)據(jù)類型(變量)。
  • 數(shù)值與字符串互轉(zhuǎn):
    • 數(shù)字轉(zhuǎn)字符串:使用fmt.Sprintf()字符串格式化函數(shù)。
    • 數(shù)字轉(zhuǎn)字符串:使用strconv.FormatBool()、strconv.FormatInt()、strconv.FormatUint()、strconv.FormatFloat()格式化函數(shù)。
    • 字符串轉(zhuǎn)數(shù)字:使用strconv.ParseBool()、strconv.ParseInt()、strconv.ParseUint()strconv.ParseFloat()格式化函數(shù)。

(9)指針數(shù)據(jù)類型

package main

import "fmt"

func main() {

    var i = 10
    var ptr *int = &i

    fmt.Println("變量i的內(nèi)存地址是: ", ptr)
    // 變量i的內(nèi)存地址是:  0xc00004a080
    fmt.Println("變量i的存儲(chǔ)內(nèi)容是: ", *ptr)
    // 變量i的存儲(chǔ)內(nèi)容是:  10
}

(10) 值類型與引用類型

寫給大忙人看的Go語言

寫給大忙人看的Go語言

  • 值類型通常存放到棧區(qū)。
  • 引用類型通常存放在堆區(qū),棧中有堆中的引用。

(11)Golang中的標(biāo)識(shí)符

  • Go中使用_表示空標(biāo)識(shí)符
  • 嚴(yán)格區(qū)分大小寫
  • 包名盡量與目錄保持一致
  • 推薦使用駝峰命名法
  • 變量名稱、函數(shù)名稱、常量名稱首字母大寫表示可以被其他包訪問,否則表示私有的

(12)運(yùn)算符

  • Golang中只有x++x--,沒有++x--x
  • 自增自減運(yùn)算值獨(dú)立的語句,不允許類似的:x := a++

(13)控制臺(tái)輸入輸出

  • fmt.Sacnf():使用指定的格式獲取文本
  • fmt.Sacnln():以換行為結(jié)束的文本

(14) 原碼、反碼、補(bǔ)碼

  • 計(jì)算機(jī)中0表示正數(shù),1表示負(fù)數(shù)
  • 計(jì)算機(jī)中都是用補(bǔ)碼進(jìn)行運(yùn)算的,因?yàn)镃PU只會(huì)加法運(yùn)算
  • 正數(shù)的原、反、補(bǔ)都是相同的
  • 負(fù)數(shù)的反碼是原碼符號(hào)位不變其他位取反
  • 負(fù)數(shù)的補(bǔ)碼是反碼加1
  • 0的原、反、補(bǔ)相同

(15)流程控制

(15.1)順序控制

(15.2) 分支控制
  • if語句:

    if x>12 { 
    }
    // Golang支持直接在condition中定義變量
    if x:=12; x>12 {
    }
  • if-else語句:

    if x:=12; x>20 {
    }else {
    }
  • if-else if-else語句:

    if x:=12; x>100 {
    }else if x>50 {
    }else if x>10 {
    }else {
    }
  • switch-case-default語句:

    // 每個(gè)分支不需要break語句
    // switch也支持在condition中直接定義變量
    // case支持多個(gè)表達(dá)式
    // 取消break使用fallthrough語句————switch穿透
    switch y:=10;y {
    case 5:
        // something
    case 10:
        // something
        fallthrough
    case 20, 25, 30:
        // something
    default:
        // something
    }
(15.3)循環(huán)控制
  • for循環(huán)

    for i:=1;i<10;i++ {
    }
    // Golang也提供了for-each或for-range類似的循環(huán)
    str := "Hello Golang."
    for index, value:=range str {
      // index表示索引
      // value表示值
    }
  • while循環(huán)

    for {
      if condition {
          break
      }
      // something
    }
  • do-while循環(huán)

    for {
      // something
      if condition {
          break
      }
    }

(16) 隨機(jī)數(shù)

// 設(shè)置隨機(jī)數(shù)的種子為當(dāng)前的系統(tǒng)時(shí)間
rand.Seed(time.Now().Unix())
// 生成0-99范圍的隨機(jī)數(shù)
randomNumber := rand.Intn(100)

(17)break、continue、goto、return語句

  • break語句在多層嵌套中可以通過標(biāo)簽指明要終止到哪一層語句塊:
label:
for {
    break label
}
  • continue語句在多層嵌套中可以通過標(biāo)簽指明要跳出到到哪一層語句塊:
label:
for {
    continue label
}
  • goto語句可以無條件跳轉(zhuǎn),容易造成邏輯混亂,一般不主張使用goto語句:

    label:
    for {
    goto label
    }
  • return語句用戶退出函數(shù)
    return
    // 或
    return some

(18)函數(shù)

  • 函數(shù)基本語法:

    func functionName (paramsList) (returnList) {}
  • Golang不支持函數(shù)重載

  • Golang函數(shù)本身也是一種數(shù)據(jù)類型,可以賦值給變量,那么該變量也是函數(shù)類型

  • Golang函數(shù)可以作為實(shí)參傳入另一個(gè)函數(shù)

  • Golang支持自定義數(shù)據(jù)類型,使用:type 自定義數(shù)據(jù)類型名 數(shù)據(jù)類型

    type myfunc func(int)(int, int)
  • 支持使用_忽略返回值

  • 支持可變參數(shù)

    package main
    
    import "fmt"
    
    func main() {
    ret := sum(1, 2, 3)
    fmt.Println(ret) //6
    }
    // 可變參數(shù)
    func sum(args...int) int {
    
    sum := 0
    
    for i:=0; i<len(args); i++ {
        sum += args[i]
    }
    
    return sum
    }

(19)包

? 包的本質(zhì)就是一個(gè)目錄,Go的每一個(gè)文件都必須屬于一個(gè)包。

  • 打包:

    package packageName
  • 導(dǎo)入包:

    import "packageName"
    // 導(dǎo)入多個(gè)包
    import (
    "packageName"
      ...
    )
    // 導(dǎo)入包時(shí)自動(dòng)從GOPATH下面的src下面引入
  • 包支持別名

    package main
    
    import f "fmt"
    
    func main() {
    f.Println()
    }

(20)init函數(shù)

  • 在Go中每一個(gè)源文件都可以有一個(gè)init函數(shù),它優(yōu)先于main函數(shù)執(zhí)行,被Go框架調(diào)用。
func init() {}
  • 先執(zhí)行引入的包中的init函數(shù)再執(zhí)行main包中的init函數(shù)
// util.HelloWorld.go
package utils

import "fmt"

func init() {
    fmt.Println("Util.HelloWorld() init")
}

func HelloWorld()(){
    fmt.Println("Hello World")
}

// main.test.go
package main

import (
    "StudyGo/utils"
    "fmt"
)

func init() {
    fmt.Println("Main.main() init")
}

func main() {
    utils.HelloWorld()
}

// Util.HelloWorld() init
// Main.main() init
// Hello World

(21)匿名函數(shù)

  • 直接調(diào)用

    func (paramsList)(returnList){
        // something
    }()
  • 賦值給一個(gè)變量

    x := func (paramsList)(returnList){
        // something
    }
    y := x(paramsList)

(22)閉包

  • 閉包就是函數(shù)與其相關(guān)的引用環(huán)境構(gòu)成的實(shí)體

    package main
    
    import (
        "fmt"
        "strings"
    )
    
    func main() {
    
        fileName := "file"
        fileSuffix := ".mp3"
    
        ms := makeSuffix(fileSuffix)
        ret := ms(fileName)
    
        fmt.Println(ret)
    
    }
    
    func makeSuffix(suffix string) func(string) string {
        return func (s string) string {
            if strings.HasSuffix(s, suffix) {
                return s
            }else {
                return s + suffix
            }
        }
    }

(23)defer 關(guān)鍵字

  • defer是Go語言中的延時(shí)機(jī)制,用于處理關(guān)閉文件句柄等資源釋放操作

    package main
    
    import "fmt"
    
    func main() {
    SayHello()
    }
    
    func SayHello() {
    defer fmt.Println("Bye.")
    fmt.Println("Hi.")
    }
    
    // Hi.
    // Bye.
  • 使用defer修飾的語句會(huì)壓入棧中,其相關(guān)的值也會(huì)被壓入棧中

(24) 字符串函數(shù)

  • len():計(jì)算字符串長(zhǎng)度
  • strconv.Atoi(s string) (i int, err error):將字符串轉(zhuǎn)換為整數(shù)
  • strconv.Itoa(i int) string:將整數(shù)轉(zhuǎn)換為字符串
  • strconv.FormatInt(i int64, base int) string:將十進(jìn)制轉(zhuǎn)換為其他進(jìn)制
  • strings.Contains(s string, sub string) bool:判斷字符串是否包含子字符串
  • strings.Count(s string, sub string) int:統(tǒng)計(jì)字符串中有幾個(gè)子字符串
  • strings.EqualFold(s_0 string, s_1 string) bool:忽略大小寫比較字符串是否相等
  • strings.Index(s string, sub string) int:返回子字符串在字符串中的第一個(gè)位置
  • strings.LastIndex(s string, sub string):返回子字符串在字符串中的最后一個(gè)位置
  • string.Replace(s string, oldSub string, newSub string, n int) string:將指定的子字符串替換為其他字符串,n代表替換個(gè)數(shù),-1表示全部,返回新字符串
  • string.ToLower(s string):將字符串轉(zhuǎn)換為小寫
  • string.ToUpper(s string):將字符串轉(zhuǎn)換為大寫
  • string.Split(s string, sep string) array:將字符串按照sep分隔
  • string.TrimSpace(s string) string:刪除字符串兩側(cè)的空格
  • string.Trim(s string, sub string) string:將字符串兩側(cè)的sub去掉
  • string.TrimLeft(s string, sub string) string:將字符串左邊的sub刪除
  • string.TrimRight(s string, sub string) string:將字符串右邊的sub刪除
  • string.HasPrefix(s string, sub string) bool:判斷s是否以sub開頭
  • string.HasSuffix(s string, sub string) bool:判斷s是否以sub結(jié)尾

(25)時(shí)間日期函數(shù)

  • time.Time:表示時(shí)間類型
  • time.Now() struct:獲取當(dāng)前本地時(shí)間
  • time.Now().Year():返回年
  • time.Now().Month():返回月,使用int(time.Now().Month())取得數(shù)字
  • time.Now().Day():返回日
  • time.Now().Hour():返回時(shí)
  • time.Now().Minute():返回分
  • time.Now().Second():返回秒
  • time.Now().Format(s string):格式化時(shí)間數(shù)據(jù),2006-01-02 15:04:05表示格式化的格式字符串其中的值不能改變
  • time.Sleep(d Duration):休眠函數(shù)
    • time.Hour:一小時(shí)
    • time.Minute:一分鐘
    • time.Second:一秒
    • time.Millisecond:一毫秒
    • time.Microsecond:一微秒
    • time.Nanosecon:一納秒
  • time.Now().Unix() int64:返回Unix秒時(shí)間戳
  • time.Now().UnixNano() int64:返回Unix納秒時(shí)間戳

(26)內(nèi)置函數(shù)

  • len():求長(zhǎng)度
  • new():分配內(nèi)存,主要用來分配值類型
  • make():分配內(nèi)存,主要用來分配引用類型

(27)錯(cuò)誤處理

  • 在Go中捕獲異常的機(jī)制是使用defer關(guān)鍵字修飾匿名函數(shù),導(dǎo)致匿名函數(shù)最后執(zhí)行,在匿名函數(shù)中調(diào)用recover()函數(shù),通過返回值是否為nill來判斷是否發(fā)生異常信息。

    package main
    
    import "fmt"
    
    func main() {
    
        ret := ComputeNumber(1, 0)
        fmt.Println(ret)
    }
    
    func ComputeNumber(n_0 int, n_1 int) int {
    
        defer func() {
            if e := recover(); e != nil{
                fmt.Println(e)
            }
    
        }()
    
        result := n_0 / n_1
    
        return result
    }
  • 自定義錯(cuò)誤

    使用errors.New(Type) *Type創(chuàng)建一個(gè)error類型,panic()接收一個(gè)空接口類型,輸出錯(cuò)誤信息并結(jié)束運(yùn)行。

    package main
    
    import "errors"
    
    func main() {
    
    err := readConfigureFile("config.json")
    
    if err !=nil {
        panic(err) // panic: Read config.ini error.
    }
    
    }
    
    func readConfigureFile(path string)(err error) {
    
    if path != "config.ini" {
        return errors.New("Read config.ini error.")
    } else {
        return nil
    }
    }

    (28)數(shù)組

    ? 在Go中數(shù)據(jù)是值類型,使用以下方式創(chuàng)建數(shù)組。

    • var 數(shù)組名稱 [元素個(gè)數(shù)]數(shù)據(jù)類型 = [元素個(gè)數(shù)]數(shù)據(jù)類型{元素}
    • var 數(shù)組名稱 = [元素個(gè)數(shù)]數(shù)據(jù)類型{元素}
    • var 數(shù)組名稱 = [...]數(shù)據(jù)類型{元素個(gè)數(shù)}
    • var 數(shù)組名稱 = [...]數(shù)據(jù)類型{索引:值}
    • 數(shù)組支持類型推導(dǎo)
    • 數(shù)組支持for-each/for-range遍歷

    (29)slice 切片

    數(shù)組的長(zhǎng)度是固定的,切片 的長(zhǎng)度不是固定的。

    • var 切片名稱 []數(shù)據(jù)類型

    • 切片名稱[索引:索引]

    • 切片的結(jié)構(gòu):[起始數(shù)據(jù)元素指針, 長(zhǎng)度, 容量]

    • 通過make創(chuàng)建切片:var 切片名稱 []數(shù)據(jù)類型 = make([]數(shù)據(jù)類型, 長(zhǎng)度, 容量)

    • 切片支持普通遍歷和for-range方式遍歷

    • 使用append()函數(shù)追加元素到切片末尾,容量不夠時(shí)自動(dòng)擴(kuò)容

    • 使用copy()函數(shù)拷貝數(shù)組

    • string類型底層是個(gè)byte數(shù)組,也可以進(jìn)行切片處理。string是不可變的,如果要修改字符串,需要先將字符串轉(zhuǎn)換為切片修改完成后再轉(zhuǎn)換成為字符串。
    str := "Hello World."
    arr := []byte(str)
    arr[11] = '!'
    str = string(arr)
    fmt.Println(str)

    (28)Map映射

    • Map是一種鍵值對(duì)數(shù)據(jù)結(jié)構(gòu),聲明方式如下:

    var Map名稱 map[KeyType]ValueType

    • 使用make(map[KeyType]ValueType)分配空間

    • delete(m map[Type]Type, key Type):通過Key刪除元素,如果元素不存在也不會(huì)報(bào)錯(cuò)

    • 清空Map一種是遍歷刪除,一種是make重新分配空間,使得原來的Map成為垃圾讓GC回收

    • 查找使用value, ok = mapName[Key],如果oktrue,表示元素存在

    • Map支持for-range遍歷
    for key, value := range mapName{
    }
    • Map支持切片

(28)OOP

  • Go中的OOP是通過struct來實(shí)現(xiàn)的

    type 類名 struct {
      屬性名 數(shù)據(jù)類型
      ...
    }
  • 創(chuàng)建結(jié)構(gòu)體變量

    var 變量名稱 結(jié)構(gòu)體類型
    var 變量名稱 結(jié)構(gòu)體類型 = 結(jié)構(gòu)體類型{}
    變量名稱 := 結(jié)構(gòu)體類型{}
    
    // 下面兩種寫法等價(jià):
    var 變量名稱 *結(jié)構(gòu)體名稱 = new(結(jié)構(gòu)體名稱)
    var 變量名稱 *結(jié)構(gòu)體名稱 = &結(jié)構(gòu)體名稱
    
    // 在操作屬性、方法的時(shí)候Go進(jìn)行了優(yōu)化,下面兩種寫法是等價(jià)的:
    (*變量名稱).屬性 = 值
    變量名稱.屬性 = 值
  • 每一個(gè)字段可以加上一個(gè)tag,該tag可以通過反射機(jī)制獲取,常見的場(chǎng)景就是序列化與反序列化

    屬性名稱 數(shù)據(jù)類型 `json:Tag名稱`
  • Go中的類沒有構(gòu)造函數(shù),通常通過工廠模式來實(shí)現(xiàn)

    package model
    
    // 如果Name和Age改為name和age,需要為person綁定Getter和Setter方法
    type person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    }
    
    func NewPerson(n string, a int)(*person){
    return &person{
        Name : n,
        Age : a,
    }
    }
    package main
    
    import "fmt"
    import "StudyGo/model"
    
    func main() {
    var tom = model.NewPerson("Tom", 20)
    fmt.Println((*tom).Name)
    fmt.Println((*tom).Age)
    }
  • 在Go中在一個(gè)結(jié)構(gòu)體中嵌套另一個(gè)匿名結(jié)構(gòu)體就認(rèn)為實(shí)現(xiàn)了繼承

    type Ani struct {
      name string
      age int
    }
    
    type Cat struct {
      Ani
      say string
    }
    • Go中的結(jié)構(gòu)體可以訪問嵌套結(jié)構(gòu)體中的所有字段和方法
    • 結(jié)構(gòu)體的字段和屬性采用就近原則
    • 如果一個(gè)結(jié)構(gòu)體繼承自兩個(gè)結(jié)構(gòu)體,這兩個(gè)結(jié)構(gòu)體都有同名字段但是該子結(jié)構(gòu)體沒有,訪問時(shí)需要指明父結(jié)構(gòu)體
    • struct支持匿名字段,但是數(shù)據(jù)類型不能重復(fù),使用結(jié)構(gòu)體變量.數(shù)據(jù)類型 = 值來訪問
  • 接口

    type 接口名稱 interface {
      方法名稱(參數(shù)列表)(返回值列表)
    }
    • 接口不允許包含任何變量
    • Go中的接口不需要顯式實(shí)現(xiàn),只要一個(gè)變量含有接口的所有方法,那么就實(shí)現(xiàn)了這個(gè)接口
  • 類型斷言

    • 當(dāng)一個(gè)類型轉(zhuǎn)換為了接口類型在轉(zhuǎn)換為該類型時(shí)需要使用類型斷言判斷是否可以轉(zhuǎn)換為該類型
    var number float32
    var x interface{}
    x = t
    t = x.(float32) // 判斷一下是否可以轉(zhuǎn)換成為float32類型

(29)方法

func (recv type) funcName (paramsList)(returnList) {
    // something
}
  • recv 表示這個(gè)方法與type類進(jìn)行綁定,方法內(nèi)通過recv操作type類中的字段
  • type是個(gè)結(jié)構(gòu)體類型
  • recv是個(gè)結(jié)構(gòu)體類型變量

通常為了執(zhí)行效率一般不會(huì)直接傳入結(jié)構(gòu)體類型作為接收器,而是結(jié)構(gòu)體類型指針:

func (dog *Dog) function()(){ // 綁定的是地址,操作時(shí)也要使用地址
    // something
}
// 調(diào)用時(shí)
var d Dog
(&d).function()

但是編譯器做出了相關(guān)的優(yōu)化:

var d Dog
d.function()

(30)文件

  • File.Open(name string) (file *File, err error):打開文件
  • File.Close() error:關(guān)閉文件
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("main/file.txt")
    if err != nil {
        fmt.Println(err)
    }
    err = file.Close()
    if err != nil {
        fmt.Println(err)
    }
}
  • 使用bufio.NewReader(file *File) *reader創(chuàng)建讀取器對(duì)象,調(diào)用bufio.ReadString(end string) (s string, err error)讀取文件,以end結(jié)尾。

  • 使用ioutil.ReadFile(path string) ([]byte, error):返回nil,一次性讀取全部文件,不需要手動(dòng)打開和關(guān)閉。

  • 使用os.OpenFile(name string, flag int, perm FileMode)(file *File, err error)

    name:文件完整路徑
    flag:打開模式
    perm:權(quán)限(類Unix生效)
    // 打開模式:可以使用"|"進(jìn)行組合
    const (
      O_RDONLY int = syscall.O_RDONLY // 只讀打開
      O_WRONLY int = syscall.O_WRONLY // 只寫打開
      O_RDWR   int = syscall.O_RDWR   // 讀寫打開
      O_APPEND int = syscall.O_APPEND // 追加打開
      O_CREATE int = syscall.O_CREAT  // 如果不存在就創(chuàng)建
      O_EXCL   int = syscall.O_EXCL   // 創(chuàng)建打開,文件必須不存在
      O_SYNC   int = syscall.O_SYNC   // 打開文件用于同步IO
      O_TRUNC  int = syscall.O_TRUNC  // 如果可能,打開文件是清空文件
    )
    // 權(quán)限:
    r,w,x
  • 使用bufio.NewWriter(file *File) *writer來創(chuàng)建寫入器,使用bufio.Flush()將緩存同步到文件,使用bufio.WriterString(str string)來寫入文件。

  • 使用io.Copy(dst Writer, src Reader)(written int64, err error)來實(shí)現(xiàn)文件拷貝

(31)命令行參數(shù)處理

os.Args []string保管了所有命令行參數(shù),第一個(gè)參數(shù)是程序名稱。

flag包可以實(shí)現(xiàn)更加高級(jí)的命令行參數(shù)處理:

var username string
// 綁定參數(shù)
flag.StringVar(&username, "u", "root", "Username")
//  -- 保存參數(shù)字符串的地址
//  -- 參數(shù)名稱
//  -- 默認(rèn)值
//  -- 參數(shù)釋義
// 解析參數(shù)
flag.Parse() 

(32)Json文件處理

結(jié)構(gòu)體、切片、Map等都可以解析為Json字符串,使用encoding/json.Marshal(i interface{},)([]byte, error)來實(shí)現(xiàn)各種類型到Json數(shù)據(jù);使用encoding/json.Unmarshal(Json字符串, 實(shí)例對(duì)象的引用)反序列化。

(33)單元測(cè)試

Go語言自帶輕量級(jí)的測(cè)試框架和go test -v命令來實(shí)現(xiàn)單元測(cè)試和性能測(cè)試。Go的測(cè)試指令會(huì)自動(dòng)識(shí)別以TestXxx命名的函數(shù):

import "testing"
func TestXxx(t *testing.T){
    t.Fatalf(string) // 停止測(cè)試并記錄日志
}

(34)goroutine

Go主線程可以理解為線程也可以理解為進(jìn)程,一個(gè)Go線程可以包含多個(gè)協(xié)程(微程),Go程具備以下幾點(diǎn)特質(zhì):

  • 有獨(dú)立的??臻g

  • 共享程序堆空間

  • 調(diào)度由用戶控制

  • 輕量級(jí)的線程

主線程是一個(gè)重量級(jí)物理線程,直接作用在CPU上,非常消耗資源,協(xié)程從主線程開啟,是邏輯態(tài)的輕量級(jí)線程,相對(duì)資源消耗少。在Go中可以輕松開啟成千上萬個(gè)協(xié)程,其他語言的并發(fā)機(jī)制一般是線程實(shí)現(xiàn),這就是Go的優(yōu)勢(shì)。使用go關(guān)鍵字修飾一個(gè)函數(shù)等即可開啟一個(gè)Go程。Go可以充分發(fā)揮多核多CPU的優(yōu)勢(shì),使用runtime.NumCpu()可以獲取當(dāng)前機(jī)器的CPU個(gè)數(shù),使用runtime.GOMAXPROCS(n int)設(shè)置可用的CPU數(shù)量。在Go1.8之前需要手動(dòng)設(shè)置,Go1.8以后默認(rèn)使用多CPU。

(35)管道

不同的Go協(xié)程如何實(shí)現(xiàn)通信,下面給出兩種方法:

  • 全局變量加鎖
  • 管道

在Go中,sync包提供了基本的同步單元,大部分適用于低水平的程序線程,高水平的同步一般使用管道解決。

  • 使用全局變量加鎖

    使用sync.Lock 聲明一個(gè)全局變量:

    var lock sync.Mutex

    使用lock.Lock()加鎖,使用lock.Unlock()解鎖。

  • 使用管道

    管道的本質(zhì)就是隊(duì)列,使用var 管道名稱 chan 數(shù)據(jù)類型,channel必須是引用類型,管道使用make聲明空間以后才可以使用,管道是有數(shù)據(jù)類型區(qū)分的,如果要存儲(chǔ)任意數(shù)據(jù)類型需要聲明為interface{}類型。

    管道使用&lt;-運(yùn)算符存取數(shù)據(jù):

    var MyChannel chan int
    MyChannel = make(chan int, 8)
    MyChannel <- 8 // 存入數(shù)據(jù)
    number := <- MyChannel // 取出數(shù)據(jù)
    close(MyChannel) // 關(guān)閉管道,但是可以讀取數(shù)據(jù)

    管道容量用完以后不能再存儲(chǔ)數(shù)據(jù);在沒有協(xié)程使用的情況下,如果管道的數(shù)據(jù)用完就會(huì)產(chǎn)生dead lock錯(cuò)誤。

    默認(rèn)情況下管道是雙向的,可讀可寫,聲明只讀/寫管道:

    var chan_0 = chan<- int  //  只讀
    var chan_1 = <-chan int  //  只寫

    在使用管道讀取數(shù)據(jù)的時(shí)候沒有關(guān)閉可能會(huì)發(fā)生阻塞,如果沒有數(shù)據(jù)會(huì)發(fā)生死鎖現(xiàn)象,因此可以使用select關(guān)鍵字來解決。

    for {
      select {
        case v <- chan_0 :
            // something
          default:
            // something
      }
    }

    如果有一個(gè)協(xié)程出現(xiàn)panic,將會(huì)導(dǎo)致整個(gè)程序崩潰,因此需要異常處理機(jī)制來維護(hù)。

(36)反射

通過reflect.TypeOf()獲取變量類型,返回reflect.Type類型

通過reflect.ValueOf()獲取變量的值,返回reflect.Value類型

(37)網(wǎng)絡(luò)編程

listen, err := net.Listen("tcp", "0.0.0.0:8888")
if err != nil{
    return
}
defer listen.Close()

for {
    connect, err := listen.Accept()
    if err == nil {
        go func (connect net,Conn)(){
            defer connect.Close()
            buffer := make([]byte, 1024)
            num, err := connect.Read(buffer)
            if err != nil {
                return
            }
        }()
    }
}
connect, err := Dial("tcp", "127.0.0.1:8888")
if err != nil {
    return
}
num, err := connect.Write([]byte("Hello"))
connect.Close()
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI