Go總結(2)
struct
go中struct結構預設欄位都會有零值,故不能用nil來判斷struct是否為空,可通過額外的欄位來判斷struct是否填充或為空
type Demo struct{ ready bool name string //其他欄位 }
在初始化的時候必須將ready設定為true
var d Demo if !d.ready{ //do stuff }
Web工作方式
- Go通過
ListenAndServer
來建立web伺服器,底層是初始化一個server
物件,然後呼叫net.Listen("tcp",addr)
來監聽埠。 - 呼叫
srv.server(net.Listener)
函式來處理接收客戶端請求。函式裡面為一個for{}
,首先通過accept
接受請求,接著建立一個Conn,最後單獨開一個goroutine取執行:go c.server()
。 - 使用者的每一次請求都是一個新的goroutine去執行。
- conn通過解析request
c.readRequest()
獲取相應的handler := c.server.Handler
,它本質是一個路由器,通過它來匹配url跳到對應的handle函式。 - 可通過`http.HandleFunc("/",sayhelloName)來註冊請求的路由規則。
OS獲取環境變數
os.getenv()獲取環境變數獲取不到最新設定的環境變數,最新設定的需要重新啟動電腦獲取
基本型別
這兩天在搞反射,看到Go的基礎資料型別那麼多,int,int32,int64都有,而且運算過程中還需要轉換,所以抽空看了些部落格以及官方文件。
-
int跟uint
- 有符號:int8,int16,int32,int64
- 無符號:unit8,unit16,unit32,uint64
- int和unit取決於作業系統,在32位系統就是32位元組,在64位系統就是64位元組
- int跟int32不是相同的型別,雖然在特定的場景下它們大小相同,但是在運算過程中需要轉換
- byte是unit8的別名,rune是int32的別名
-
浮點型別為float32和float64
- 浮點型別在運算過程中可能存在精度丟失的情況
-
string
- 字串是不可變的,一旦建立,就不能改變字串的內容
- 可以使用內建函式len來發現s的長度,如果字串為常量,則length是編譯時常量。
- 字串的位元組可以通過索引來獲取,但是取元素的地址是非法的,即&s[i]是無效的。
反射
每個語言的反射模型都不同(很多語言根本不支援它)
Type And interface
因為反射是建立在型別系統上,讓我們先回顧一下Go中的型別。
Go是靜態型別語言,每個變數都有一個靜態型別,即在編譯時就已知被固定上一種型別: int, float32, &MyType, []byte
type MyInt int var i int var j MyInt
變數i和j具有不同的靜態型別,雖然他們具有相同的底層型別,但如果沒有轉換,則無法將他們分配給彼此。
interface可以儲存任何具體的值,interface包括:
- 變數包括(type,value)兩部分,這也是為什麼nil != nil的原因
- type包括static type和concrete type,static type是編輯時就看到的型別,而concrete type是runtime看到的型別
反射就是建立在型別之上的,Golang的指定型別的變數的型別是靜態的(也就是指定int、string這些變數,它的type是static type),在建立變數的時候就已經確定,反射主要於Golang的interface有關(它的型別是concrete type),只有interface型別才有反射之說。
API
以下是一些API
reflect: TypeOf(interface{}) Type : 返回介面中儲存值的型別,i為nil值返回nil ValueOf(interface{}) Value : 返回一個初始化為i介面保管的具體值的Value,但i為nil時返回Value零值 New(Type) Value:返回一個指向型別為Type的新申請的零值的指標。 Type: Kind():返回該介面的具體型別 Name():返回型別名 Elem():返回該型別的元素型別,如果Kind不是Array,Chan,Map,Slice,Ptr會panic Value: Append(s Value,x ...Value) Value: s需為切片型別的Value值,x需為s的元素型別的Value值,將x複製給s並且返回s Type():返回v持有的型別的Type表示 Elem() Value:返回v持有的介面或者指標保管值的Value封裝,如果v的Kind不是interface或者Ptr將會panic Kind():同上一致 CanSet():判斷v持有的值是否能更改,只有當Value持有值為Ptr並且為共有型別時,它才可以被修改。 Set(x Value):將v的持有值修改為x的持有值 SetInt(x Int64) SetString(s string) ....
更多的可參考官方文件: https://go-zh.org/pkg/reflect/#Value.Convert
反射講得比較好的一篇文章: https://juejin.im/post/5a75a4fb5188257a82110544
Go執行時
儘管Go編譯器產生的是本地可執行程式碼,這些程式碼仍舊執行在Go的runtime(這部分的程式碼可以在runtime包中找到)當中,這個runtime類似虛擬機器,它負責管理包括記憶體分配、垃圾回收、棧處理、goroutine、channel、slice、map和reflection等等。
Interface
Go中的interface並不是顯示實現的,這就導致,一個方法接收的型別為IPerson,但是雖然我的Student物件已經實現了IPerson,但是還是不要在通過方法去new實現返回IPerson物件
type Student struct{ name string age int } func newIPerson(name string,age int) IPerson{ return Student{ name : name, age : age, } }
切片
-
make([]int,l,c)
,l
為長度,c
為容量,不傳c
則容量等於長度 - 底層還是陣列,通過
len()
獲取長度,cap()
獲取容量 -
append
之後返回的是一個新的切片 -
擴容:
-
capacity
小於1000時,兩倍擴容 -
capacity
大於1000時,增長因子為1.25
,25%
擴容
-
-
賦值:將一個切片賦值給另一個切片可指定索引
- 第一個索引:指定切片的頭部
- 第二個索引:指定切片長度的尾部
- 第三個索引:限制切片的容量
參考下面程式碼:
a := []int{1, 2, 3, 4, 5} b := a[1:] c := a[:4] d := a[1:4] e := a[2:3:4] fmt.Println("a", len(a), cap(a)) fmt.Println("b", len(b), cap(b)) fmt.Println("c", len(c), cap(c)) fmt.Println("d", len(d), cap(d)) fmt.Println("e", len(e), cap(e)) //列印結果 a 5 5 b 4 4 c 4 5 d 3 4 e 1 2
for-range