Golang聖經----讀書筆記

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

前言

Go語言起源


Golang發展曆程

Go語言項目

  • "軟體的複雜性是乘法級相關的-----Rob Pike"
  • 簡潔的設計需要在工作開始的時候捨棄不必要的想法,並且在軟體的生命週期內嚴格區別好的改變和壞的改變。通過足夠的努力,一個好的改變可以在不破壞原有完整概念的前提下保持自適應,正如Fred Brooks所說的“概念完整性”;而一個壞的改變則不能達到這個效果,它們僅僅是通過膚淺的和簡單的妥協來破壞原有設計的一致性。只有通過簡潔的設計,才能讓一個系統保持穩定、安全和持續的進化。

本書的組織

基礎

  • 第一章包含了本教程的基本結構,通過十幾個程式介紹了用Go語言如何?類似讀寫檔案、文字格式設定化、建立映像、網路用戶端和伺服器通訊等日常工作。
  • 第二章描述了Go語言程式的基本元素結構、變數、新類型定義、包和檔案、以及範圍等概念。
  • 第三章討論了數字、布爾值、字串和常量,並示範了如何顯示和處理Unicode字元。
  • 第四章描述了複合類型,從簡單的數組、字典、切片到動態列表。
  • 第五章涵蓋了函數,並討論了錯誤處理、panic和recover,還有defer語句。

第一章到第五章是基礎部分,主流命令式程式設計語言這部分都類似。個別之處,Go語言有自己特色的文法和風格,但是大多數程式員能很快適應。其餘章節是Go語言特有的:方法、介面、並發、包、測試和反射等語言特性。

進階

  • 第八章討論了基於順序通訊進程(CSP)概念的並發編程,使用goroutines和channels處理並發編程。
  • 第九章則討論了傳統的基於共用變數的並發編程。
  • 第十章描述了包機制和包的組織圖。這一章還展示了如何有效地利用Go內建的工具,使用單個命令完成編譯、測試、基準測試、代碼格式化、文檔以及其他諸多任務。
  • 第十一章討論了單元測試,Go語言的工具和標準庫中整合了輕量級的測試功能,避免了強大但複雜的測試架構。測試庫提供了一些基本構件,必要時可以用來構建複雜的測試構件。
  • 第十二章討論了反射,一種程式在運行期間審視自己的能力。反射是一個強大的編程工具,不過要謹慎地使用;這一章利用反射機制實現一些重要的Go語言庫函數, 展示了反射的強大用法。第十三章解釋了底層編程的細節,在必要時,可以使用unsafe包繞過Go語言安全的類型系統。

入門

Hello, World

嘗試用100中方法列印出Hello, World
哈哈!

package mainfunc main() {    print("Hello, 世界") //Go語言原生支援Unicode,它可以處理全世界任何語言的文本。}

命令列參數

os包以跨平台的方式,提供了一些與作業系統互動的函數和變數。程式的命令列參數可從os包的Args變數擷取;os包外部使用os.Args訪問該變數。

尋找重複的行

  • bufio包,它使處理輸入和輸出方便又高效。Scanner類型是該包最有用的特性之一,它讀取輸入並將其拆成行或單詞;通常是處理行形式的輸入最簡單的方法。
  • 格式化verb
    %d          十進位整數%x, %o, %b  十六進位,八進位,二進位整數。%f, %g, %e  浮點數: 3.141593 3.141592653589793 3.141593e+00%t          布爾:true或false%c          字元(rune) (Unicode碼點)%s          字串%q          帶雙引號的字串"abc"或帶單引號的字元'c'%v          變數的自然形式(natural format)%T          變數的類型%%          字面上的百分比符號標誌(無運算元)
    ioutil.ReadFile(filename)函數返回一個位元組切片(byte slice)

GIF動畫

  • 產生的圖形名字叫利薩形(Lissajous figures)

擷取URL

http.Get(url)
ioutil.ReadAll(resp.Body)

並發擷取多個URL

goroutine和channel

Web服務

package mainimport (    "fmt"    "log"    "net/http")func main() {    http.HandleFunc("/", handler) // each request calls handler    log.Fatal(http.ListenAndServe("localhost:8000", nil))}// handler echoes the Path component of the requested URL.func handler(w http.ResponseWriter, r *http.Request) {    fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)}
  • 如果你的請求pattern是以/結尾,那麼所有以該url為首碼的url都會被這條規則匹配。

要點

  • 控制流程
      switch coinflip() {  case "heads":      heads++  case "tails":      tails++  default:      fmt.Println("landed on edge!")
    Go語言並不需要顯式地在每一個case後寫break
    如果你想要相鄰的幾個case都執行同一邏輯的話,需要自己顯式地寫上一個fallthrough語句來覆蓋這種預設行為。
  • 命名類型
  • 指標
    指標是一種直接儲存了變數的記憶體位址的資料類型。指標是可見的記憶體位址,&操作符可以返回一個變數的記憶體位址,並且*操作符可以擷取指標指向的變數內容,但是在Go語言裡沒有指標運算,也就是不能像c語言裡可以對指標進行加或減操作。
  • 方法和介面
    Go語言裡的方法可以被關聯到任意一種命名類型。
  • 注釋

程式結構

命名

  • 25個關鍵字
    break default func interface selectcase defer go map structchan else goto package switchconst fallthrough if range typecontinue for import return var
  • 30多個預定義的名字
    內建常量: true false iota nil內建類型: int int8 int16 int32 int64uint uint8 uint16 uint32 uint64 uintptrfloat32 float64 complex128 complex64bool byte rune string error內建函數: make len cap new append copy close deletecomplex real imagpanic recover
  • Go語言程式員推薦使用 駝峰式 命名

聲明

變數

  • 簡短變數聲明
    i := 100
  • 指標
    x := 1p := &x // p, of type *int, points to xfmt.Println(*p) // "1"*p = 2 // equivalent to x = 2fmt.Println(x) // "2"
  • new函數
    p := new(int) // p, *int 類型, 指向匿名的 int 變數
  • 變數的生命週期
    Go語言的自動垃圾收集器對編寫正確的代碼是一個巨大的協助,但也並不是說你完全不用考慮記憶體了。你雖然不需要顯式地分配和釋放記憶體,但是要編寫高效的程式你依然需要瞭解變數的生命週期。例如,如果將指向短生命週期對象的指標儲存到具有長生命週期的對象中,特別是儲存到全域變數時,會阻止對短生命週期對象的記憶體回收(從而可能影響程式的效能)。

賦值

  • 元組賦值
    x, y = y, xa[i], a[j] = a[j], a[i]
  • 可賦值性
    可賦值性的規則對於不同類型有著不同要求,對每個新類型特殊的地方我們會專門解釋。對於目前我們已經討論過的類型,它的規則是簡單的:類型必須完全符合,nil可以賦值給任何指標或參考型別的變數。常量(§3.6)則有更靈活的賦值規則,因為這樣可以避免不必要的顯式的類型轉換。

類型

包和檔案

  • 匯入包
    "fmt". "fmt" //省略包名_ "fmt" //只匯入
  • 包的初始化
    func init() { /* ... */ }

範圍

  • 不要將範圍和生命週期混為一談。聲明語句的範圍對應的是一個原始碼的文本地區;它是一個編譯時間的屬性。一個變數的生命週期是指程式運行時變數存在的有效時間段,在此時間地區內它可以被程式的其他部分引用;是一個運行時的概念。
  • 控制流程標號,就是break、continue或goto語句後面跟著的那種標號,則是函數級的範圍。

基礎資料類型

整型

8、16、32、64bit

  • 運算子
    *(乘)      /(除)      %(餘)      <<(左移)       >>(右移)     &(位元運算 AND)       &^(位清空 (AND NOT))+(加)      -(減)      | (位元運算 OR)     ^(位元運算 XOR)==(等於)     != (不等於)    < (小於)     <=(小於或等於)       > (大於)     >=(大於或等於)&&(AND)||(OR)
    二元運算子有五種優先順序。在同一個優先順序,使用左優先結合規則,但是使用括弧可以明確優先順序,使用括弧也可以用於提升優先順序

浮點數

float32和float64


好漂亮
// Copyright  2016 Alan A. A. Donovan & Brian W. Kernighan.// License: https://creativecommons.org/licenses/by-nc-sa/4.0/// See page 58.//!+// Surface computes an SVG rendering of a 3-D surface function.package mainimport (    "fmt"    "math")const (    width, height = 600, 320            // canvas size in pixels    cells         = 100                 // number of grid cells    xyrange       = 30.0                // axis ranges (-xyrange..+xyrange)    xyscale       = width / 2 / xyrange // pixels per x or y unit    zscale        = height * 0.4        // pixels per z unit    angle         = math.Pi / 6         // angle of x, y axes (=30°))var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)func main() {    fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+        "style='stroke: grey; fill: white; stroke-width: 0.7' "+        "width='%d' height='%d'>", width, height)    for i := 0; i < cells; i++ {        for j := 0; j < cells; j++ {            ax, ay := corner(i+1, j)            bx, by := corner(i, j)            cx, cy := corner(i, j+1)            dx, dy := corner(i+1, j+1)            fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",                ax, ay, bx, by, cx, cy, dx, dy)        }    }    fmt.Println("</svg>")}func corner(i, j int) (float64, float64) {    // Find point (x,y) at corner of cell (i,j).    x := xyrange * (float64(i)/cells - 0.5)    y := xyrange * (float64(j)/cells - 0.5)    // Compute surface height z.    z := f(x, y)    // Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).    sx := width/2 + (x-y)*cos30*xyscale    sy := height/2 + (x+y)*sin30*xyscale - z*zscale    return sx, sy}func f(x, y float64) float64 {    r := math.Hypot(x, y) // distance from (0,0)    return math.Sin(r) / r}//!-

複數

Go語言提供了兩種精度的複數類型:complex64和complex128,分別對應float32和float64兩種浮點數精度。內建的complex函數用於構建複數,內建的real和imag函數分別返回複數的實部和虛部:

布爾型

字串

\a      響鈴\b      退格\f      換頁\n      換行\r      斷行符號\t      定位字元\v      垂直定位字元\'      單引號 (只用在 '\'' 形式的rune符號面值中)\"      雙引號 (只用在 "..." 形式的字串面值中)\\      反斜線
  • 得益於UTF8編碼優良的設計,諸多字串操作都不需要解碼操作。我們可以不用解碼直接測試一個字串是否是另一個字串的首碼:
    func HasPrefix(s, prefix string) bool {    return len(s) >= len(prefix) && s[:len(prefix)] == prefix}
    或者是尾碼測試:
    func HasSuffix(s, suffix string) bool {     return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix}
    或者是包含子串測試:
    func Contains(s, substr string) bool {  for i := 0; i < len(s); i++ {      if HasPrefix(s[i:], substr) {          return true      }  }  return false}
package mainimport "fmt"func main() {    s := "Hello, 世界"    fmt.Printf("%s\t%d\n", s, len(s))    for i := 0; i < len(s); i++ {        fmt.Printf("%d\t%v\t%q\n", i, s[i], s[i])    }    fmt.Println("...")    for i, r := range s {        fmt.Printf("%d\t%d\t%q\n", i, r, r)    }}

結果

Hello, 世界    130    72    'H'1    101    'e'2    108    'l'3    108    'l'4    111    'o'5    44    ','6    32    ' '7    228    'ä'8    184    '¸'9    150    '\u0096'10    231    'ç'11    149    '\u0095'12    140    '\u008c'...0    72    'H'1    101    'e'2    108    'l'3    108    'l'4    111    'o'5    44    ','6    32    ' '7    19990    '世'10    30028    '界'
  • 字串和Byte切片
    標準庫中有四個包對字串處理尤為重要:bytes、strings、strconv和unicode包。
    • strings包提供了許多如字串的查詢、替換、比較、截斷、拆分和合并等功能。
    • bytes包也提供了很多類似功能的函數,但是針對和字串有著相同結構的[]byte類型。因為字串是唯讀,因此逐步構建字串會導致很多分配和複製。在這種情況下,使用bytes.Buffer類型將會更有效,稍後我們將展示。
    • strconv包提供了布爾型、整型數、浮點數和對應字串的相互轉換,還提供了雙引號轉義相關的轉換。
    • unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等類似功能,它們用於給字元分類。每個函數有一個單一的rune類型的參數,然後返回一個布爾值。而像ToUpper和ToLower之類的轉換函式將用於rune字元的大小寫轉換。所有的這些函數都是遵循Unicode標準定義的字母、數字等分類規範。strings包也有類似的函數,它們是ToUpper和ToLower,將原始字串的每個字元都做相應的轉換,然後返回新的字串。
  • 字串和數位轉換

常量

  • iota 常量產生器
    const ( _   = 1 << (10 * iota) KiB // 1024 MiB // 1048576 GiB // 1073741824 TiB // 1099511627776             (exceeds 1 << 32) PiB // 1125899906842624 EiB // 1152921504606846976 ZiB // 1180591620717411303424    (exceeds 1 << 64) YiB // 1208925819614629174706176)

複合資料型別

數組

Slice


  • Slice記憶體技巧

Map

在Go語言中,一個map就是一個雜湊表的引用,map類型可以寫為map[K]V,其中K和V分別對應key和value。
內建的make函數可以建立一個map:

ages := make(map[string]int) // mapping from strings to ints

我們也可以用map字面值的文法建立map,同時還可以指定一些最初的key/value:

ages := map[string]int{ "alice": 31, "charlie": 34,}

使用內建的delete函數可以刪除元素:

delete(ages, "alice") // remove element ages["alice"]

結構體

  • 結構體比較
    如果結構體的全部成員都是可以比較的,那麼結構體也是可以比較的

  • 結構體嵌入和匿名成員

    • 需要注意的是Printf函數中%v參數包含的#副詞,它表示用和Go語言類似的文法列印值。對於結構體類型來說,將包含每個成員的名字。
      fmt.Printf("%#v\n", w)

JSON

json.Marshaljson.MarshalIndent

  • json處理struct未匯出成員
    golang的結構體裡的成員的名字如果以小寫字母開頭,那麼其他的包是無法訪問的,也就是json無法訪問我們的結構體裡小寫字母開頭的成員。兩種解決方案
    1. struct的成員用大寫開頭,然後加tag
package mainimport (    "encoding/json"    "fmt"    "log")type Movie struct {    Title  string    Year   int  `json:"released"`    Color  bool `json:"color,omitempty"`    Actors []string}func main() {    var movies = []Movie{        {Title: "Casablanca", Year: 1942, Color: false,            Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},        {Title: "Cool Hand Luke", Year: 1967, Color: true,            Actors: []string{"Paul Newman"}},        {Title: "Bullitt", Year: 1968, Color: true,            Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},        // ...    }    data, err := json.Marshal(movies) // json.MarshalIndent(struct, "", "    ")    if err != nil {        log.Fatalf("JSON marshaling failed: %s", err)    }    fmt.Printf("%s\n", data)}
[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingrid Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Actors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"Actors":["Steve McQueen","Jacqueline Bisset"]}]
  1. 實現json.Marshaler介面
    ```
    package main

import (
"encoding/json"
"fmt"
)

func main() {
var s S
s.a = 5
s.b[0] = 3.123
s.b[1] = 111.11
s.b[2] = 1234.123
s.c = "hello"
s.d[0] = 0x55

j, _ := json.Marshal(s)fmt.Println(string(j))

}

type S struct {
a int
b [4]float32
c string
d [12]byte
}

func (this S) MarshalJSON() ([]byte, error) {
return json.MarshalIndent(map[string]interface{}{ // json.MarshalIndent(struct, "", " ")
"a": this.a,
"b": this.b,
"c": this.c,
"d": this.d,
}, "", " ")
}

```{"a":5,"b":[3.123,111.11,1234.123,0],"c":"hello","d":[85,0,0,0,0,0,0,0,0,0,0,0]}

文本和HTML模板

函數

函式宣告

遞迴

多傳回值

錯誤

函數值

匿名函數

可變參數

Deferred函數

Panic異常

  • 一般而言,當panic異常發生時,程式會中斷運行,並立即執行在該goroutine(可以先理解成線程,在第8章會詳細介紹)中被延遲的函數(defer 機制)

Recover捕獲異常

方法

方法聲明

基於指標對象的方法

通過嵌入結構體來擴充類型

方法值和方法運算式

Bit數組

封裝

介面

當設計一個新的包時,新的Go程式員總是通過建立一個介面的集合開始和後面定義滿足它們的具體類型。這種方式的結果就是有很多的介面,它們中的每一個僅只有一個實現。不要再這麼做了。這種介面是不必要的抽象;它們也有一個運行時損耗。你可以使用匯出機制(§6.6)來限制一個類型的方法或一個結構體的欄位是否在包外可見。介面只有當有兩個或兩個以上的具體類型必須以相同的方式進行處理時才需要。
當一個介面只被一個單一的具體類型實現時有一個例外,就是由於它的依賴,這個具體類型不能和這個介面存在在一個相同的包中。這種情況下,一個介面是解耦這兩個包的一個好好方式。
因為在Go語言中只有當兩個或更多的類型實現一個介面時才使用介面,它們必定會從任意特定的實現細節中抽象出來。結果就是有更少和更簡單方法(經常和io.Writer或 fmt.Stringer一樣只有一個)的更小的介面。當新的類型出現時,小的介面更容易滿足。對於介面設計的一個好的標準就是 ask only for what you need(只考慮你需要的東西)

Goroutines和Channels

基於共用變數的並發

包和工具

工具

測試

go test

反射

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.