在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 4.2 Go 程序的基本結構和要素
4.7 strings 和 strconv 包
13.6 啟動外部命令和程序
?# 11.4 類型判斷:type-switch
12.1 讀取用戶的輸入
10.6 方法
12.2 文件讀寫
13 錯誤處理與測試
9.3 鎖和 sync 包
12.3 文件拷貝
?# 11.7 第一個例子:使用 Sorter 接口排序
?# 11.5 測試一個值是否實現(xiàn)了某個接口
6.4 defer 和追蹤
12.10 XML 數(shù)據(jù)格式
13.10 性能調試:分析并優(yōu)化 Go 程序
?# 11.1 接口是什么
2.2 Go 環(huán)境變量
2.6 安裝目錄清單
2.5 在 Windows 上安裝 Go
11.11 Printf 和反射
1.2 語言的主要特性與發(fā)展的環(huán)境和影響因素
9.0 包(package)
7.4 切片重組(reslice)
13.2 運行時異常和 panic
10.2 使用工廠方法創(chuàng)建結構體實例
12.8 使用接口的實際例子:fmt.Fprintf
2.4 在 Mac OS X 上安裝 Go
3.8 Go 性能說明
7.2 切片
8.0 Map
3.1 Go 開發(fā)環(huán)境的基本要求
5.6 標簽與 goto
6.10 使用閉包調試
9.5 自定義包和可見性
4.3 常量
?# 11.2 接口嵌套接口
6.5 內置函數(shù)
前言
10.8 垃圾回收和 SetFinalizer
2.8 Go 解釋器
13.7 Go 中的單元測試和基準測試
6.8 閉包
4.9 指針
13.1 錯誤處理
10.1 結構體定義
5.1 if-else 結構
6.6 遞歸函數(shù)
9.9 通過 Git 打包和安裝
2.7 Go 運行時(runtime)
10.7 類型的 String() 方法和格式化描述符
3.7 其它工具
9.6 為自定義包使用 godoc
11.12 接口與動態(tài)類型
13.3 從 panic 中恢復(Recover)
10.3 使用自定義包中的結構體
11.14 結構體、集合和高階函數(shù)
3.6 生成代碼文檔
9.2 regexp 包
4.1 文件名、關鍵字與標識符
?# 11.6 使用方法集與接口
7.0 數(shù)組與切片
7.1 聲明和初始化
12.11 用 Gob 傳輸數(shù)據(jù)
5.5 Break 與 continue
1.1 起源與發(fā)展
?# 11 接口(Interfaces)與反射(reflection)
6.9 應用閉包:將函數(shù)作為返回值
4.2 Go 程序的基本結構和要素
8.6 將 map 的鍵值對調
6.11 計算函數(shù)執(zhí)行時間
5.0 控制結構
10.5 匿名字段和內嵌結構體
4.6 字符串
3.0 編輯器、集成開發(fā)環(huán)境與其它工具
13.8 測試的具體例子
7.6 字符串、數(shù)組和切片的應用
8.4 map 類型的切片
3.9 與其它語言進行交互
7.3 For-range 結構
9.7 使用 go install 安裝自定義包
6.0 函數(shù)
9.8 自定義包的目錄結構、go install 和 go test
6.3 傳遞變長參數(shù)
13.9 用(測試數(shù)據(jù))表驅動測試
11.9 空接口
8.1 聲明、初始化和 make
6.2 函數(shù)參數(shù)與返回值
9.11 在 Go 程序中使用外部庫
3.3 調試器
4.5 基本類型和運算符
?# 11.8 第二個例子:讀和寫
12.5 用 buffer 讀取文件
總結:Go 中的面向對象
11.10 反射包
12.7 用 defer 關閉文件
9.4 精密計算和 big 包
4.4 變量
6.1 介紹
13.4 自定義包中的錯誤處理和 panicking
12.4 從命令行讀取參數(shù)
9.10 Go 的外部包和項目
8.3 for-range 的配套用法
3.5 格式化代碼
10.4 帶標簽的結構體
7.5 切片的復制與追加
?# 11.3 類型斷言:如何檢測和轉換接口變量的類型
5.4 for 結構
4.8 時間和日期
2.3 在 Linux 上安裝 Go
12 讀寫數(shù)據(jù)
6.12 通過內存緩存來提升性能
9.1 標準庫概述
12.6 用切片讀寫文件
10 結構(struct)與方法(method)
8.5 map 的排序
12.9 JSON 數(shù)據(jù)格式
13.5 一種用閉包處理錯誤的模式
3.2 編輯器和集成開發(fā)環(huán)境
12.12 Go 中的密碼學
5.2 測試多返回值函數(shù)的錯誤
6.7 將函數(shù)作為參數(shù)
8.2 測試鍵值對是否存在及刪除元素
3.4 構建并運行 Go 程序
2.1 平臺與架構
5.3 switch 結構

4.2 Go 程序的基本結構和要素

示例 4.1 hello_world.go

package main

import "fmt"

func main() {
    fmt.Println("hello, world")
}

4.2.1 包的概念、導入與可見性

包是結構化代碼的一種方式:每個程序都由包(通常簡稱為 pkg)的概念組成,可以使用自身的包或者從其它包中導入內容。

如同其它一些編程語言中的類庫或命名空間的概念,每個 Go 文件都屬于且僅屬于一個包。一個包可以由許多以 .go 為擴展名的源文件組成,因此文件名和包名一般來說都是不相同的。

你必須在源文件中非注釋的第一行指明這個文件屬于哪個包,如:package main。package main表示一個可獨立執(zhí)行的程序,每個 Go 應用程序都包含一個名為 main 的包。

一個應用程序可以包含不同的包,而且即使你只使用 main 包也不必把所有的代碼都寫在一個巨大的文件里:你可以用一些較小的文件,并且在每個文件非注釋的第一行都使用 package main 來指明這些文件都屬于 main 包。如果你打算編譯包名不是為 main 的源文件,如 pack1,編譯后產生的對象文件將會是 pack1.a 而不是可執(zhí)行程序。另外要注意的是,所有的包名都應該使用小寫字母。

標準庫

在 Go 的安裝文件里包含了一些可以直接使用的包,即標準庫。在 Windows 下,標準庫的位置在 Go 根目錄下的子目錄 pkg\windows_386 中;在 Linux 下,標準庫在 Go 根目錄下的子目錄 pkg\linux_amd64 中(如果是安裝的是 32 位,則在 linux_386 目錄中)。一般情況下,標準包會存放在 $GOROOT/pkg/$GOOS_$GOARCH/ 目錄下。

Go 的標準庫包含了大量的包(如:fmt 和 os),但是你也可以創(chuàng)建自己的包(第 8 章)。

如果想要構建一個程序,則包和包內的文件都必須以正確的順序進行編譯。包的依賴關系決定了其構建順序。

屬于同一個包的源文件必須全部被一起編譯,一個包即是編譯時的一個單元,因此根據(jù)慣例,每個目錄都只包含一個包。

如果對一個包進行更改或重新編譯,所有引用了這個包的客戶端程序都必須全部重新編譯。

Go 中的包模型采用了顯式依賴關系的機制來達到快速編譯的目的,編譯器會從后綴名為 .o 的對象文件(需要且只需要這個文件)中提取傳遞依賴類型的信息。

如果 A.go 依賴 B.go,而 B.go 又依賴 C.go

  • 編譯 C.go, B.go, 然后是 A.go.
  • 為了編譯 A.go, 編譯器讀取的是 B.o 而不是 C.o.

這種機制對于編譯大型的項目時可以顯著地提升編譯速度。

每一段代碼只會被編譯一次

一個 Go 程序是通過 import 關鍵字將一組包鏈接在一起。

import "fmt" 告訴 Go 編譯器這個程序需要使用 fmt 包(的函數(shù),或其他元素),fmt 包實現(xiàn)了格式化 IO(輸入/輸出)的函數(shù)。包名被封閉在半角雙引號 "" 中。如果你打算從已編譯的包中導入并加載公開聲明的方法,不需要插入已編譯包的源代碼。

如果需要多個包,它們可以被分別導入:

import "fmt"
import "os"

或:

import "fmt"; import "os"

但是還有更短且更優(yōu)雅的方法(被稱為因式分解關鍵字,該方法同樣適用于 const、var 和 type 的聲明或定義):

import (
   "fmt"
   "os"
)

它甚至還可以更短的形式,但使用 gofmt 后將會被強制換行:

import ("fmt"; "os")

當你導入多個包時,最好按照字母順序排列包名,這樣做更加清晰易讀。

如果包名不是以 ./ 開頭,如 "fmt" 或者 "container/list",則 Go 會在全局文件進行查找;如果包名以 ./ 開頭,則 Go 會在相對目錄中查找;如果包名以 / 開頭(在 Windows 下也可以這樣使用),則會在系統(tǒng)的絕對路徑中查找。

導入包即等同于包含了這個包的所有的代碼對象。

除了符號 _,包中所有代碼對象的標識符必須是唯一的,以避免名稱沖突。但是相同的標識符可以在不同的包中使用,因為可以使用包名來區(qū)分它們。

包通過下面這個被編譯器強制執(zhí)行的規(guī)則來決定是否將自身的代碼對象暴露給外部文件:

可見性規(guī)則

當標識符(包括常量、變量、類型、函數(shù)名、結構字段等等)以一個大寫字母開頭,如:Group1,那么使用這種形式的標識符的對象就可以被外部包的代碼所使用(客戶端程序需要先導入這個包),這被稱為導出(像面向對象語言中的 public);標識符如果以小寫字母開頭,則對包外是不可見的,但是他們在整個包的內部是可見并且可用的(像面向對象語言中的 private )。

(大寫字母可以使用任何 Unicode 編碼的字符,比如希臘文,不僅僅是 ASCII 碼中的大寫字母)。

因此,在導入一個外部包后,能夠且只能夠訪問該包中導出的對象。

假設在包 pack1 中我們有一個變量或函數(shù)叫做 Thing(以 T 開頭,所以它能夠被導出),那么在當前包中導入 pack1 包,Thing 就可以像面向對象語言那樣使用點標記來調用:pack1.Thing(pack1 在這里是不可以省略的)。

因此包也可以作為命名空間使用,幫助避免命名沖突(名稱沖突):兩個包中的同名變量的區(qū)別在于他們的包名,例如 pack1.Thingpack2.Thing

你可以通過使用包的別名來解決包名之間的名稱沖突,或者說根據(jù)你的個人喜好對包名進行重新設置,如:import fm "fmt"。下面的代碼展示了如何使用包的別名:

示例 4.2 alias.go

package main

import fm "fmt" // alias3

func main() {
   fm.Println("hello, world")
}

注意事項

如果你導入了一個包卻沒有使用它,則會在構建程序時引發(fā)錯誤,如 imported and not used: os,這正是遵循了 Go 的格言:“沒有不必要的代碼!“。

包的分級聲明和初始化

你可以在使用 import 導入包之后定義或聲明 0 個或多個常量(const)、變量(var)和類型(type),這些對象的作用域都是全局的(在本包范圍內),所以可以被本包中所有的函數(shù)調用(如 gotemplate.go 源文件中的 c 和 v),然后聲明一個或多個函數(shù)(func)。

4.2.2 函數(shù)

這是定義一個函數(shù)最簡單的格式:

func functionName()

你可以在括號 () 中寫入 0 個或多個函數(shù)的參數(shù)(使用逗號 , 分隔),每個參數(shù)的名稱后面必須緊跟著該參數(shù)的類型。

main 函數(shù)是每一個可執(zhí)行程序所必須包含的,一般來說都是在啟動后第一個執(zhí)行的函數(shù)(如果有 init() 函數(shù)則會先執(zhí)行該函數(shù))。如果你的 main 包的源代碼沒有包含 main 函數(shù),則會引發(fā)構建錯誤 undefined: main.main。main 函數(shù)既沒有參數(shù),也沒有返回類型(與 C 家族中的其它語言恰好相反)。如果你不小心為 main 函數(shù)添加了參數(shù)或者返回類型,將會引發(fā)構建錯誤:

func main must have no arguments and no return values results.

在程序開始執(zhí)行并完成初始化后,第一個調用(程序的入口點)的函數(shù)是 main.main()(如:C 語言),該函數(shù)一旦返回就表示程序已成功執(zhí)行并立即退出。

函數(shù)里的代碼(函數(shù)體)使用大括號 {} 括起來。

左大括號 { 必須與方法的聲明放在同一行,這是編譯器的強制規(guī)定,否則你在使用 gofmt 時就會出現(xiàn)錯誤提示:

`build-error: syntax error: unexpected semicolon or newline before {`

(這是因為編譯器會產生 func main() ; 這樣的結果,很明顯這錯誤的)

Go 語言雖然看起來不使用分號作為語句的結束,但實際上這一過程是由編譯器自動完成,因此才會引發(fā)像上面這樣的錯誤

右大括號 } 需要被放在緊接著函數(shù)體的下一行。如果你的函數(shù)非常簡短,你也可以將它們放在同一行:

func Sum(a, b int) int { return a + b }

對于大括號 {} 的使用規(guī)則在任何時候都是相同的(如:if 語句等)。

因此符合規(guī)范的函數(shù)一般寫成如下的形式:

func functionName(parameter_list) (return_value_list) {
   …
}

其中:

  • parameter_list 的形式為 (param1 type1, param2 type2, …)
  • return_value_list 的形式為 (ret1 type1, ret2 type2, …)

只有當某個函數(shù)需要被外部包調用的時候才使用大寫字母開頭,并遵循 Pascal 命名法;否則就遵循駱駝命名法,即第一個單詞的首字母小寫,其余單詞的首字母大寫。

下面這一行調用了 fmt 包中的 Println 函數(shù),可以將字符串輸出到控制臺,并在最后自動增加換行字符 \n

fmt.Println("hello, world")

使用 fmt.Print("hello, world\n") 可以得到相同的結果。

PrintPrintln 這兩個函數(shù)也支持使用變量,如:fmt.Println(arr)。如果沒有特別指定,它們會以默認的打印格式將變量 arr 輸出到控制臺。

單純地打印一個字符串或變量甚至可以使用預定義的方法來實現(xiàn),如:print、println:print("ABC")、println("ABC")、println(i)(帶一個變量 i)。

這些函數(shù)只可以用于調試階段,在部署程序的時候務必將它們替換成 fmt 中的相關函數(shù)。

當被調用函數(shù)的代碼執(zhí)行到結束符 } 或返回語句時就會返回,然后程序繼續(xù)執(zhí)行調用該函數(shù)之后的代碼。

程序正常退出的代碼為 0 即 Program exited with code 0;如果程序因為異常而被終止,則會返回非零值,如:1。這個數(shù)值可以用來測試是否成功執(zhí)行一個程序。

4.2.3 注釋

示例 4.2 hello_world2.go

package main

import "fmt" // Package implementing formatted I/O.

func main() {
   fmt.Printf("Καλημ?ρα κ?σμε; or こんにちは 世界\n")
}

上面這個例子通過打印 Καλημ?ρα κ?σμε; or こんにちは 世界 展示了如何在 Go 中使用國際化字符,以及如何使用注釋。

注釋不會被編譯,但可以通過 godoc 來使用(第 3.6 節(jié))。

單行注釋是最常見的注釋形式,你可以在任何地方使用以 // 開頭的單行注釋。多行注釋也叫塊注釋,均已以 /* 開頭,并以 */ 結尾,且不可以嵌套使用,多行注釋一般用于包的文檔描述或注釋成塊的代碼片段。

每一個包應該有相關注釋,在 package 語句之前的塊注釋將被默認認為是這個包的文檔說明,其中應該提供一些相關信息并對整體功能做簡要的介紹。一個包可以分散在多個文件中,但是只需要在其中一個進行注釋說明即可。當開發(fā)人員需要了解包的一些情況時,自然會用 godoc 來顯示包的文檔說明,在首行的簡要注釋之后可以用成段的注釋來進行更詳細的說明,而不必擁擠在一起。另外,在多段注釋之間應以空行分隔加以區(qū)分。

示例:

// Package superman implements methods for saving the world.
//
// Experience has shown that a small number of procedures can prove
// helpful when attempting to save the world.
package superman

幾乎所有全局作用域的類型、常量、變量、函數(shù)和被導出的對象都應該有一個合理的注釋。如果這種注釋(稱為文檔注釋)出現(xiàn)在函數(shù)前面,例如函數(shù) Abcd,則要以 "Abcd..." 作為開頭。

示例:

// enterOrbit causes Superman to fly into low Earth orbit, a position
// that presents several possibilities for planet salvation.
func enterOrbit() error {
   ...
}

godoc 工具(第 3.6 節(jié))會收集這些注釋并產生一個技術文檔。

4.2.4 類型

可以包含數(shù)據(jù)的變量(或常量),可以使用不同的數(shù)據(jù)類型或類型來保存數(shù)據(jù)。使用 var 聲明的變量的值會自動初始化為該類型的零值。類型定義了某個變量的值的集合與可對其進行操作的集合。

類型可以是基本類型,如:int、float、bool、string;結構化的(復合的),如:struct、array、slice、map、channel;只描述類型的行為的,如:interface。

結構化的類型沒有真正的值,它使用 nil 作為默認值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 語言中不存在類型繼承。

函數(shù)也可以是一個確定的類型,就是以函數(shù)作為返回類型。這種類型的聲明要寫在函數(shù)名和可選的參數(shù)列表之后,例如:

func FunctionName (a typea, b typeb) typeFunc

你可以在函數(shù)體中的某處返回使用類型為 typeFunc 的變量 var:

return var

一個函數(shù)可以擁有多返回值,返回類型之間需要使用逗號分割,并使用小括號 () 將它們括起來,如:

func FunctionName (a typea, b typeb) (t1 type1, t2 type2)

示例: 函數(shù) Atoi (第 4.7 節(jié)):func Atoi(s string) (i int, err error)

返回的形式:

return var1, var2

這種多返回值一般用于判斷某個函數(shù)是否執(zhí)行成功(true/false)或與其它返回值一同返回錯誤消息(詳見之后的并行賦值)。

使用 type 關鍵字可以定義你自己的類型,你可能想要定義一個結構體(第 10 章),但是也可以定義一個已經存在的類型的別名,如:

type IZ int

這里并不是真正意義上的別名,因為使用這種方法定義之后的類型可以擁有更多的特性,且在類型轉換時必須顯式轉換。

然后我們可以使用下面的方式聲明變量:

var a IZ = 5

這里我們可以看到 int 是變量 a 的底層類型,這也使得它們之間存在相互轉換的可能(第 4.2.6 節(jié))。

如果你有多個類型需要定義,可以使用因式分解關鍵字的方式,例如:

type (
   IZ int
   FZ float64
   STR string
)

每個值都必須在經過編譯后屬于某個類型(編譯器必須能夠推斷出所有值的類型),因為 Go 語言是一種靜態(tài)類型語言。

4.2.5 Go 程序的一般結構

下面的程序可以被順利編譯但什么都做不了,不過這很好地展示了一個 Go 程序的首選結構。這種結構并沒有被強制要求,編譯器也不關心 main 函數(shù)在前還是變量的聲明在前,但使用統(tǒng)一的結構能夠在從上至下閱讀 Go 代碼時有更好的體驗。

所有的結構將在這一章或接下來的章節(jié)中進一步地解釋說明,但總體思路如下:

  • 在完成包的 import 之后,開始對常量、變量和類型的定義或聲明。
  • 如果存在 init 函數(shù)的話,則對該函數(shù)進行定義(這是一個特殊的函數(shù),每個含有該函數(shù)的包都會首先執(zhí)行這個函數(shù))。
  • 如果當前包是 main 包,則定義 main 函數(shù)。
  • 然后定義其余的函數(shù),首先是類型的方法,接著是按照 main 函數(shù)中先后調用的順序來定義相關函數(shù),如果有很多函數(shù),則可以按照字母順序來進行排序。

示例 4.4 gotemplate.go

package main

import (
   "fmt"
)

const c = "C"

var v int = 5

type T struct{}

func init() { // initialization of package
}

func main() {
   var a int
   Func1()
   // ...
   fmt.Println(a)
}

func (t T) Method1() {
   //...
}

func Func1() { // exported function Func1
   //...
}

Go 程序的執(zhí)行(程序啟動)順序如下:

  1. 按順序導入所有被 main 包引用的其它包,然后在每個包中執(zhí)行如下流程:
  2. 如果該包又導入了其它的包,則從第一步開始遞歸執(zhí)行,但是每個包只會被導入一次。
  3. 然后以相反的順序在每個包中初始化常量和變量,如果該包含有 init 函數(shù)的話,則調用該函數(shù)。
  4. 在完成這一切之后,main 也執(zhí)行同樣的過程,最后調用 main 函數(shù)開始執(zhí)行程序。

4.2.6 類型轉換

在必要以及可行的情況下,一個類型的值可以被轉換成另一種類型的值。由于 Go 語言不存在隱式類型轉換,因此所有的轉換都必須顯式說明,就像調用一個函數(shù)一樣(類型在這里的作用可以看作是一種函數(shù)):

valueOfTypeB = typeB(valueOfTypeA)

類型 B 的值 = 類型 B(類型 A 的值)

示例:

a := 5.0
b := int(a)

但這只能在定義正確的情況下轉換成功,例如從一個取值范圍較小的類型轉換到一個取值范圍較大的類型(例如將 int16 轉換為 int32)。當從一個取值范圍較大的轉換到取值范圍較小的類型時(例如將 int32 轉換為 int16 或將 float32 轉換為 int),會發(fā)生精度丟失(截斷)的情況。當編譯器捕捉到非法的類型轉換時會引發(fā)編譯時錯誤,否則將引發(fā)運行時錯誤。

具有相同底層類型的變量之間可以相互轉換:

var a IZ = 5
c := int(a)
d := IZ(c)

4.2.7 Go 命名規(guī)范

干凈、可讀的代碼和簡潔性是 Go 追求的主要目標。通過 gofmt 來強制實現(xiàn)統(tǒng)一的代碼風格。Go 語言中對象的命名也應該是簡潔且有意義的。像 Java 和 Python 中那樣使用混合著大小寫和下劃線的冗長的名稱會嚴重降低代碼的可讀性。名稱不需要指出自己所屬的包,因為在調用的時候會使用包名作為限定符。返回某個對象的函數(shù)或方法的名稱一般都是使用名詞,沒有 Get... 之類的字符,如果是用于修改某個對象,則使用 SetName。有必須要的話可以使用大小寫混合的方式,如 MixedCaps 或 mixedCaps,而不是使用下劃線來分割多個名稱。

鏈接