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,只能使用静态工厂模式,过多的使用硬编码。
相关文章
相关标签/搜索