GOLang 學習筆記(一)

Knight_Davion發表於2019-05-10

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)
複製程式碼

僅當通道的緩衝區填滿後,向其傳送資料時才會阻塞。當緩衝區為空時,接受方會阻塞。

相關文章