Go基礎篇
判斷和流程控制
ofollow,noindex">https://www.jianshu.com/p/77be2afa225d
- if的使用,布林判斷條件不加括號;而且初始化的時候可以新增一個申明的初始值
if num == 3 { fmt.Println("index 3 :", i) }else{ fmt.Println(num) }
- switch 的使用:預設每個case自帶break,如果想繼續判斷需要手動新增 fallthrough;有2種寫法(直接switch 或者 switchi),適用不同的業務場景;case的值可以是整數,字串等型別
//這種場景適用 條件值是固定的業務場景 i := 5 switch i{ case 1: fmt.Println("i is equal to 1") case 2: fmt.Println("i is equal to 2") case 3,4,5,6: fmt.Println("i is equal to 3,4,5 or 6") fallthrough default: fmt.Println("others") } //這種場景適用 條件值不固定,為某一區間的業務場景 switch { case i < 0: fmt.Println("小於零") case i > 0: fmt.Println("大於零") default: fmt.Println("等於零") }
- for迴圈:go的for迴圈有如下三種形式。條件不需要括號。for迴圈 陣列和map的時候可以結合range一起用
for init; condition; post{} //和C的for一樣 for condition{}//和while一樣 for{}//結合break,continue nums := []int{2, 3, 4} for i, num := range nums { if num == 3 { fmt.Println("index 3 :", i) }else{ fmt.Println(num) } }
Golang的函式特徵
- Go的函式支援多返回值,這方便程式設計,java實現多返回值只能封裝在實體物件或者Hashmap中,典型的使用場景就是分頁查詢,既需要知道count,也需要當頁的資料
func main(){ result1,result2 := swap("hello", "kitty") } func swap(x, y string) (string, string) { return y, x }
-
Go的函式寫法和其它語言不一樣,先申明變數的名字,再申明變數的型別。設計者覺得這樣才符合正常人類的思維方式
-
Go函式和通過方法名的大小寫來實現對外許可權的控制
-
Go函式不支援過載
-
Go語言的函式的引數支援不指定數量和型別
func main(){ result := sum(3,5,7,9) fmt.Println("結果為:", result) } func sum(aregs ...int) int { s := 0 for _, number := range aregs{ s += number } return s }
Go物件的申明
- 申明一個物件,不需要再寫冗餘的get、set方法、機械式的建構函式、toString方法了,自帶靈活的建構函式
- 支援三種類型的建構函式
- 另外通過(r *rect)來新增類的方法
type Books struct { title string author string subject string book_id int } func main() { var Book1 Books Book1.title = "Go 語言" Book1.author = "www.runoob.com" Book1.subject = "Go 語言教程" Book1.book_id = 6495407 fmt.Printf( "Book title : %s\n", book1.title); var Book2 Books=Books{"Go 語言", "www.runoob.com", "Go 語言教程", 6495407} fmt.Println(Book2) fmt.Println(Book2.title) Book4 :=Books{ title:"testJson", subject:"testSubject", } } type rect struct { width int height int } func (r *rect) area() int { return r.width * r.height } func main() { r := rect{width: 10, height: 5} fmt.Println("area: ", r.area()) }
Go物件的繼承和多型
- Go語言通過把目標物件當作自身成員變數的方式,間接的實現繼承
func main(){ stu := Student{"chain"} fmt.Println(stu) aStu := AStudent{Student{"chain"}, 23} fmt.Println(aStu) } type Student struct{ name string } type AStudent struct{ Student Age int }
-
Go語言通過實現介面的方式,來實現多型,需要用到指標 &,不能指定為非 Phone介面的實現類,否則會報錯
Go語言簡潔歸簡潔,有時候過於簡潔並不方便,比如不能知道這個類的介面是哪個介面
定義一個Phone的介面,它的實現類必須實現call()方法 type Phone interface { call() } 定義一個普通類NokiaPhone type NokiaPhone struct { } 實現call()的方法 func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!") } type IPhone struct { } func (iPhone IPhone) call() { fmt.Println("I am IPhone, I can call you!") } func main() { var phone Phone phone = &NokiaPhone{} phone.call() phone= &IPhone{} phone.call() }
Go中指標的使用& 、 *
https://www.cnblogs.com/grimm/p/5623860.html
&符號的意思是對變數取地址,如:變數a的地址是&a
符號的意思是對指標取值,如: &a,就是a變數所在地址的值,當然也就是a的值了
是不可以抵消的
a和
&a是一樣的,都是a的值,值為1 (因為*&互相抵消掉了)var a int = 1 var b *int = &a fmt.Println("a = ",a)//1 fmt.Println("&a = ",&a)//0xc00000a098 a的記憶體地址 fmt.Println("*&a = ",*&a)//1*和&抵消掉了,還是1 fmt.Println("b = ",b) //0xc00000a098 b是a的指標,和 &a的值一樣 fmt.Println("&b = ",&b) //0xc000006028指標的指標 fmt.Println("*b = ",*b) //1 b指標指向的值
defer的用法
http://developer.51cto.com/art/201306/400489.htm
defer語句延遲執行一個函式,該函式被推遲到當包含它的程式返回時(包含它的函式 執行了return語句/執行到函式結尾自動返回/對應的goroutine panic/當前執行緒runtime.Goexit())執行。
按照棧的形式後進先出的原則依次執行每個defer註冊的函式。通常用來進行資源釋放,錯誤的處理,清理資料等。
- defer是後進先出
//列印的順序 main method 、method2、 method1,因為類似於棧模型,後進先出 func main() { defer doSomething("method 1") defer doSomething("method 2") fmt.Println("i am main method") } func doSomething(things string){ fmt.Println(" ia am doing somthing",things) }
- defer雖然是在當前方法執行後執行,但是defer的引數就會被實時解析。下面列印 1 0
func main() { i := 0 defer fmt.Println(i) //此時是0 i++ defer fmt.Println(i) //此時是1 }
- 經典的使用場景:簡化資源的釋放,用起來有類似finally的效果,特別適合多分支返回語句的情況,可以明顯簡化程式碼
func main() { fmt.Println(judgeNumber(-1)) fmt.Println(judgeNumber(0)) fmt.Println(judgeNumber(1)) } func judgeNumber(num int) string{ var lockName ="numlock" getLock(lockName) defer unlock(lockName) switch{ case num<0 : return "nagetive" case num==0 : return "Zero" default: return "good number" } } func getLock(lockname string){ fmt.Println("i got the lock of ",lockname) } func unlock(lockname string){ fmt.Println("i release the lock of ",lockname) }
- defer是Go語言中 異常處理的一部分,和panic()、recover()一起使用
錯誤和異常處理機制
https://www.jianshu.com/p/f30da01eea97
- Golang中引入error介面型別作為錯誤處理的標準模式,如果函式要返回錯誤,則返回值型別列表中肯定包含error,可逐層返回,直到被處理。
- Golang中引入兩個內建函式panic和recover來觸發和終止異常處理流程,同時引入關鍵字defer來延遲執行defer後面的函式。
異常和錯誤的正確使用方式:
- 當錯誤的型別只有1種的時候,直接用bool,而不是用error的實現類,因為bool更簡潔
//當hostType為 virtual_machine 或者bare_metal時校驗通過,否則校驗失敗 func (self *AgentContext) IsValidHostType(hostType string) bool { return hostType == "virtual_machine" || hostType == "bare_metal" }
-
一般error的返回值放在返回值的最後一個
-
規範使用errors.New的使用,放在常量檔案裡面,標準化輸出,而不是每次用的時候,自己定義輸出內容
util.go 裡面定義這樣的常量 var ERR_NOT_EXIST = errors.New("file is not exist") 其它地方直接使用 err := createResource1() if err != nil { return ERR_CREATE_RESOURCE1_FAILED }
經典的異常和錯誤的使用方式
- 手動丟擲異常:
if f < 0 { return 0, errors.New("math: square root of negative number") }
- 手動實現一個特定型別的異常類(類似於java的自定義Exception),需要實現Error介面的Error()方法
// 定義一個 DivideZeroError 結構,分母為0的專門的異常處理類 type DivideError struct { dividee int divider int } // 實現 `error` 介面 func (de *DivideError) Error() string { strFormat := ` Cannot proceed, the divider is zero. dividee: %d divider: 0 ` return fmt.Sprintf(strFormat, de.dividee) } // 定義 `int` 型別除法運算的函式 func Divide(varDividee int, varDivider int) (result int, errorMsg string) { if varDivider == 0 { dData := DivideError{ dividee: varDividee, divider: varDivider, } errorMsg = dData.Error() return } else { return varDividee / varDivider, "" } } func main() { // 正常情況 if result, errorMsg := Divide(100, 10); errorMsg == "" { fmt.Println("100/10 = ", result) } // 當被除數為零的時候會返回錯誤資訊 if _, errorMsg := Divide(100, 0); errorMsg != "" { fmt.Println("errorMsg is: ", errorMsg) } }
- 通過defer實現一個未知可能型別的異常,類似於java的 try catch finally的用法,聯合 panic() recover()一起使用
func main(){ defer func(){ if err := recover(); err != nil{ //捕捉異常並處理 fmt.Println("err: ", err) }}() if num, err := delive(20, -5); err == nil{ fmt.Printf("%f / %f = %f\n", 20.0, -5.0, num) }else{ fmt.Println(err) } if num, err := delive(20, 0); err == nil{ fmt.Printf("%f / %f = %f\n", 20.0, 0.0, num) }else{ fmt.Println(err) } fmt.Println("Test") } func delive(numA, numB float32) (float32, error){ if numB < 0{ return 0, errors.New("被除數不能為負數") }else if numB == 0{ panic("被除數不能為0") //丟擲異常 }else{ return numA/numB, nil } }
Go的資料型別和轉換
-
int 預設是和CPU的位數一樣的,在64位的作業系統上,預設int就是64位,相當於java的long型資料
當然 int支援: int8 、int16、 int32、int64 - uint:無符號位的整形,就是沒有符號位了,比如 int8 的範圍是-127 到128,但是 uint8的範圍是 0- 255
- 型別轉換
var test string="120" //把test字串轉換為10進位制的32位結果的數字 result,err:=strconv.ParseInt(test,10,32) if err!=nil{ fmt.Println(result) } count :=2000 resultResult:= strconv.Itoa(count)
import原理
https://blog.csdn.net/zhangzhebjut/article/details/25564457
- 按照import的先後順序挨個引入依賴類,並且先執行依賴類的init方法,再執行自己的init方法,最後執行main方法
- 現在的1.11的版本,import還是加 相對路徑吧;或者把工程路徑放在 GOPATH/src下面。
- 路徑前面加 . 的話,則可用直接引用,省略引用包名字首
- import的話,還支援別名操作,和 _ 初始化假引用
- package下小寫開頭的方法對外不可見,類似於java中的private
import ( . "fmt" "./learn1" ) import( f “fmt” )
Server版helloWord
設定訪問埠為 8888
- 設定不接受引數的網路請求
func main() { http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprintln(writer,"<h1>hello kitty </h1>") }) http.ListenAndServe(":8888",nil) }
- 接收URL引數, 利用Fprintf使用格式化輸出, %s為佔位符,取的是 request中 引數name的值
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprintf(writer,"<h1>hello kitty %s</h1>",request.FormValue("name")) })
- 併發型輸出HelloWorld,1秒內開5000個微執行緒輸出
func main() { for i :=0;i<5000;i++{ go printHelloWorld(i) } time.Sleep(time.Second) } func printHelloWorld(i int){ fmt.Printf("hello kitty ,%d \n",i) }