Go語言特性小結-2017.03.29

學習背景

做爲動態類型語言的PHP在工程實現上提供了不少便利,但也致使編碼上的隨意程度愈來愈高,恰巧內部推出了一個比賽,主題是在限定條件的服務器環境下,如何實現一個最快的echo server。技術方案固然是不設限制,因爲在去年下半年涉及了一部分流式分發系統的設計內容,加上組內大牛的一直推廣,對golang產生了興趣。因此最終選擇了go1.6方向的調研,學習過程當中的一些point會彙總在這兒。html


參考資料


正文

語言特性

  • golang中不存在類、對象的概念,所以是一個基於併發模型的語言,並非完整的面向對象編程。替代方法是以type爲中心,定義類型方法,也能夠達到部分對象的功能,好處是代碼組織上有跡可尋,同一類的用同一個type+一組類型方法便可。同時,類型方法支持值傳遞和引用傳遞,以面向對象的角度看,會在編程過程當中,大部分使用引用傳遞。
  • 以package來組織代碼,名爲main的package是整個項目的入口,其他的package以庫的形式起做用,被import引進相關的package。目前理解的是,go爲了作到環境無關,把相關的庫都靜態編譯成一個可執行文件,所以編譯出來的go程序明顯體積都大於代碼,這是編譯類語言中比較少見的。
  • main方法做爲程序的入口,在main以前執行的爲init方法(每一個源文件能夠且只能夠包含一個init方法),是一個現代的用法,更貼近於咱們在業務開發中爲流程準備的hook。所以該方法在業務上也一般用來:
    1. 程序執行以前對環境、數據進行檢驗或者修復,保證程序狀態正確;
    2. 在main程序以前調用後臺執行的gorutine。
      目前看,對init的使用要慎重,由於順序不可控,起碼在ide(gogland)裏是如此。 這是一個謬誤,init的調用是嚴格有順序的,按照import的順序來進行調用。驗證參考《go語言的初始化順序,包,變量,init》結論就是,在一個go的main package文件中, 初始化順序規則: 引入的包 -> 當前包中的變量常量 -> 當前包的init -> main函數。
  • go語言對格式、引入代碼是否被使用的校驗是很嚴格的,若是引入了package、定義了變量,但在程序執行流程中沒有使用,則編譯沒法經過。
  • go爲強類型(值得推敲的一句)語言《強、弱、靜態、動態類型》,所以在值類型變換時要注意精度的問題,數據類型轉換可能會有精度損失。fmt.Printf打印各類類型須要熟記,例如bool類型的%t,golang結構獨有的%v、%+v等等。輸入輸出能夠參考一下這篇blog。能夠對字符類型作Type操做,但這個操做和別名不徹底相同,由於沒法調用原有類型的函數:Type TZ int 將int類型一個命名爲TZ。
  • 區分make和new,首先這兩個關鍵字都是golang語言預留的用於內存分配的原語,
  • 4種引用類型:map slice channel interface 其中前三者都經常使用make初始化。
  • go get = git clone + go install 由於gw問題使用:http_proxy=http://localhost:8123 go get來安裝所需的包
  • go官方全部的源碼包都託管在github上,所以,理論上去github上均可如下載
  • 同一代碼塊內的defer,後聲明先執行,是一個棧的結構,後聲明在回收過程當中先聲明
  • import (_ "xxx/fff") 下劃線表明只須要執行包中的init,並不須要全部內容,所以使用這種方式import的包,內部的方法依然沒法直接調用。import(. "xxxx/fff")這種引入方式,則不須要使用報名稱引用方法,問題是使用這種引用方式的話,多個包內有同名函數在編譯過程當中會出衝突,所以包前綴在這裏是個相似於命名空間的功能。第三種特別的寫法爲import(test "xxx/fff")表示給這個包一個別名,不須要使用package的名字,直接使用別名(這種使用場景須要進一步摸索-> todo
  • 爲了更快的開發http服務,學習beego。做爲一個restful框架,提供了不少方便的工具模塊,所以不限於http web server。參考地址beego官方網站
  • golang各個包的init加載順序是可控的,所以能夠大量使用(eg:beego)執行流程以下圖:Golang init flow
  • 匿名引用是golang面向對象的核心,一直質疑一個問題若是實現一個接口全部的func就默認是這個接口的實現(implement)那麼在代碼組織或者閱讀上天生有缺陷,這種隱式實現的方式有必定問題,目前(17.09.06)選擇只要是struct實現了某種接口的狀況下就在這個struct中增長匿名的該接口,但問題就在於,這樣的狀況,即便struct不實現interface定義的func,依然能夠算做一種interface的實現,所以不能說是很完美的解決了這個問題。具體參考Golang中的面向對象繼承
  • new和make的區別深刻學習golang。new出來的數據結構是一個指向該結構的指針,make只建立slice channel map,而且返回的是值。new會把相關結構的值置爲零值(bool 爲false int 爲0 string爲"")
  • golang中沒有構造函數,一般建立一個對象都是由全局函數Newxxx(... interface{})(*T) T爲要建立的對象。
  • switch case 中專門應對interface{}變量,增長了一個value.(type)寫法,能夠根據空接口具體的數據類型來作不一樣的操做。
  • 計算機語言上的反射指的是支持程序運行時的狀態檢測
  • select下面case命中的執行是沒有明確順序的,select 配合多條channel來使用,更可能是用來傳遞信號,若是要傳遞大量數據的話合適麼?這個須要調研
  • make只能建立切片、channel與map,之因此這麼劃分,是由於此三者在使用前都須要提早分配空間(相似於malloc),同時返回是一個具體數據結構的實例(作了初始化)。new

踩小坑

  • 有時使用go build | go install時候編譯不過,清除一下pkg庫。TODO:梳理一下golang編譯過程文件之間的依賴關係。
  • 注意包之間的引用,不能直接用包名來做爲形參引用,在編譯過程會被拒絕。
  • 網絡併發是golang的兩大核心優點,所以咱們在學習過程當中也要持續關注語言的應用場景。golang被稱爲互聯網的c語言,在須要寫server的時候,使用起來很方便。而實際上一個server須要關注的也就是這兩部分,在併發中golang提供的channel和goroutine是併發相關的兩大feature。
  • 使用yaml配置文件解析包的過程當中,yaml的key只能用小寫,若是混合大小寫,則不能識別
  • 花了半個下午,踩了個坑... 在作producer consumer的時候,沒有先把消費的gorutine先啓來,先運行select,阻塞住了。致使程序不向下運行,還沒定位到問題,腦子沒有跟上。從現象上看,一邊開會,一邊寫代碼的效率,大概爲原來的1/3。
  • gogland上有按步調試,可是暫時尚未很好的使用起來。
  • 過久沒有使用編譯型語言,對於struct內map在數據定義過程當中只是個聲明,在使用時須要先make初始化
  • 爲啥在寫代碼以前沒有好好梳理這種知識?關於golang實現繼承和組合的方法。主要是2點:1. 如何用struct + method(值方法 | 引用方法)實現類;2. 如何用struct實現繼承:匿名接口|匿名struct|匿名struct指針
  • 昨天解了本身心中的一個問題,關於對interface編程,爲什麼不能用interface的實現struct直接替代在函數中對應interface的形參,會報一個錯誤( MyType does not implement Stringer (String method has pointer receiver)),在這種狀況下使用引用傳遞是沒問題的,參考stackoverflowgit

    //數據結構定義
      type Stringer interface {
          String() string
      }
    
      type MyType struct {
          value string
      }
    
      func (m *MyType) String() string { return m.value }
    
      //錯誤用法
      m := MyType{value: "something"}
      var s Stringer
      s = m // cannot use m (type MyType) as type Stringer in assignment:
            //   MyType does not implement Stringer (String method has pointer receiver)
    
      //正確用法1
      s = &m
      fmt.Println(s)
    
      //正確用法2是在定義時把struct的方法定義成值類型,可是這種用法不少時候沒法知足須要,也就是須要在函數中修改MyType內部變量的時候,值引用不符合場景
      func (m MyType) String() string{ return m.value }

這些都是語言的特性,其中還包括在 structs and embedding 中的特色。struct A實現了interface x,當struct B 包含Struct A 時,將Struct B直接賦值給interface x類型會報錯,須要將struct *B賦值給interface x類型;或者讓struct B包含struct *A,則能夠直接對interface x類型賦值,固然在這種狀況下,struct *B也能夠直接對interface x類型賦值。github

  • 一個可能會出現的錯誤:panic: runtime error: index out of range in Go。由於引用了沒法索引的數組/切片下標。
  • 爲何咱們不用struct指針方法的模式作構造函數?由於沒法將這樣初始化出來的內容直接賦給其實現的接口,接口及其實現之間的賦值只能用指針。
  • 能夠對外返回函數局部變量的指針,而後可用。咱們理解簡單類型聲明便可用,由於變量空間開闢在棧上,struct等複雜類型聲明以後須要顯示的在堆上開闢空間,make/new 這種理解目前尚未理論來佐證。
  • redis-client一個坑 連續new兩個Client,後者的db會切到與前一個相同的db,以後來解除,TA這麼搞 就無法作多庫的一致性hash了
  • 數據的位數很重要,昨天利用snowflake算法寫一個發號器,以ip string 2 int 來作其中的主機標誌段,果真沒長腦子的溢出了,浪費了好久的時間

想知道

  • 在gogland裏面定義一個叫producer.yml的文件,結果沒有語法提示 pro.yml就行?!爲了個啥?(命名問題)
  • 如何在golang中面向接口編程?寫的時候都像個樣子,真正用的時候就慫了。須要將抽象的接口具象到具體的struct,這個動做何時實施?匿名成員-> 結構體方法(值傳遞、指針傳遞)
  • PHP和JS容許使用變量做爲函數名,或者使用CALL、CALL_STATIC這種相似的方法來調用一個命名爲aaa的方法。而go做爲靜態類型語言,須要預先定義好方法,目前用switch...case...的方法去調用對應的func,只能使用靜態工廠模式,過多的使用硬編碼。
相關文章
相關標籤/搜索