Go編程語言:支持併發、垃圾回收的編譯型系統級編程語言!本文主要是按照無聞的《Go 編程基礎》開源視頻學習並記錄筆記。編程
函數是基本的代碼塊,用於執行一個任務。Go 語言最少有個 main() 函數
。函數聲明告訴了編譯器函數的名稱,返回類型,和參數
。
Go 語言標準庫提供了多種可動用的內置的函數。例如,len() 函數能夠接受不一樣類型參數並返回該類型的長度。若是咱們傳入的是字符串則返回字符串的長度,若是傳入的是數組,則返回數組中包含的函數個數。數組
函數定義格式以下:閉包
func function_name( [parameter list] ) [return_types] { 函數體 }
函數定義解析:併發
func
:函數由 func 開始聲明function_name
:函數名稱,函數名和參數列表一塊兒構成了函數簽名。parameter list
:參數列表,參數就像一個佔位符
,當函數被調用時,你能夠將值傳遞給參數,這個值被稱爲實際參數。參數列表指定的是參數類型、順序、及參數個數
。參數是可選
的,也就是說函數也能夠不包含參數。return_types
:返回類型,函數返回一列值。return_types 是該列值的數據類型。有些功能不須要返回值,這種狀況下 return_types 不是必須的。函數體
:函數定義的代碼集合。示例:編程語言
/* 函數返回兩個數的最大值 */ func max(num1, num2 int) int { /* 聲明局部變量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
不支持
嵌套、重載和默認參數 支持
無需聲明原型、不定長度變參、多返回值、命名返回值參數、匿名函數、閉包func
,且左大括號不能另起一行函數也能夠做爲一種類型使用
ackage main import "fmt" func main() { A(1, 2, 3, 4 ,5) } // ... 不定長變參 func A(a ...int) { fmt.Println(a) }
輸出:函數
➜ myfirstgo go run func.go [1 2 3 4 5]
不定長變參特性:
一、不能夠在不定長變參後邊添加其餘參數 func b(a ...int, b string)
, 這種寫法是錯誤
的
二、不定長參數能夠放在其餘參數後邊 func b(b string, a ...int)
學習
func main() { // 將一個函數賦值一個變量,該變量是函數類型 a := func(){ fmt.Println("匿名函數") } // 調用 a() }
/** * 閉包函數 * * 該閉包函數接收一個int型參數,其返回值是函數類型 * */ func closure(x int) func(int) int { fmt.Println("%p\n", &x) return func (y int) int { fmt.Println("%p\n", &x) fmt.Println(x) fmt.Println(y) return x + y } } func main() { f := closure(10); fmt.Println(f(1)) fmt.Println(f(2)) }
打印結果:測試
➜ myfirstgo go run func.go %p 0xc42000e228 %p 0xc42000e228 10 1 11 %p 0xc42000e228 10 2 12 ➜ myfirstgo
析構函數
,在函數體執行結束後按照調用順序的相反順序
逐個執行嚴重錯誤
也會執行資源清理、文件關閉、解鎖以及記錄時間
等操做修改
函數計算結果panic/recover
模式來處理錯誤Panic
能夠在任何地方引起,但 recover
只有在 defer 調用的函數中有效簡單的測試:指針
func main() { fmt.Println("a") defer fmt.Println("b") defer fmt.Println("c") }
上邊的執行會打印什麼結果呢? 會打印:a b c 嗎?
讓咱們實際執行一下:code
myfirstgo go run func.go a c b
實際打印的結果爲:a c b
defer 的執行方式相似其餘語言中的
析構函數
,在函數體執行結束後按照調用順序的相反順序
逐個執行
使用閉包
func main() { for i := 0; i < 3; i++ { // defer 普通調用 // defer fmt.Println(i) // 打印 2 1 0 // 使用閉包,引用局部變量 defer func () { fmt.Println(i) }() } }
打印結果:
➜ myfirstgo go run func.go 3 3 3
panic 使用示例:
func main() { A() B() C() } func A() { fmt.Println("FUNC A") } func B() { // 匿名函數,若是沒有參數,則末尾須要使用括號 defer func() { if err := recover(); err != nil { fmt.Println("Recover is B") } }() panic("B panic") } func C() { fmt.Println("FUNC C") }
打印結果:
➜ myfirstgo go run func.go FUNC A Recover is B FUNC C ➜ myfirstgo
Go 語言中數組能夠存儲同一類型的數據,但在結構體中咱們能夠爲不一樣項定義不一樣的數據類型
。
結構體是由一系列具備相同類型或不一樣類型的數據構成的數據集合
。
結構體表示一項記錄,好比保存圖書館的書籍記錄,每本書有如下屬性:
Title :標題
Author : 做者
Subject:學科
ID:書籍ID
結構體定義須要使用 type
和 struct
語句。struct 語句定義一個新的數據類型,結構體中有一個或多個成員。type 語句設定告終構體的名稱。結構體的格式以下:
type struct_variable_type struct { member definition; member definition; ... member definition; }
一旦定義告終構體類型,它就能用於變量的聲明,語法格式以下:
variable_name := structure_variable_type {value1, value2...valuen}
type 是定義名稱,struct 是結構體類型,如同 int
類型同樣。
結構體能夠包含多種數據類型,數組只能是單一類型的數據集合。
訪問結構體成員
若是要訪問結構體成員,須要使用點號 (.) 操做符,格式爲:"結構體.成員名"
。
結構體類型變量使用struct
關鍵字定義,實例以下:
package main import "fmt" type Books struct { title string author string subject string book_id int } func main() { var Book1 Books /* 聲明 Book1 爲 Books 類型 */ var Book2 Books /* 聲明 Book2 爲 Books 類型 */ /* book 1 描述 */ Book1.title = "Go 語言" Book1.author = "www.runoob.com" Book1.subject = "Go 語言教程" Book1.book_id = 6495407 /* book 2 描述 */ Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 語言教程" Book2.book_id = 6495700 /* 打印 Book1 信息 */ fmt.Printf( "Book 1 title : %s\n", Book1.title) fmt.Printf( "Book 1 author : %s\n", Book1.author) fmt.Printf( "Book 1 subject : %s\n", Book1.subject) fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id) /* 打印 Book2 信息 */ fmt.Printf( "Book 2 title : %s\n", Book2.title) fmt.Printf( "Book 2 author : %s\n", Book2.author) fmt.Printf( "Book 2 subject : %s\n", Book2.subject) fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id) }
以上實例執行運行結果爲:
Book 1 title : Go 語言 Book 1 author : www.runoob.com Book 1 subject : Go 語言教程 Book 1 book_id : 6495407 Book 2 title : Python 教程 Book 2 author : www.runoob.com Book 2 subject : Python 語言教程 Book 2 book_id : 6495700
type <Name> struct{}
定義結構,名稱遵循可見性規則匿名
結構,可用做成員或定義成員變量示例:
package main import "fmt" type person struct{ Name string Age int } func main() { a := person{ Name:"Jam", Age:19, } // a.Age = 20 fmt.Println(a) }
打印:
➜ myfirstgo go run struct.go {Jam 19}
傳遞指針變量:
package main import "fmt" type person struct{ Name string Age int } func main() { a := person{ Name:"Jam", Age:19, } // a.Age = 20 fmt.Println(a) A(a) // 值拷貝,若是須要原來的改變,則須要添加指針 B(&a) fmt.Println(a) } // per 爲變量名,person表示爲結構體類型 func A(per person) { per.Age = 25 fmt.Println("A", per) } // per 爲變量名,person表示爲結構體類型 func B(per *person) { per.Age = 18 fmt.Println("B", per) }
打印:
➜ myfirstgo go run struct.go {Jam 19} A {Jam 25} B &{Jam 18} {Jam 18}
或者在初始化結構體時,獲取到變量地址並賦值變量,這樣作的好處是在傳遞參數時,不須要傳遞地址符號了,只需在函數定義時,給參數加星號便可。
package main import "fmt" type person struct{ Name string Age int } func main() { // 在結構初識化時,咱們習慣取地址符號,這樣a就爲指向某個結構的指針 a := &person{ Name:"Jam", Age:19, } a.Name = "Corwien" // a.Age = 20 fmt.Println(a) A(a) // 值拷貝,若是須要原來的改變,則須要添加指針 B(a) fmt.Println(a) } // per 爲變量名,person表示爲結構體類型 func A(per *person) { per.Age = 25 fmt.Println("A", per) } // per 爲變量名,person表示爲結構體類型 func B(per *person) { per.Age = 18 fmt.Println("B", per) }
打印:
➜ myfirstgo go run struct.go &{Corwien 19} A &{Corwien 25} B &{Corwien 18} &{Corwien 18}
匿名結構:
匿名結構,沒有名稱的結構體
func main() { // 匿名結構,沒有名稱的結構體 a := struct { Name string Age int }{ Name:"Corwien", Age: 20, } fmt.Println(a) }
打印:
➜ myfirstgo go run struct.go {Corwien 20}
匿名結構嵌套:
type person struct{ Name string Age int Contact struct { Phone, City string Code int // 門牌號 } } func main() { a := person{Name:"Corwien", Age:15} a.Contact.Phone = "10086" a.Contact.City = "Guangzhou" a.Contact.Code = 2007 fmt.Println(a) }
打印:
➜ myfirstgo go run struct.go {Corwien 15 {10086 Guangzhou 2007}}
匿名字段:
匿名字段:結構體沒有命名結構體屬性的字段,只有類型,匿名字段必須嚴格遵照字段類型聲明的順序。
type person struct{ string int } func main() { // 匿名字段必須嚴格遵照字段類型聲明的順序 a := person{"Corwien", 12} fmt.Println(a) }
打印:
➜ myfirstgo go run struct.go {Corwien 12}
結構類型比較
type person struct{ Name string Age int } func main() { // 匿名字段必須嚴格遵照字段類型聲明的順序 a := person{Name:"Corwien", Age:12} b := person{Name:"Corwien", Age:12} fmt.Println(a == b) }
打印:
➜ myfirstgo go run struct.go true
咱們知道其餘語言有繼承,好比相同的屬性,咱們沒必要重複去寫,只需繼承父類的公共屬性便可。遺憾的是Go沒有繼承,但Go有組合
.
package main import "fmt" // 嵌入結構做爲匿名字段 type human struct { Sex int } type teacher struct { human // Go會將嵌入字段默認做爲屬性名,因此在賦值時須要這樣寫:human: human{Sex: 1} Name string Age int } type student struct { human Name string Age int } func main() { a := teacher{Name:"Corwien", Age:25, human: human{Sex: 1}} b := student{Name:"mark", Age:12, human: human{Sex: 1}} a.Name = "Jack" a.Age = 10 // a.human.Sex = 0 a.Sex = 0 fmt.Println(a, b) }
打印:
➜ myfirstgo go run struct.go {{0} Jack 10} {{1} mark 12}
Go不像其它面相對象語言同樣能夠寫個class,而後在class裏面寫一堆方法,可是它也很巧妙的實現了這種效果,咱們只須要在普通函數前面加個接受者(receiver,寫在函數名前面的括號裏面),這樣編譯器就知道這個函數(方法)屬於哪一個struct了。
method
是附屬在一個給定的類型上,語法和函數的聲明語法幾乎同樣,只是再func後面增長了一個recevier
(也就是method所依從的主體)
func (r ReceiverType) funcName(parameters) (results)
形象一點說,就是 ReceiverType
類型的全部字段,方法 funcName
都是可使用的,能夠認爲 funcName
屬於 ReceiverType
。
示例:
package main import ( "fmt" "math" ) type Rectangle struct { width, height float64 } type Circle struct { radius float64 } func (r Rectangle) area() float64 { return r.width * r.height } func (c Circle) area() float64 { return c.radius * c.radius * math.Pi } func main() { r1 := Rectangle{12, 2} r2 := Rectangle{9, 4} c1 := Circle{10} c2 := Circle{25} fmt.Println("Area of r1 is: ", r1.area()) fmt.Println("Area of r2 is: ", r2.area()) fmt.Println("Area of c1 is: ", c1.area()) fmt.Println("Area of c2 is: ", c2.area()) }
輸出:
Area of r1 is: 24 Area of r2 is: 36 Area of c1 is: 314.1592653589793 Area of c2 is: 1963.4954084936207
method 是經過 .
來訪問,就像訪問struct裏面字段同樣。
method 裏面能夠訪問接受者的字段,好比 r1.area() 就能夠訪問 r1 裏面的 width 和 height。
雖然 method 的名字是同樣的,可是不一樣的 receiver 不同,那麼 method 就不同。這一點很重要哦
。
還有一點,method不只能做用再struct上,也能夠定義再任何自定義的類型、內置類型等各類類型上面。
method 中的 receiver 能夠是值傳遞,也能夠是指針。指針的話,就能夠直接修改 receiver 中的內容。
不存在方法重載
方法是函數的語法糖
,由於receiver其實就是方法所接收的第1個參數)舉例:
package main import "fmt" type A struct { Name string } type B struct { Name string } func main() { a := A{} a.Print() fmt.Println(a.Name) b := B{} b.Print() fmt.Println(b.Name) } // 指針傳遞 func (a *A) Print() { a.Name = "AA" fmt.Println("A") } func (b B) Print() { b.Name = "BB" fmt.Println("B") }
打印:
➜ myfirstgo go run method.go A AA B ➜ myfirstgo
type TZ int func main() { var a TZ a.Print() fmt.Println(a) } func (a *TZ) Print() { fmt.Println("TZ") }
打印:
➜ myfirstgo go run method.go TZ 0
最後說下訪問權限,由於Go是以大小寫來區分是公有仍是私有,但都是針對包級別的,因此在包內全部的都能訪問,而方法綁定自己只能綁定包內的類型,因此方法能夠訪問接收者全部成員。若是是包外調用某類型的方法,則須要看方法名是大寫仍是小寫,大寫能被包外訪問,小寫只能被包內訪問。