1. 程式人生 > >寫給大忙人看的Go語言(一)

寫給大忙人看的Go語言(一)

保留 fff 代碼規範 ebo utf 返回值 sha new do-while

Tips

寫給大忙人看的Golang教程(一)
閱讀本文之前,我認為你已經掌握其他語言基礎並寫出一個簡單的項目。

(1)Golang編程註意事項

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

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

  • \t 制表符
  • \n 換行符
  • \\ 一個斜杠
  • \" 一個引號
  • \r 一個回車

(3)註釋方式

  • // 註釋內容行註釋
  • /* 註釋內容 */多行註釋
  • 多行註釋不可以嵌套多行註釋

(4)Golang的代碼規範

  • 盡量使用行註釋註釋整個方法或語句
  • 使用Tab縮進
  • 使用gofmt -w格式化代碼
  • 運算符兩側加空格
  • Golang的代碼風格:

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

(5)官方編程指南

  • Go的官方網站 https://golang.org
  • Go的標準庫英文官方文檔 https://golang.org/pkgdoc
  • Go的標準庫中文官方文檔 https://studygolang.com/pkgdoc

(6)變量

  • 使用var關鍵字定義變量:var 變量名稱 數據類型
  • 使用類型推導:var 變量名稱 = 值
  • 省略var關鍵字:變量名稱 := 值, 變量名稱不應該是已經定義過的
  • 變量名稱 := 值等同於var 變量名稱 數據類型 = 值
  • 多變量聲明:var 變量名稱, 變量名稱... 數據類型
  • 多變量賦值:var 變量名稱, 變量名稱, ... = 值, 值, ...
  • 省略var關鍵字多變量聲明和賦值: 變量名稱, 變量名稱, ... := 值, 值, ...
  • 聲明全局變量:
    var (
        變量名稱 = 值
        ...
    )

(7)Golang支持的數據類型

技術分享圖片

  • 使用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表示雙精度

  • 字符常量使用單引號

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

  • bool類型只能取truefalse

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

  • Go支持使用反引號輸出原生字符串

  • 字符串拼接使用加號時最後一個加號需要保留在行尾

(8)基本數據類型轉換

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

(9)指針數據類型

package main

import "fmt"

func main() {

    var i = 10
    var ptr *int = &i

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

(10) 值類型與引用類型

技術分享圖片

技術分享圖片

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

(11)Golang中的標識符

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

(12)運算符

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

(13)控制臺輸入輸出

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

(14) 原碼、反碼、補碼

  • 計算機中0表示正數,1表示負數
  • 計算機中都是用補碼進行運算的,因為CPU只會加法運算
  • 正數的原、反、補都是相同的
  • 負數的反碼是原碼符號位不變其他位取反
  • 負數的補碼是反碼加1
  • 0的原、反、補相同

(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語句:

    // 每個分支不需要break語句
    // switch也支持在condition中直接定義變量
    // case支持多個表達式
    // 取消break使用fallthrough語句————switch穿透
    switch y:=10;y {
    case 5:
        // something
    case 10:
        // something
        fallthrough
    case 20, 25, 30:
        // something
    default:
        // something
    }

(15.3)循環控制

  • for循環

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

    for {
      if condition {
          break
      }
      // something
    }
  • do-while循環

    for {
      // something
      if condition {
          break
      }
    }

(16) 隨機數

// 設置隨機數的種子為當前的系統時間
rand.Seed(time.Now().Unix())
// 生成0-99範圍的隨機數
randomNumber := rand.Intn(100)

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

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

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

(18)函數

  • 函數基本語法:

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

  • Golang函數本身也是一種數據類型,可以賦值給變量,那麽該變量也是函數類型

  • Golang函數可以作為實參傳入另一個函數

  • Golang支持自定義數據類型,使用:type 自定義數據類型名 數據類型

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

  • 支持可變參數

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

(19)包

? 包的本質就是一個目錄,Go的每一個文件都必須屬於一個包。

  • 打包:

    package packageName
  • 導入包:

    import "packageName"
    // 導入多個包
    import (
    "packageName"
      ...
    )
    // 導入包時自動從GOPATH下面的src下面引入
  • 包支持別名

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

(20)init函數

  • 在Go中每一個源文件都可以有一個init函數,它優先於main函數執行,被Go框架調用。
func init() {}
  • 先執行引入的包中的init函數再執行main包中的init函數
// 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)匿名函數

  • 直接調用

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

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

(22)閉包

  • 閉包就是函數與其相關的引用環境構成的實體

    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 關鍵字

  • defer是Go語言中的延時機制,用於處理關閉文件句柄等資源釋放操作

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

(24) 字符串函數

  • len():計算字符串長度
  • strconv.Atoi(s string) (i int, err error):將字符串轉換為整數
  • strconv.Itoa(i int) string:將整數轉換為字符串
  • strconv.FormatInt(i int64, base int) string:將十進制轉換為其他進制
  • strings.Contains(s string, sub string) bool:判斷字符串是否包含子字符串
  • strings.Count(s string, sub string) int:統計字符串中有幾個子字符串
  • strings.EqualFold(s_0 string, s_1 string) bool:忽略大小寫比較字符串是否相等
  • strings.Index(s string, sub string) int:返回子字符串在字符串中的第一個位置
  • strings.LastIndex(s string, sub string):返回子字符串在字符串中的最後一個位置
  • string.Replace(s string, oldSub string, newSub string, n int) string:將指定的子字符串替換為其他字符串,n代表替換個數,-1表示全部,返回新字符串
  • string.ToLower(s string):將字符串轉換為小寫
  • string.ToUpper(s string):將字符串轉換為大寫
  • string.Split(s string, sep string) array:將字符串按照sep分隔
  • string.TrimSpace(s string) string:刪除字符串兩側的空格
  • string.Trim(s string, sub string) string:將字符串兩側的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結尾

(25)時間日期函數

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

(26)內置函數

  • len():求長度
  • new():分配內存,主要用來分配值類型
  • make():分配內存,主要用來分配引用類型

(27)錯誤處理

  • 在Go中捕獲異常的機制是使用defer關鍵字修飾匿名函數,導致匿名函數最後執行,在匿名函數中調用recover()函數,通過返回值是否為nill來判斷是否發生異常信息。

    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
    }
  • 自定義錯誤

    使用errors.New(Type) *Type創建一個error類型,panic()接收一個空接口類型,輸出錯誤信息並結束運行。

    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)數組

    ? 在Go中數據是值類型,使用以下方式創建數組。

    • var 數組名稱 [元素個數]數據類型 = [元素個數]數據類型{元素}
    • var 數組名稱 = [元素個數]數據類型{元素}
    • var 數組名稱 = [...]數據類型{元素個數}
    • var 數組名稱 = [...]數據類型{索引:值}
    • 數組支持類型推導
    • 數組支持for-each/for-range遍歷

    (29)slice 切片

    數組的長度是固定的,切片 的長度不是固定的。

    • var 切片名稱 []數據類型

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

    • 切片的結構:[起始數據元素指針, 長度, 容量]

    • 通過make創建切片:var 切片名稱 []數據類型 = make([]數據類型, 長度, 容量)

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

    • 使用append()函數追加元素到切片末尾,容量不夠時自動擴容

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

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

    (28)Map映射

    • Map是一種鍵值對數據結構,聲明方式如下:

    var Map名稱 map[KeyType]ValueType

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

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

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

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

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

(28)OOP

  • Go中的OOP是通過struct來實現的

    type 類名 struct {
      屬性名 數據類型
      ...
    }
  • 創建結構體變量

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

    屬性名稱 數據類型 `json:Tag名稱`
  • Go中的類沒有構造函數,通常通過工廠模式來實現

    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中在一個結構體中嵌套另一個匿名結構體就認為實現了繼承

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

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

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

(29)方法

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

通常為了執行效率一般不會直接傳入結構體類型作為接收器,而是結構體類型指針:

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

但是編譯器做出了相關的優化:

var d Dog
d.function()

寫給大忙人看的Go語言(一)