Go語言基礎知識點html
go env
命令能夠查看Go語言的環境變量設置wget https://studygolang.com/dl/golang/go1.9.2.linux-amd64.tar.gz
mysql
sudo tar -xzf go1.8.3.linux-amd64.tar.gz -C /usr/local
linux
vi /etc/profile
git
export GOROOT=/usr/local/go
github
export PATH=$PATH:$GOROOT/bin
golang
export GOPATH=/usr/local/go/path
web
source /etc/profile
sql
首先,咱們在解壓的時候會獲得一個名爲go的文件夾,其中包括了全部Go語言相關的一些文件,在這下面又包含不少文件夾和文件,咱們來簡單說明其中主要文件夾的做爲:mongodb
這在Go中是一個很是重要的概念,在通常狀況下,Go源碼文件必須放在工做區中
,也就是說,咱們寫的項目代碼都必須放在咱們所設定的工做區中,雖然對於命令源碼文件來講,這不是必須的。但咱們大多都是前一種狀況。工做區其實就是一個對應特定工程的目錄,它應包含3個子目錄:shell
若是一個源碼文件被聲明屬於main代碼包,且該文件代碼中包含無參數聲明喝結果聲明的main函數,則它就是命令源碼文件。命令源碼文件可經過go run命令直接啓動運行
安裝完以後,咱們能夠進入以下目錄 cd $GOPATH/pkg/${GOOS}_${GOARCH} //能夠看到以下文件 mymath.a 這個.a文件是應用包,那麼咱們如何進行調用呢? 接下來咱們新建一個應用程序來調用這個應用包 新建應用包mathapp cd $GOPATH/src mkdir mathapp cd mathapp vim main.go $GOPATH/src/mathapp/main.go源碼: package main import ( "mymath" "fmt" ) func main() { fmt.Printf("Hello, world. Sqrt(2) = %v\n", mymath.Sqrt(2)) } 能夠看到這個的package是main,import裏面調用的包是mymath,這個就是相對於$GOPATH/src的路徑,若是是多級目錄,就在import裏面引入多級目錄,若是你有多個GOPATH,也是同樣,Go會自動在多個$GOPATH/src中尋找。 如何編譯程序呢?進入該應用目錄,而後執行go build,那麼在該目錄下面會生成一個mathapp的可執行文件 ./mathapp 輸出以下內容 Hello, world. Sqrt(2) = 1.414213562373095 如何安裝該應用,進入該目錄執行go install,那麼在$GOPATH/bin/下增長了一個可執行文件mathapp, 還記得前面咱們把$GOPATH/bin加到咱們的PATH裏面了,這樣能夠在命令行輸入以下命令就能夠執行 mathapp 也是輸出以下內容 Hello, world. Sqrt(2) = 1.414213562373095 這裏咱們展現如何編譯和安裝一個可運行的應用,以及如何設計咱們的目錄結構。
25個關鍵字
break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var var和const Go語言基礎裏面的變量和常量申明 package和import已經有太短暫的接觸 func 用於定義函數和方法 return 用於從函數返回 defer 用於相似析構函數 go 用於併發 select 用於選擇不一樣類型的通信 interface 用於定義接口 struct 用於定義抽象數據類型 break、case、continue、for、fallthrough、else、if、switch、goto、default這些參考2.3流程介紹裏面 chan用於channel通信 type用於聲明自定義類型 map用於聲明map類型數據 range用於讀取slice、map、channel數據
package是最基本的分發單位和工程管理中依賴關係的體現
import 「./model」 //當前文件同一目錄的model目錄,可是不建議這種方式來import
import 「shorturl/model」 //加載gopath/src/shorturl/model模塊
import "fmt" inport ( "fmt" ) import ( "log" "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" "gin-blog/pkg/setting" )
import( . "fmt" )
import( f "fmt" )
import ( "database/sql" _ "github.com/ziutek/mymysql/godrv" )
var
、func
、等等):=
簡潔賦值語句在明確類型的地方,能夠用於替代 var 定義。:=
這種形式有個侷限只能用在函數內部,因此通常用var方式來定義全局變量_
(下劃線)是個特殊的變量名,任何賦予它的值都會被丟棄。package main func main() { var i int }
var variableName type var vname1, vname2, vname3 type var variableName type = value var vname1, vname2, vname3 type= v1, v2, v3 // Go會根據其相應值的類型來幫你初始化它們 var vname1, vname2, vname3 = v1, v2, v3 vname1, vname2, vname3 := v1, v2, v3
import "fmt" import "os" const i = 100 const pi = 3.1415 const prefix = "Go_" var i int var pi float32 var prefix string 能夠分組寫成以下形式: import( "fmt" "os" ) const( i = 100 pi = 3.1415 prefix = "Go_" ) var( i int pi float32 prefix string )
const constantName = value const Pi float32 = 3.1415926
int 0 int8 0 int32 0 int64 0 uint 0x0 rune 0 //rune的實際類型是 int32 byte 0x0 // byte的實際類型是 uint8 float32 0 //長度爲 4 byte float64 0 //長度爲 8 byte bool false string ""
string
var s string = "hello" s[0] = 'c'
s := "hello" c := []byte(s) // 將字符串 s 轉換爲 []byte 類型 c[0] = 'c' s2 := string(c) // 再轉換回 string 類型 fmt.Printf("%s\n", s2)
s := "hello," m := " world" a := s + m fmt.Printf("%s\n", a)
s := "hello" s = "c" + s[1:] // 字符串雖不能更改,但可進行切片操做 fmt.Printf("%s\n", s)
m := `hello world`
整數類型
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
rune
是int32的別稱,byte
是uint8的別稱float32 float64
complex64 complex128
錯誤類型
err := errors.New("emit macho dwarf: elf header corrupted") if err != nil { fmt.Print(err) }
表達式 T(v) 將值 v 轉換爲類型 T
。
一些關於數值的轉換:
var i int = 42 var f float64 = float64(i) var u uint = uint(f)
或者,更加簡單的形式:
i := 42 f := float64(i) u := uint(f)
與 C 不一樣的是 Go 的在不一樣類型之間的項目賦值時須要顯式轉換。 試着移除例子中 float64 或 int 的轉換看看會發生什麼。
在定義一個變量但不指定其類型時(使用沒有類型的 var 或 := 語句), 變量的類型由右值推導得出。
當右值定義了類型時,新變量的類型與其相同:
var i int j := i // j 也是一個 int
可是當右邊包含了未指名類型的數字常量時,新的變量就多是 int 、 float64 或 complex128
。 這取決於常量的精度:
i := 42 // int f := 3.142 // float64 g := 0.867 + 0.5i // complex128
嘗試修改演示代碼中 v 的初始值,並觀察這是如何影響其類型的。
<http://www.cnblogs.com/zsy/p/5370052.html> Go裏面有一個關鍵字iota,這個關鍵字用來聲明enum的時候採用,它默認開始值是0,const中每增長一行加1: package main import ( "fmt" ) const ( x = iota // x == 0 y = iota // y == 1 z = iota // z == 2 w // 常量聲明省略值時,默認和以前一個值的字面相同。這裏隱式地說w = iota,所以w == 3。其實上面y和z可一樣不用"= iota" ) const v = iota // 每遇到一個const關鍵字,iota就會重置,此時v == 0 const ( h, i, j = iota, iota, iota //h=0,i=0,j=0 iota在同一行值相同 ) const ( a = iota //a=0 b = "B" c = iota //c=2 d, e, f = iota, iota, iota //d=3,e=3,f=3 g = iota //g = 4 ) func main() { fmt.Println(a, b, c, d, e, f, g, h, i, j, x, y, z, w, v) } 除非被顯式設置爲其它值或iota,每一個const分組的第一個常量被默認設置爲它的0值,第二及後續的常量被默認設置爲它前面那個常量的值,若是前面那個常量的值是iota,則它也被設置爲iota。
Go 具備指針。 指針保存了變量的內存地址。
類型 *T 是指向類型 T 的值的指針。其零值是 nil
。
var p *int int型的指針
& 符號會生成一個指向其做用對象的指針。
i := 42 p = &i
fmt.Println(p) // 經過指針 p 讀取 i p = 21 // 經過指針 p 設置 i
這也就是一般所說的「間接引用」或「非直接引用」。
與 C 不一樣,Go 沒有指針運算。
package main
import "fmt"
func main() { i, j := 42, 2701
p := &i // point to i fmt.Println(*p) // read i through the pointer *p = 21 // set i through the pointer fmt.Println(i) // see the new value of i p = &j // point to j *p = *p / 37 // divide j through the pointer fmt.Println(j) // see the new value of j
}
Go語言中,也和C或者其餘語言同樣,咱們能夠聲明新的類型,做爲其它類型的屬性或字段的容器。例如,咱們能夠建立一個自定義類型person表明一我的的實體。這個實體擁有屬性:姓名和年齡。這樣的類型咱們稱之struct
type person struct { name string age int } var P person // P如今就是person類型的變量了 P.name = "Astaxie" // 賦值"Astaxie"給P的name屬性. P.age = 25 // 賦值"25"給變量P的age屬性 fmt.Printf("The person's name is %s", P.name) // 訪問P的name屬性.
P := person{"Tom", 25}
P := person{age:24, name:"Tom"}
P := new(person)
package main import "fmt" type Human struct { name string age int weight int } type Student struct { Human // 匿名字段,那麼默認Student就包含了Human的全部字段 speciality string } func main() { // 咱們初始化一個學生 mark := Student{Human{"Mark", 25, 120}, "Computer Science"} // 咱們訪問相應的字段 fmt.Println("His name is ", mark.name) fmt.Println("His age is ", mark.age) fmt.Println("His weight is ", mark.weight) fmt.Println("His speciality is ", mark.speciality) // 修改對應的備註信息 mark.speciality = "AI" fmt.Println("Mark changed his speciality") fmt.Println("His speciality is ", mark.speciality) // 修改他的年齡信息 fmt.Println("Mark become old") mark.age = 46 fmt.Println("His age is", mark.age) // 修改他的體重信息 fmt.Println("Mark is not an athlet anymore") mark.weight += 60 fmt.Println("His weight is", mark.weight) }
mark.Human = Human{"Marcus", 55, 220} mark.Human.age -= 1
package main import "fmt" type Skills []string type Human struct { name string age int weight int } type Student struct { Human // 匿名字段,struct Skills // 匿名字段,自定義的類型string slice int // 內置類型做爲匿名字段 speciality string } func main() { // 初始化學生Jane jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"} // 如今咱們來訪問相應的字段 fmt.Println("Her name is ", jane.name) fmt.Println("Her age is ", jane.age) fmt.Println("Her weight is ", jane.weight) fmt.Println("Her speciality is ", jane.speciality) // 咱們來修改他的skill技能字段 jane.Skills = []string{"anatomy"} fmt.Println("Her skills are ", jane.Skills) fmt.Println("She acquired two new ones ") jane.Skills = append(jane.Skills, "physics", "golang") fmt.Println("Her skills now are ", jane.Skills) // 修改匿名內置類型字段 jane.int = 3 fmt.Println("Her preferred number is", jane.int) }
package main import "fmt" type Human struct { name string age int phone string // Human類型擁有的字段 } type Employee struct { Human // 匿名字段Human speciality string phone string // 僱員的phone字段 } func main() { Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"} fmt.Println("Bob's work phone is:", Bob.phone) // 若是咱們要訪問Human的phone字段 fmt.Println("Bob's personal phone is:", Bob.Human.phone) }
package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} p := &v p.X = 1e9 fmt.Println(v) } 結構體字段能夠經過結構體指針來訪問。
u := &User{UserId: 1, UserName: "tony"} j, _ := json.Marshal(u) fmt.Println(string(j)) // 輸出內容:{"user_id":1,"user_name":"tony"} 若是在屬性中不增長標籤說明,則輸出: {"UserId":1,"UserName":"tony"} 能夠看到直接用struct的屬性名作鍵值。 其中還有一個bson的聲明,這個是用在將數據存儲到mongodb使用的。
t := reflect.TypeOf(u) field := t.Elem().Field(0) fmt.Println(field.Tag.Get("json")) fmt.Println(field.Tag.Get("bson"))
1 package main 2 import ( 3 "fmt" 4 "reflect" // 這裏引入reflect模塊 5 ) 6 type User struct { 7 Name string "user name" //這引號裏面的就是tag 8 Passwd string "user passsword" 9 } 10 func main() { 11 user := &User{"chronos", "pass"} 12 s := reflect.TypeOf(user).Elem() //經過反射獲取type定義 13 for i := 0; i < s.NumField(); i++ { 14 fmt.Println(s.Field(i).Tag) //將tag輸出出來 15 } 16 }
1 package main 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 func main() { 9 type S struct { 10 F string `species:"gopher" color:"blue"` 11 } 12 13 s := S{} 14 st := reflect.TypeOf(s) 15 field := st.Field(0) 16 fmt.Println(field.Tag.Get("color"), field.Tag.Get("species")) 17 18 }
var arr [n]type
:在[n]type中,n表示數組的長度,type表示存儲元素的類型package main import "fmt" func main() { var a [2]string a[0] = "Hello" a[1] = "World" fmt.Println(a[0], a[1]) fmt.Println(a) }
a := [3]int{1, 2, 3} // 聲明瞭一個長度爲3的int數組 b := [10]int{1, 2, 3} // 聲明瞭一個長度爲10的int數組,其中前三個元素初始化爲一、二、3,其它默認爲0 c := [...]int{4, 5, 6} // 能夠省略長度而採用`...`的方式,Go會自動根據元素個數來計算長度☆☆☆☆☆
// 聲明瞭一個二維數組,該數組以兩個數組做爲元素,其中每一個數組中又有4個int類型的元素 doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}} // 上面的聲明能夠簡化,直接忽略內部的類型 easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} fmt.Println("p ==", p) for i := 0; i < len(p); i++ { fmt.Printf("p[%d] == %d\n", i, p[i]) } }
// 聲明一個數組 var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // 聲明兩個slice var aSlice, bSlice []byte // 演示一些簡便操做 aSlice = array[:3] // 等價於aSlice = array[0:3] aSlice包含元素: a,b,c aSlice = array[5:] // 等價於aSlice = array[5:10] aSlice包含元素: f,g,h,i,j aSlice = array[:] // 等價於aSlice = array[0:10] 這樣aSlice包含了所有的元素 // 從slice中獲取slice aSlice = array[3:7] // aSlice包含元素: d,e,f,g,len=4,cap=7 bSlice = aSlice[1:3] // bSlice 包含aSlice[1], aSlice[2] 也就是含有: e,f bSlice = aSlice[:3] // bSlice 包含 aSlice[0], aSlice[1], aSlice[2] 也就是含有: d,e,f bSlice = aSlice[0:5] // 對slice的slice能夠在cap範圍內擴展,此時bSlice包含:d,e,f,g,h bSlice = aSlice[:] // bSlice包含全部aSlice的元素: d,e,f,g
nil
。package main import "fmt" func main() { var z []int fmt.Println(z, len(z), cap(z)) if z == nil { fmt.Println("nil!") } }
slice 由函數 make 建立。這會分配一個零長度的數組而且返回一個 slice 指向這個數組:
a := make([]int, 5) // len(a)=5
爲了指定容量,可傳遞第三個參數到 make
:
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5 b = b[1:] // len(b)=4, cap(b)=4
package main import "fmt" func main() { a := make([]int, 5) printSlice("a", a) b := make([]int, 0, 5) printSlice("b", b) c := b[:2] printSlice("c", c) d := c[2:5] printSlice("d", d) } func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x) }
從Go1.2開始slice支持了三個參數的slice,以前咱們一直採用這種方式在slice或者array基礎上來獲取一個slice
var array [10]int slice := array[2:4]
這個例子裏面slice的容量是8,新版本里面能夠指定這個容量
slice = array[2:4:7]
上面這個的容量就是7-2,即5。這樣這個產生的新的slice就沒辦法訪問最後的三個元素。
若是slice是這樣的形式array[:i:j],即第一個參數爲空,默認值就是0。
對於slice有幾個有用的內置函數:
append 向slice裏面追加一個或者多個元素,而後返回一個和slice同樣類型的slice
若是 s 的底層數組過小,而不能容納全部值時,會分配一個更大的數組。 返回的 slice 會指向這個新分配的數組。
package main import "fmt" func main() { var a []int printSlice("a", a) // append works on nil slices. a = append(a, 0) printSlice("a", a) // the slice grows as needed. a = append(a, 1) printSlice("a", a) // we can add more than one element at a time. a = append(a, 2, 3, 4) printSlice("a", a) } func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x) }
for 循環的 range 格式能夠對 slice 或者 map 進行迭代循環。
package main import "fmt" var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } }
能夠經過賦值給 _ 來忽略序號和值。 若是隻須要索引值,去掉「, value」的部分便可。
package main import "fmt" func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1 << uint(i) } for _, value := range pow { fmt.Printf("%d\n", value) } }
package main import "fmt" type Vertex struct { Lat, Long float64 } var m map[string]Vertex func main() { m = make(map[string]Vertex) m["Bell Labs"] = Vertex{ 40.68433, -74.39967, } fmt.Println(m["Bell Labs"]) }
// 一、初始化一個字典 rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 } // 二、插入元素 m[key] = elem // 三、得到元素 elem = m[key] // 四、map有兩個返回值,第二個返回值,若是存在ok爲true,若是不存在key,那麼ok爲false,而且 elem 是 map 的元素類型的零值 csharpRating, ok := rating["C#"] if ok { fmt.Println("C# is in the map and its rating is ", csharpRating) } else { fmt.Println("We have no rating associated with C# in the map") } // 五、刪除元素 delete(rating, "C") // 刪除key爲C的元素
make用於內建類型(map、slice 和channel)的內存分配。new用於各類類型的內存分配。
內建函數new本質上說跟其它語言中的同名函數功能同樣:new(T)分配了零值填充的T類型的內存空間,而且返回其地址,即一個*T類型的值。用Go的術語說,它返回了一個指針,指向新分配的類型T的零值。有一點很是重要:
new返回指針。
內建函數make(T, args)與new(T)有着不一樣的功能,make只能建立slice、map和channel,而且返回一個有初始值(非零)的T類型,而不是*T。本質來說,致使這三個類型有所不一樣的緣由是指向數據結構的引用在使用前必須被初始化。例如,一個slice,是一個包含指向數據(內部array)的指針、長度和容量的三項描述符;在這些項目被初始化以前,slice爲nil。對於slice、map和channel來講,make初始化了內部的數據結構,填充適當的值。
make返回初始化後的(非零)值。
Go裏面最強大的一個控制邏輯就是for,它既能夠用來循環讀取數據,又能夠看成while來控制邏輯,還能迭代操做
package main import "fmt" func main(){ sum := 0; for index:=0; index < 10 ; index++ { sum += index } fmt.Println("sum is equal to ", sum) } // 輸出:sum is equal to 45
sum := 1 for ; sum < 1000; { sum += sum }
package main import "fmt" func main() { sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) }
for k,v:=range map { fmt.Println("map's key:",k) fmt.Println("map's val:",v) }
for _, v := range map{ fmt.Println("map's val:", v) }
package main func main() { for { } }
if x > 10 { fmt.Println("x is greater than 10") } else { fmt.Println("x is less than 10") }
// 計算獲取值x,而後根據x返回的大小,判斷是否大於10。 if x := computedValue(); x > 10 { fmt.Println("x is greater than 10") } else { fmt.Println("x is less than 10") } //這個地方若是這樣調用就編譯出錯了,由於x是條件裏面的變量 fmt.Println(x)
if integer == 3 { fmt.Println("The integer is equal to 3") } else if integer < 3 { fmt.Println("The integer is less than 3") } else { fmt.Println("The integer is greater than 3") }
switch sExpr { case expr1: some instructions case expr2: some other instructions case expr3: some other instructions default: other code }
integer := 6 switch integer { case 4: fmt.Println("The integer was <= 4") fallthrough case 5: fmt.Println("The integer was <= 5") fallthrough case 6: fmt.Println("The integer was <= 6") fallthrough case 7: fmt.Println("The integer was <= 7") fallthrough case 8: fmt.Println("The integer was <= 8") fallthrough default: fmt.Println("default case") }
switch true
同樣。package main import ( "fmt" "time" ) 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.") } }
func myFunc() { i := 0 Here: //這行的第一個詞,以冒號結束做爲標籤 println(i) i++ goto Here //跳轉到Here去 }
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) { //這裏是處理邏輯代碼 //返回多個值 return value1, value2 }
package main import "fmt" // 返回a、b中最大值. func max(a, b int) int { if a > b { return a } return b } func main() { x := 3 y := 4 z := 5 max_xy := max(x, y) //調用函數max(x, y) max_xz := max(x, z) //調用函數max(x, z) fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy) fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz) fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在這直接調用它 }
func SumAndProduct(A, B int) (add int, Multiplied int) { add = A+B Multiplied = A*B return }
func myfunc(arg ...int) {}
for _, n := range arg { fmt.Printf("And the number is: %d\n", n) }
package main import "fmt" //簡單的一個函數,實現了參數+1的操做 func add1(a *int) int { // 請注意, *a = *a+1 // 修改了a的值 return *a // 返回新值 } func main() { x := 3 fmt.Println("x = ", x) // 應該輸出 "x = 3" x1 := add1(&x) // 調用 add1(&x) 傳x的地址 fmt.Println("x+1 = ", x1) // 應該輸出 "x+1 = 4" fmt.Println("x = ", x) // 應該輸出 "x = 4" }
func ReadWrite() bool { file.Open("file") // 作一些工做 if failureX { file.Close() return false } if failureY { file.Close() return false } file.Close() return true } 咱們看到上面有不少重複的代碼,Go的defer有效解決了這個問題。使用它後,不但代碼量減小了不少,並且程序變得更優雅。在defer後指定的函數會在函數退出前調用。 func ReadWrite() bool { file.Open("file") defer file.Close() if failureX { return false } if failureY { return false } return true }
因此以下代碼會輸出4 3 2 1 0 for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) }
例如,函數 adder 返回一個閉包。每一個閉包都被綁定到其各自的 sum 變量上。 package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } }
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
package main import "fmt" type testInt func(int) bool // 聲明瞭一個函數類型 func isOdd(integer int) bool { if integer%2 == 0 { return false } return true } func isEven(integer int) bool { if integer%2 == 0 { return true } return false } // 聲明的函數類型在這個地方當作了一個參數 func filter(slice []int, f testInt) []int { var result []int for _, value := range slice { if f(value) { result = append(result, value) } } return result } func main(){ slice := []int {1, 2, 3, 4, 5, 7} fmt.Println("slice = ", slice) odd := filter(slice, isOdd) // 函數當作值來傳遞了 fmt.Println("Odd elements of slice are: ", odd) even := filter(slice, isEven) // 函數當作值來傳遞了 fmt.Println("Even elements of slice are: ", even) } 函數當作值和類型在咱們寫一些通用接口的時候很是有用,經過上面例子咱們看到testInt這個類型是一個函數類型,而後兩個filter函數的參數和返回值與testInt類型是同樣的,可是咱們能夠實現不少種的邏輯,這樣使得咱們的程序變得很是的靈活。
下面這個函數演示瞭如何在過程當中使用panic var user = os.Getenv("USER") func init() { if user == "" { panic("no value for $USER") } } 下面這個函數檢查做爲其參數的函數在執行時是否會產生panic: func throwsPanic(f func()) (b bool) { defer func() { if x := recover(); x != nil { b = true } }() f() //執行函數f,若是f中出現了panic,那麼就能夠恢復回來 return }
type ages int type money float32 type months map[string]int m := months { "January":31, "February":28, ... "December":31, }
package main import ( "fmt" "math" ) 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()) }
package main import ( "fmt" "math" ) type MyFloat float64 func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } func main() { f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs()) }
package main import "fmt" const( WHITE = iota BLACK BLUE RED YELLOW ) type Color byte type Box struct { width, height, depth float64 color Color } type BoxList []Box //a slice of boxes func (b Box) Volume() float64 { return b.width * b.height * b.depth } func (b *Box) SetColor(c Color) { b.color = c } func (bl BoxList) BiggestColor() Color { v := 0.00 k := Color(WHITE) for _, b := range bl { if bv := b.Volume(); bv > v { v = bv k = b.color } } return k } func (bl BoxList) PaintItBlack() { for i := range bl { bl[i].SetColor(BLACK) } } func (c Color) String() string { strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"} return strings[c] } func main() { boxes := BoxList { Box{4, 4, 4, RED}, Box{10, 10, 1, YELLOW}, Box{1, 1, 20, BLACK}, Box{10, 10, 1, BLUE}, Box{10, 30, 1, WHITE}, Box{20, 20, 20, YELLOW}, } fmt.Printf("We have %d boxes in our set\n", len(boxes)) fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³") fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String()) fmt.Println("The biggest one is", boxes.BiggestColor().String()) fmt.Println("Let's paint them all black") boxes.PaintItBlack() fmt.Println("The color of the second one is", boxes[1].color.String()) fmt.Println("Obviously, now, the biggest one is", boxes.BiggestColor().String()) }
副本
做爲操做對象,並不對原實例對象發生操做package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string } type Employee struct { Human //匿名字段 company string } //在human上面定義了一個method func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } func main() { mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} mark.SayHi() sam.SayHi() }
package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string } type Employee struct { Human //匿名字段 company string } //Human定義method func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } //Employee的method重寫Human的method func (e *Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) //Yes you can split into 2 lines here. } func main() { mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} mark.SayHi() sam.SayHi() }
type Human struct { name string age int phone string } type Student struct { Human //匿名字段Human school string loan float32 } type Employee struct { Human //匿名字段Human company string money float32 } //Human對象實現Sayhi方法 func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } // Human對象實現Sing方法 func (h *Human) Sing(lyrics string) { fmt.Println("La la, la la la, la la la la la...", lyrics) } //Human對象實現Guzzle方法 func (h *Human) Guzzle(beerStein string) { fmt.Println("Guzzle Guzzle Guzzle...", beerStein) } // Employee重載Human的Sayhi方法 func (e *Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) //此句能夠分紅多行 } //Student實現BorrowMoney方法 func (s *Student) BorrowMoney(amount float32) { s.loan += amount // (again and again and...) } //Employee實現SpendSalary方法 func (e *Employee) SpendSalary(amount float32) { e.money -= amount // More vodka please!!! Get me through the day! } // 定義interface type Men interface { SayHi() Sing(lyrics string) Guzzle(beerStein string) } type YoungChap interface { SayHi() Sing(song string) BorrowMoney(amount float32) } type ElderlyGent interface { SayHi() Sing(song string) SpendSalary(amount float32) }
package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string loan float32 } type Employee struct { Human //匿名字段 company string money float32 } //Human實現SayHi方法 func (h Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } //Human實現Sing方法 func (h Human) Sing(lyrics string) { fmt.Println("La la la la...", lyrics) } //Employee重載Human的SayHi方法 func (e Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) } // Interface Men被Human,Student和Employee實現 // 由於這三個類型都實現了這兩個方法 type Men interface { SayHi() Sing(lyrics string) } func main() { mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00} paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100} sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000} tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000} //定義Men類型的變量i var i Men //i能存儲Student i = mike fmt.Println("This is Mike, a Student:") i.SayHi() i.Sing("November rain") //i也能存儲Employee i = tom fmt.Println("This is tom, an Employee:") i.SayHi() i.Sing("Born to be wild") //定義了slice Men fmt.Println("Let's use a slice of Men and see what happens") x := make([]Men, 3) //這三個都是不一樣類型的元素,可是他們實現了interface同一個接口 x[0], x[1], x[2] = paul, sam, mike for _, value := range x{ value.SayHi() } }
// 定義a爲空接口 var a interface{} var i int = 5 s := "Hello world" // a能夠存儲任意類型的數值 a = i a = s
咱們知道interface的變量裏面能夠存儲任意類型的數值(該類型實現了interface)。那麼咱們怎麼反向知道這個變量裏面實際保存了的是哪一個類型的對象呢?目前經常使用的有兩種方法:
package main import ( "fmt" "strconv" ) type Element interface{} type List [] Element type Person struct { name string age int } //定義了String方法,實現了fmt.Stringer func (p Person) String() string { return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" } func main() { list := make(List, 3) list[0] = 1 // an int list[1] = "Hello" // a string list[2] = Person{"Dennis", 70} for index, element := range list { if value, ok := element.(int); ok { fmt.Printf("list[%d] is an int and its value is %d\n", index, value) } else if value, ok := element.(string); ok { fmt.Printf("list[%d] is a string and its value is %s\n", index, value) } else if value, ok := element.(Person); ok { fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) } else { fmt.Printf("list[%d] is of a different type\n", index) } } }
element.(type)
語法不能在switch外的任何邏輯裏面使用,若是你要在switch外面判斷一個類型就使用comma-ok
package main import ( "fmt" "strconv" ) type Element interface{} type List [] Element type Person struct { name string age int } //打印 func (p Person) String() string { return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" } func main() { list := make(List, 3) list[0] = 1 //an int list[1] = "Hello" //a string list[2] = Person{"Dennis", 70} for index, element := range list{ switch value := element.(type) { case int: fmt.Printf("list[%d] is an int and its value is %d\n", index, value) case string: fmt.Printf("list[%d] is a string and its value is %s\n", index, value) case Person: fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) default: fmt.Println("list[%d] is of a different type", index) } } }
咱們能夠看到源碼包container/heap裏面有這樣的一個定義 type Interface interface { sort.Interface //嵌入字段sort.Interface Push(x interface{}) //a Push method to push elements into the heap Pop() interface{} //a Pop elements that pops elements from the heap } 咱們看到sort.Interface其實就是嵌入字段,把sort.Interface的全部method給隱式的包含進來了。也就是下面三個方法: type Interface interface { // Len is the number of elements in the collection. Len() int // Less returns whether the element with index i should sort // before the element with index j. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int) } 另外一個例子就是io包下面的 io.ReadWriter ,它包含了io包下面的Reader和Writer兩個interface: package main import ( "fmt" "os" ) type Reader interface { Read(b []byte) (n int, err error) } type Writer interface { Write(b []byte) (n int, err error) } type ReadWriter interface { Reader Writer } func main() { var w Writer // os.Stdout 實現了 Writer w = os.Stdout fmt.Fprintf(w, "hello, writer\n") }
t := reflect.TypeOf(i) //獲得類型的元數據,經過t咱們能獲取類型定義裏面的全部元素 v := reflect.ValueOf(i) //獲得實際的值,經過v咱們獲取存儲在裏面的值,還能夠去改變值
tag := t.Elem().Field(0).Tag //獲取定義在struct裏面的標籤 name := v.Elem().Field(0).String() //獲取存儲在第一個字段裏面的值
var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("value:", v.Float())
var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1) 若是要修改相應的值,必須這樣寫 var x float64 = 3.4 p := reflect.ValueOf(&x) v := p.Elem() v.SetFloat(7.1) 由於傳值,傳的是拷貝的副本,若是想改變就必須改變原始值
type Stringer struct { String() string }
package main import ( "fmt" "strconv" ) type Human struct { name string age int phone string } // 經過這個方法 Human 實現了 fmt.Stringer func (h Human) String() string { return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years - ✆ " +h.phone+"❱" } func main() { Bob := Human{"Bob", 39, "000-7777-XXX"} fmt.Println("This Human is : ", Bob) }
type error interface { Error() string } (與 fmt.Stringer 相似,`fmt` 包在輸出時也會試圖匹配 `error`。) 一般函數會返回一個 error 值,調用的它的代碼應當判斷這個錯誤是否等於 `nil`, 來進行錯誤處理。 i, err := strconv.Atoi("42") if err != nil { fmt.Printf("couldn't convert number: %v\n", err) } fmt.Println("Converted integer:", i) error 爲 nil 時表示成功;非 nil 的 error 表示錯誤。 package main import ( "fmt" "time" ) type MyError struct { When time.Time What string } func (e *MyError) Error() string { return fmt.Sprintf("at %v, %s", e.When, e.What) } func run() error { return &MyError{ time.Now(), "it didn't work", } } func main() { if err := run(); err != nil { fmt.Println(err) } }
go f(x, y, z)
ci := make(chan int) cs := make(chan string) cf := make(chan interface{})
ch <- v // 發送v到channel ch. v := <-ch // 從ch中接收數據,並賦值給v
無緩衝的信道永遠不會存儲數據,只負責數據的流通,爲何這麼講呢?
默認狀況下,channel接收和發送數據都是阻塞的,除非另外一端已經準備好,這樣就使得Goroutines同步變的更加的簡單,而不須要顯式的lock。所謂阻塞,也就是若是讀取(value := <-ch)它將會被阻塞,直到有數據接收。其次,任何發送(ch<-5)將會被阻塞,直到數據被讀出。☆☆☆☆☆無緩衝channel是在多個goroutine之間同步很棒的工具☆☆☆☆☆
package main import "fmt" func sum(a []int, c chan int) { total := 0 for _, v := range a { total += v } c <- total // send total to c } func main() { a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := <-c, <-c // receive from c fmt.Println(x, y, x + y) }
var ch chan int = make(chan int) func foo() { ch <- 0 // 向ch中加數據,若是沒有其餘goroutine來取走這個數據,那麼掛起foo, 直到main函數把0這個數據拿走 } func main() { go foo() <- ch // 從ch取數據,若是ch中還沒放數據,那就掛起main線,直到foo函數中放數據爲止 }
死鎖1 func main() { ch := make(chan int) <- ch // 阻塞main goroutine, 信道c被鎖 }
死鎖2 var ch1 chan int = make(chan int) var ch2 chan int = make(chan int) func say(s string) { fmt.Println(s) ch1 <- <- ch2 // ch1 等待 ch2流出的數據 } func main() { go say("hello") <- ch1 // 堵塞主線 }
ch := make(chan type, value) 當 value = 0 時,channel 是無緩衝阻塞讀寫的,當value > 0 時,channel 有緩衝、是非阻塞的,直到寫滿 value 個元素才阻塞寫入
package main import "fmt" func main() { c := make(chan int, 2)//修改2爲1就報錯,修改2爲3能夠正常運行 c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) } //修改成1報以下的錯誤: //fatal error: all goroutines are asleep - deadlock!
package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 1, 1 for i := 0; i < n; i++ { c <- x x, y = y, x + y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } } for i := range c可以不斷的讀取channel裏面的數據,直到該channel被顯式的關閉(這句話說明這個循環會一直存在,直到你調用了close)。上面代碼咱們看到能夠顯式的關閉channel,生產者經過內置函數close關閉channel。關閉channel以後就沒法再發送任何數據了,在消費方能夠經過語法v, ok := <-ch測試channel是否被關閉。若是ok返回false,那麼說明channel已經沒有任何數據而且已經被關閉。
只有一個channel
的狀況,那麼若是存在多個channel
的時候,咱們該如何操做呢,Go裏面提供了一個關鍵字select,經過select能夠監聽channel上的數據流動。package main import "fmt" func fibonacci(c, quit chan int) { x, y := 1, 1 for { select { case c <- x: x, y = y, x + y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) }
select { case i := <-c: // use i default: // 當c阻塞的時候執行這裏 }
func main() { c := make(chan int) o := make(chan bool) go func() { for { select { case v := <- c: println(v) case <- time.After(5 * time.Second): println("timeout") o <- true break } } }() <- o }
runtime包中有幾個處理goroutine的函數: