go環境安裝
go不像java這種成熟的語言有eclipse這種編譯器,但是go提供了很多插件
首先先選擇一個編譯器,我這邊使用的vscode,接着在vscode中配置go的開發環境
配置開發環境之後,接着需要安裝一些go的插件方便開發,首先先了解每種插件的作用,像gocode的作用就是代碼補全,具體可參考https://blog.csdn.net/langzi7...
安裝插件有幾種方式,可以通過vscode自動安裝,也可以通過命令行安裝,但因爲有些插件需要vpn,故可先從github上clone插件包,然後再進行安裝,過程中可能會相關的問題.....
可跟着一下博文安裝https://blog.csdn.net/Yo_oYgo...
go build go install go getValue
- go build:在當前目錄下生成可執行文件,注意:go build指令會調用所有引用包的源碼,重新編譯,而不是直接使用pkg裏的編譯後的文件,如果在
$GOROOT
或者$GOPATH
下沒有找到import引入的項目源碼,就會報錯。 - go install:編譯源代碼,如果爲可執行文件(package "main"且包含main方法),則會編譯生成可執行文件到
$GOPATH\bin
目錄下,可執行文件import引入其他包,就會被編譯到$GOPATH/pkg/$GOOS_$GOARCH
目錄下。 - go get:git clone到
$GOPATH\src
+ go install
method和function的關係
method是特殊的function,定義在某一特定的類型上,通過類型的實例來進行調用,這個實例被叫receivermethod belongs to instance
fuction is a global function belongs to package
使用method的時候注意幾點:
- 雖然method的名字一模一樣,但是如果接受者不一樣,那麼method就不一樣
- method裏面可以訪問接收者的字段
- 調用method通過.訪問,就像struct裏面訪問字段一樣
指針
類似於java的引用,但是也保證的內存安全,類似C語言內存泄漏而程序崩潰的指針運算(所謂的指針算法,如:pointer+2,移動指針指向字符串的字節數或數組的某個位置)是不允許的。
指針的傳遞很廉價,只佔用4或8個字節。
但傳遞一個變量的引用(函數的參數),這樣不會傳遞變量的拷貝。
s := "good bye"
var p *string = &s
*p = "hello"
fmt.Printf("Here is the pointer p:%p\n", p)
fmt.Printf("Here is the string *p:%s\n", *p)
fmt.Printf("Here is the string s:%s\n", s)
map的value值可以爲任何類型
interface的實現通過結構體實現相同的方式
package main
import (
"fmt"
)
type stockPosition struct {
ticker string
sharePrice float32
count float32
}
func (s stockPosition) getValue() float32 {
return s.sharePrice * s.count
}
type car struct {
make string
model string
price float32
}
func (c car) getValue() float32 {
return c.price
}
func getValue() float32 {
return 0
}
type valueable interface {
getValue() float32
}
func showValue(asset valueable) {
fmt.Printf("Value of the asset is %f\n", asset.getValue())
}
func main() {
s := getValue()
fmt.Println(s)
//接口實現
// var o valueable = stockPosition{"GOOG", 577.20, 4}
// showValue(o)
// o = car{"BMW", "M3", 66500}
// showValue(o)
//map的value可以爲任何類型
// mf := map[int]func() int{
// 1: func() int { return 10 },
// 2: func() int { return 20 },
// 3: func() int { return 50 },
// }
// fmt.Println(mf)
//map的value爲interface時
//當value爲interface,即map中的value值爲interface的實現
//跟java的面向對象有點繞,go的struct在java完全可以用class來實現
//具有接口相同的方法就可以算是實現了接口
var c car = car{"BMW", "M3", 66500}
var v valueable = stockPosition{"GOOG", 577.20, 4}
mf := map[string]interface{ getValue() float32 }{
"account": c,
"password": v,
}
for _, value := range mf {
fmt.Println(value)
fmt.Println(value.getValue())
}
}
interface萬能模型
測試
測試文件的後綴爲_test.go,並且應該跟被測試文件放在同一個目錄下
測試數據放在一個特殊的testdata目錄中,使用go build時,testdata目錄和_test.go文件都會被編譯器忽略
new跟make
func new(Type) *Type
分配空間,參數一個類型,返回值指向這個新分配的零值的指針
func make(Type,size IntegerType)
分配並且初始化一個slice,或者map或者chan對象,並且只能是這三種對象。
第一個參數爲類型,第二個參數爲長度
返回值是這個類型.
int類型跟string類型的切換
go主要通過strings包來完成對字符串的主要操作
strings.HasPrefix(s,prefix string) bool等
多返回值函數的錯誤
Go中函數經常使用兩個返回值表示執行是否成功,返回某個值以及true表示成功,返回零值(或nil)和false表示失敗。
當不使用true或false的時候,也可以使用一個error類型的變量來替代第二個返回值。
習慣用法:
value,err := pack1.Function(param1)
if err != nil{
fmt.Printf("An error occured in pack1.Function1 with parameter %v",param1)
return err
}
//未發生錯誤,繼續執行
//測試用例需要包括
- 正常的用例
- 反面的用例
- 邊界檢查用例
單元測試gomock
go中的實現泛型
go中並沒有泛型的概念,但可通過interface來實現
例如下面的冒泡排序
package main
import "fmt"
type Sortable interface {
Len() int
Less(int, int) bool
Swap(int, int)
}
func bubbleSort(array Sortable) {
for i := 0; i < array.Len(); i++ {
for j := 0; j < array.Len()-1-i; j++ {
if array.Less(j+1, j) {
array.Swap(j, j+1)
}
}
}
}
//實現接口的整形切片
type IntArr []int
func (array IntArr) Len() int {
return len(array)
}
func (array IntArr) Less(i int, j int) bool {
return array[i] < array[j]
}
func (array IntArr) Swap(i int, j int) {
array[i], array[j] = array[j], array[i]
}
func main() {
intArr1 := IntArr{3, 5, 1, 2, 3, 7, 88}
bubbleSort(intArr1)
fmt.Println(intArr1)
}
參考:https://studygolang.com/artic...
閉包
當不希望給函數起名字的時候,可以使用匿名函數,例如:func(x, y int) int {return x + y }
這樣一個函數不能夠獨立存在,但可以被賦值爲某個變量,例如 fplus := func(x, y int) int {return x + y },然後通過變量名對函數進行調用 fplus(3,4)
當然可以直接對匿名函數進行直接調用:func(x, y int) int {return x + y}(3,4)
表示參數列表的第一對括號必須緊着着關鍵字func,因爲匿名函數沒有名稱,花括號{}涵蓋着函數體,最後一堆括號表示對匿名函數的直接調用
linq
defer panic recover
defer
defer相當於java的finally
panic
panic是用來表示非常嚴重的不可恢復的錯誤,在Go語言是一個內置函數,接收一個interface{}類型的值作爲參數。panic的作用就像我們平常接觸的異常,不過Go沒有try...catch,所以panic一般會導致程序掛掉(除非recover),
所以在go語言中的異常就是真的異常了。
關鍵的一點,函數執行的時候panic了,將先到defer中執行完,panic再想傳遞
recover
recover用來捕獲panic,被recover捕獲到的panic將不會向上傳遞
recover之後,邏輯並不會恢復到panic那個點去,函數還在defer之後返回
導包下劃線"_"的作用
引入該包,但是並非真的需要使用使用這些包,同時會執行它的init()函數
//mysql的關鍵字index,處理,給該字段加上單引號反單引號Index
構建http服務
func IndexHander(w http.ResponseWriter,r *http.Request){
fmt.Fprintln("hello wrold")
}
func main(){
http.HandleFunc("/",IndexHander)
http.ListenAndServer("127.0.0.1:8080",nil)
}
接收request的過程,最重要的莫過於路由(router),即實現一個Multiplexer器,Go中既可以使用內置的multiplexer--DefaultServerMux,也可以自定義。
Multiplexer路由的目的就是爲了找到處理器函數(handle),後者講對request進行處理,同時構建response
函數作爲參數的優勢?
init函數
不能夠有任何參數,雖然每個package裏面可以寫任意多個init函數,但這無論是可讀性還是以後的可維護性來說,我們都強烈建議用戶在一個package中每個文件中只寫一個init函數
struct的聲明方式
type person struct{
name string
age int
}
//1、
var P person
p.name = "dack"
p.age = 21
//2、
p := person{"dack",21}
//3、
p := person{
age : 21
name : "dack"
}
//4、可以通過new函數分配一個指針,此處P的類型爲 *person
struct的匿名字段
當一個匿名字段是一個struct的時候,那麼這個struct所擁有的全部字段都被隱式地引入當前定義的這個struct
type Human struct{
name string
age int
weight int
}
type Student struct{
Human //匿名字段,那麼默認Student就包含了Human的所有字段
speciality string
}
func main(){
mark := Student{Human{"dacl",21,65},"Go Science"}
mark.speciality = "AI"
mark.age = 25
mark.weight = 70
}
Student可以訪問name和age就像自己用自己的字段一樣
匿名對象能夠實現字段的繼承
Student還能訪問Human
mark.Human = Human{"tony",22,55}
mark.Human.age = 1
不僅struct可以作爲匿名字段,自定義類型、內置類型也可以作爲匿名字段
如果遇到human裏面有一個字段phone,student裏面的字段phone,那麼優先訪問最外最外層
golang API json ,struct結構中標籤(Tag)的使用
在golang中,命名都是推薦用駝峯方式,並且在首字母大小寫有特殊含義:包外無法引用
但由於經常需要和其他系統進行數據交互,例如轉換成json格式,存儲到Redis等。這個時候如果直接使用屬性名來作爲鍵值會不符合項目要求
於是就有了tag,也就是有了json:name