包
main 包中的main函式是程式的入口;
包的兩種匯入方式
1 逐個匯入
import "fmt"
import "math"
複製程式碼
2 分組匯入
import (
"fmt"
"math"
)
複製程式碼
官方建議使用分組匯入的方式
匯出名
在 Go 中,如果一個名字以大寫字母開頭,那麼它就是已匯出的;在匯入一個包時,你只能引用其中已匯出的名字。任何“未匯出”的名字在該包外均無法訪問。
例如math 包中的Pi是匯出的,pi是未匯出的,執行程式時math.pi會報錯,而math.Pi可以正常執行
函式
函式可以沒有引數或接受多個引數;
func add(x int, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
複製程式碼
注意:當連續兩個或多個函式的已命名形參型別相同時,除最後一個型別以外,其它都可以省略。
func add(x, y int) int {
return x + y
}
複製程式碼
上面的函式中
x int, y int
複製程式碼
被縮寫為
x, y int
複製程式碼
多值返回
函式可以返回任意數量的返回值。
func swap(x, y string) (string, string) {
return y, x
}
複製程式碼
變數
var 語句用於宣告一個變數或一個變數列表。
var c bool
var python, java bool
複製程式碼
短變數
只在函式內有效的變數類似於Java中的區域性變數,使用:= 符號宣告。
func main() {
k := 3
}
複製程式碼
注意::= 結構不能在函式外使用。
基本型別
bool 布林
string 字串
int int8 int16 int32 int64 整形
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的別名
rune // int32 的別名,表示一個 Unicode 碼點
float32 float64 浮點
complex64 complex128
注意:int, uint 和 uintptr 在 32 位系統上通常為 32 位寬,在 64 位系統上則為 64 位寬。 當你需要一個整數值時應使用 int 型別,除非你有特殊的理由使用固定大小或無符號的整數型別
型別轉換
可以使表示式 T(v) 將值 v 轉換為型別 T。例如:
var i int = 42
var f float64 = float64(i)
複製程式碼
注意:Go 在不同型別的項之間賦值時必須要顯式轉換。
常量
常量的宣告與變數類似,使用 const 關鍵字。
常量可以是字元、字串、布林值或數值。
常量不能用 := 語法宣告。
const Pi = 3.14
複製程式碼
迴圈結構
Go 只有一種迴圈結構:for 迴圈
for i := 0; i < 10; i++ {
sum += i
}
複製程式碼
i := 0 初始化語句 i < 10 條件表示式 i++ 後置語句
和Java非常像了。
初始化語句和後置語句也是可選的
for ; sum < 1000; {
sum += sum
}
複製程式碼
無限迴圈
for {
}
複製程式碼
if
if 語句與 for 迴圈類似,表示式外無需小括號 ( ) ,而大括號 { } 則是必須的。
if x < 0 {
return sqrt(-x) + "i"
}
複製程式碼
if 語句可以在條件表示式前執行一個簡單的語句,該語句宣告的變數作用域僅在 if 之內。
if v := math.Pow(x, n); v < lim {
return v
}
複製程式碼
switch
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
fmt.Printf("%s.\n", os)
}
複製程式碼
case 無需為常量,且取值不必為整數。
沒有條件的 switch
沒有條件的 switch 同 switch true 一樣,通常用來代替較多的if-then-else。
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
複製程式碼
defer
defer 語句會將函式推遲到外層函式返回之後執行。例如:
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
複製程式碼
結果為:
hello
world
複製程式碼
原理:推遲的函式呼叫會被壓入一個棧中。當外層函式返回時,被推遲的函式會按照後進先出的順序呼叫。
指標
Go 擁有指標。指標儲存了值的記憶體地址。型別 *T 是指向 T 型別值的指標。其零值為 nil。
var p *int
& 操作符會生成一個指向其運算元的指標。
i := 42
p = &i
星號操作符表示指標指向的底層值。
fmt.Println(*p) // 通過指標 p 讀取 i 結果為42
*p = 21 // 通過指標 p 設定 i
fmt.Println(*p) //結果為21
結構體
類似於Java中的實體,一個結構體(struct)就是一組欄位(field)。
type Vertex struct {
X int
Y int
}
func main() {
fmt.Println(Vertex{1, 2})
}
複製程式碼
注意:結構體中的欄位也可以通過指標來訪問。
陣列
表示式:[n]T 表示擁有 n 個 T 型別的值的陣列。
var a [10]int
複製程式碼
注意:陣列的長度是其型別的一部分,因此陣列不能改變大小。
切片
切片為陣列元素提供動態大小的、靈活的視角,在實踐中,切片比陣列更常用。
型別 []T 表示一個元素型別為 T 的切片。
切片通過兩個下標來界定,即一個上界和一個下界,二者以冒號分隔:
a[low : high]
它會選擇一個半開區間,包括第一個元素,但排除最後一個元素。
以下表示式建立了一個切片,它包含 a 中下標從 1 到 3 的元素:
a[1:4]
切片並不儲存任何資料,它只是描述了底層陣列中的一段。
更改切片的元素會修改其底層陣列中對應的元素。
與它共享底層陣列的切片都會觀測到這些修改。
對於陣列
var a [10]int 來說,以下切片是等價的:
a[0:10]
a[:10]
a[0:]
a[:]
複製程式碼
切片的長度就是它所包含的元素個數,可以通過len(s)獲取 切片的容量是從它的第一個元素開始數,到其底層陣列元素末尾的個數,可以通過cap(s)獲取。
nil 切片
切片的零值是 nil。
nil 切片的長度和容量為 0 且沒有底層陣列。
切片可以用內建函式 make 來建立,這也是建立動態陣列的方式。
切片中可以包含其他切片。
函式的閉包?
方法
Go 沒有類。不過你可以為結構體型別定義方法。
方法就是一類帶特殊的 接收者 引數的函式。
方法接收者在它自己的引數列表內,位於 func 關鍵字和方法名之間。
在此例中,Abs 方法擁有一個名為 v,型別為 Vertex 的接收者。
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
複製程式碼
注意:方法只是個帶接收者引數的函式。
非結構體也可定義方法,例如:
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
複製程式碼
介面型別
介面型別 是由一組方法簽名定義的集合,介面型別的變數可以儲存任何實現了這些方法的值。
Go 程
Go 程(goroutine)是由 Go 執行時管理的輕量級執行緒。
go f(x, y, z)會啟動一個新的 Go 程並執行
f(x, y, z)f, x, y 和 z 的求值發生在當前的 Go 程中,而 f 的執行發生在新的 Go 程中。
Go 程在相同的地址空間中執行,因此在訪問共享的記憶體時必須進行同步。
通道
通道是帶有型別的管道,適合在各個 Go 程間進行通訊。你可以通過它用通道操作符 <- 來傳送或者接收值。
ch <- v // 將 v 傳送至通道 ch。
v := <-ch // 從 ch 接收值並賦予 v。(“箭頭”就是資料流的方向。)
複製程式碼
和對映與切片一樣,通道在使用前必須建立:
ch := make(chan int)
複製程式碼
預設情況下,傳送和接收操作在另一端準備好之前都會阻塞。這使得 Go 程可以在沒有顯式的鎖或競態變數的情況下進行同步。
帶緩衝的通道
通道可以是 帶緩衝的。將緩衝長度作為第二個引數提供給 make 來初始化一個帶緩衝的通道:
ch := make(chan int, 100)
複製程式碼
僅當通道的緩衝區填滿後,向其傳送資料時才會阻塞。當緩衝區為空時,接受方會阻塞。