## 1.8. 本章要點
本章中對Go語言做了一些介紹,實際上Go語言還有很多方面在這有限的篇幅中還沒有覆蓋到。這里我們會把沒有講到的內容也做一些簡單的介紹,這樣讀者在之後看到完整的內容之前,也可以有個簡單印象。
**控製流:** 在本章我們隻介紹了if控製和for,但是沒有提到switch多路選擇。這里是一個簡單的switch的例子:
```go
switch coinflip() {
case "heads":
heads++
case "tails":
tails++
default:
fmt.Println("landed on edge!")
}
```
在翻轉硬幣的時候,例子里的coinflip函數返迴幾種不同的結果,每一個case都會對應個返迴結果,這里需要註意,Go語言併不需要顯式地去在每一個case後寫break,語言默認執行完case後的邏輯語句會自動退出。當然了,如果你想要相鄰的幾個case都執行同一邏輯的話,需要自己顯式地寫上一個fallthrough語句來覆蓋這種默認行爲。不過fallthrough語句在一般的編程中用到得很少。
Go語言里的switch還可以不帶操作對象(譯註:switch不帶操作對象時默認用true值代替,然後將每個case的表達式和true值進行比較);可以直接羅列多種條件,像其它語言里面的多個if else一樣,下面是一個例子:
```go
func Signum(x int) int {
switch {
case x > 0:
return +1
default:
return 0
case x < 0:
return -1
}
}
```
這種形式叫做無tag switch(tagless switch);這和switch true是等價的。
像for和if控製語句一樣,switch也可以緊跟一個簡短的變量聲明,一個自增表達式、賦值語句,或者一個函數調用。
break和continue語句會改變控製流。和其它語言中的break和continue一樣,break會中斷當前的循環,併開始執行循環之後的內容,而continue會中跳過當前循環,併開始執行下一次循環。這兩個語句除了可以控製for循環,還可以用來控製switch和select語句(之後會講到),在1.3節中我們看到,continue會跳過是內層的循環,如果我們想跳過的是更外層的循環的話,我們可以在相應的位置加上label,這樣break和continue就可以根據我們的想法來continue和break任意循環。這看起來甚至有點像goto語句的作用了。當然,一般程序員也不會用到這種操作。這兩種行爲更多地被用到機器生成的代碼中。
**命名類型:** 類型聲明使得我們可以很方便地給一個特殊類型一個名字。因爲struct類型聲明通常非常地長,所以我們總要給這種struct取一個名字。本章中就有這樣一個例子,二維點類型:
```go
type Point struct {
X, Y int
}
var p Point
```
類型聲明和命名類型會在第二章中介紹。
**指針:** Go語言提供了指針。指針是一種直接存儲了變量的內存地址的數據類型。在其它語言中,比如C語言,指針操作是完全不受約束的。在另外一些語言中,指針一般被處理爲“引用”,除了到處傳遞這些指針之外,併不能對這些指針做太多事情。Go語言在這兩種范圍中取了一種平衡。指針是可見的內存地址,&操作符可以返迴一個變量的內存地址,併且*操作符可以獲取指針指向的變量內容,但是在Go語言里沒有指針運算,也就是不能像c語言里可以對指針進行加或減操作。我們會在2.3.2中進行詳細介紹。
**方法和接口:** 方法是和命名類型關聯的一類函數。Go語言里比較特殊的是方法可以被關聯到任意一種命名類型。在第六章我們會詳細地講方法。接口是一種抽象類型,這種類型可以讓我們以同樣的方式來處理不同的固有類型,不用關心它們的具體實現,而隻需要關註它們提供的方法。第七章中會詳細説明這些內容。
**包(packages):** Go語言提供了一些很好用的package,併且這些package是可以擴展的。Go語言社區已經創造併且分享了很多很多。所以Go語言編程大多數情況下就是用已有的package來寫我們自己的代碼。通過這本書,我們會講解一些重要的標準庫內的package,但是還是有很多我們沒有篇幅去説明,因爲我們沒法在這樣的厚度的書里去做一部代碼大全。
在你開始寫一個新程序之前,最好先去檢査一下是不是已經有了現成的庫可以幫助你更高效地完成這件事情。你可以在 https://golang.org/pkg 和 https://godoc.org 中找到標準庫和社區寫的package。godoc這個工具可以讓你直接在本地命令行閲讀標準庫的文檔。比如下面這個例子。
```
$ go doc http.ListenAndServe
package http // import "net/http"
func ListenAndServe(addr string, handler Handler) error
ListenAndServe listens on the TCP network address addr and then
calls Serve with handler to handle requests on incoming connections.
...
```
**註釋:** 我們之前已經提到過了在源文件的開頭寫的註釋是這個源文件的文檔。在每一個函數之前寫一個説明函數行爲的註釋也是一個好習慣。這些慣例很重要,因爲這些內容會被像godoc這樣的工具檢測到,併且在執行命令時顯示這些註釋。具體可以參考10.7.4。
多行註釋可以用 `/* ... */` 來包裹,和其它大多數語言一樣。在文件一開頭的註釋一般都是這種形式,或者一大段的解釋性的註釋文字也會被這符號包住,來避免每一行都需要加//。在註釋中//和/*是沒什麽意義的,所以不要在註釋中再嵌入註釋。
- 前言
- Go語言起源
- Go語言項目
- 本書的組織
- 更多的信息
- 致謝
- 入門
- Hello, World
- 命令行參數
- 査找重複的行
- GIF動畵
- 獲取URL
- 併發獲取多個URL
- Web服務
- 本章要點
- 程序結構
- 命名
- 聲明
- 變量
- 賦值
- 類型
- 包和文件
- 作用域
- 基礎數據類型
- 整型
- 浮點數
- 複數
- 布爾型
- 字符串
- 常量
- 複合數據類型
- 數組
- Slice
- Map
- 結構體
- JSON
- 文本和HTML模闆
- 函數
- 函數聲明
- 遞歸
- 多返迴值
- 錯誤
- 函數值
- 匿名函數
- 可變參數
- Deferred函數
- Panic異常
- Recover捕獲異常
- 方法
- 方法聲明
- 基於指針對象的方法
- 通過嵌入結構體來擴展類型
- 方法值和方法表達式
- 示例: Bit數組
- 封裝
- 接口
- 接口是合約
- 接口類型
- 實現接口的條件
- flag.Value接口
- 接口值
- sort.Interface接口
- http.Handler接口
- error接口
- 示例: 表達式求值
- 類型斷言
- 基於類型斷言識别錯誤類型
- 通過類型斷言査詢接口
- 類型分支
- 示例: 基於標記的XML解碼
- 補充幾點
- Goroutines和Channels
- Goroutines
- 示例: 併發的Clock服務
- 示例: 併發的Echo服務
- Channels
- 併發的循環
- 示例: 併發的Web爬蟲
- 基於select的多路複用
- 示例: 併發的字典遍歷
- 併發的退出
- 示例: 聊天服務
- 基於共享變量的併發
- 競爭條件
- sync.Mutex互斥鎖
- sync.RWMutex讀寫鎖
- 內存同步
- sync.Once初始化
- 競爭條件檢測
- 示例: 併發的非阻塞緩存
- Goroutines和線程
- 包和工具
- 包簡介
- 導入路徑
- 包聲明
- 導入聲明
- 包的匿名導入
- 包和命名
- 工具
- 測試
- go test
- 測試函數
- 測試覆蓋率
- 基準測試
- 剖析
- 示例函數
- 反射
- 爲何需要反射?
- reflect.Type和reflect.Value
- Display遞歸打印
- 示例: 編碼S表達式
- 通過reflect.Value脩改值
- 示例: 解碼S表達式
- 獲取結構體字段標識
- 顯示一個類型的方法集
- 幾點忠告
- 底層編程
- unsafe.Sizeof, Alignof 和 Offsetof
- unsafe.Pointer
- 示例: 深度相等判斷
- 通過cgo調用C代碼
- 幾點忠告
- 附録