美文网首页
2019-03-19

2019-03-19

作者: YDDMAX_Y | 来源:发表于2018-10-25 11:00 被阅读0次

    语法简单,上手快

    性能高,编译快,开发效率也不低
    并发友好(提供原生并发元语和多核支持)、没有回调地狱的高性能网络库(netpoll)、以机器码运行且支持GC和Runtime机制等等。

    而相对基于事件回调的服务端模型,Go 开发思路更加符合人的逻辑处理思维,因此即使使用 Go 开发大型的项目,也很容易维护。

    原生支持并发,协程模型是非常优秀的服务端模型,同时也适合网络调用

    部署方便,编译包小,几乎无依赖

    1. 包名类似于命名空间,可以用来间接访问包内声明的标识符。
    2. 主程序的package必须是main(即使文件夹不是main),必须含有main方法
    3. 包名一般和文件夹同名(不强制,但是规范)。同一个文件夹下的包名必须相同。
    4. 必须通过包的全路径导入包,在程序中使用包时不用使用全路径。当一个go文件导入了两个后缀相同的包时,通过包命名导入解决。
      包的搜索顺序如下:
      • GOROOT也就是GO的安装目录
      • GOPATH
    5. 导入的每个包必须被使用,否则go会报错。如果不需要引用包里的东西,只是要执行包内的init函数,包名前面需要加前缀。
    6. 标准库的包不用全路径

    init函数

    每个包内可能包含多个init函数,init函数用在设置包、初始化变量或者其他需要在程序运行前优先完成的引导工作。比如,数据库驱动的注册。
    包内的init函数在main函数执行前执行。

    go 工具

    go clean 清除可执行文件
    go build 编译出可执行文件
    go run go clean build
    go vet 帮助检测代码常见错误
    go fmt 代码格式化
    go doc 包/启动web服务器 查看帮助文档

    标识符

    标识符要么从包里公开,要么不从包里公开。大写的字符是公开的,小写的字符是不公开的,不能被其他包里的代码直接访问。但是其他包可以间接访问不公开的标识符。比如,一个函数可以返回一个未公开类型的值,
    var b []int 指向数组的指针
    var b[]
    int 元素指向整型的数组

    所有变量都被初始化为零值。引用类型?

    为什么是协程而不是线程

    1. 协程和线程都能实现并发,但是线程相对于协程还是太重,而且线程上下文切换的花销也增大了。
      线程有自己的信号掩码,可以分配CPU亲和力,可以放入cgroup,并且可以查询它们使用哪些资源。所有这些特点都为Go程序如何使用goroutine根本不需要的特性增加了开销。并且当程序中有100000个线程时,这些开销会迅速增加。
    2. 调度器在线程的基础上进行协程的调度,使协程具有更灵活的调度。以垃圾收集器为例。垃圾收集器要求在运行收集时停止所有线程,并且该内存必须处于一致状态。JVM垃圾收集器在stop the world时,先到达停止点的线程需要等待未到达的线程,对线程的控制力有限。因为GO的协程是由GO调度器进行调度的,具有更灵活的控制力,能更好的控制各个goroutine的stop the world。

    调度器实现

    1. 协程首先提交到全局队列,每个执
    1. 在GO中都是值传递
    2. map[""]可以返回一个值,也可以返回二个值
      value,exist:=map[""],当存在时第一个值返回的是对应值的副本,不存在时,第一个值返回的是对应类型的空值。
    3. _的用处
      3.1 当返回多个值,又不想使用时
      3.2 包的导入
    4. for _,value:=range feeds{
      }
      其中value是副本
    5. log.fatal 打印日志,然后退出程序
    6. 不要忽视返回的error值
    7. 如果需要声明初始化为零值使用var,否则使用:=
    8. main结束,会关闭所有的routine,整个程序也都结束。写并发程序时,最佳的做法是,在main函数返回前,清理并终止所有之前启动的gorroutine。编写启动和终止时状态都清晰的程序,有助于减少bug,防止资源异常。
    9. 为什么 var waitGroup sync.WaitGroup 中waitGroup不是零值?
    10. 闭包
      不是访问副本,而是直接访问那个变量。如果外层变量发生了变化,那么闭包内也会感知到变化。使用闭包时,要注意这个特性,对于外层要发生变化的,要把其作为参数传递进闭包内,比如routine就是这样做的。
    11. defer 在函数返回时才执行,defer可以缩短打开文件和关闭文件之间间隔的代码行数,有助于提高代码可读性,减少错误。
    12. 如果接口类型只包含一个方法,那么这个类型的名字以er结尾。
    13. new返回的是个指针
    14. 空结构在创建实例时,不会分配任何内存
      方法接收者开始:
    15. 因为大部分方法在被调用后都需要维护接受者的状态,所以一个最佳实践是,将方法的接受者声明为指针。
    16. 无论我们是使用接收者类型的值来调用这个方法,还是使用接收者类型值的指针来调用这个方法,编译器都会正确地引用或者解析引用对应的值。
    17. 使用指针作为接收者声明的方法时,只能在接口类型的值是一个指针的时候被调用。使用值作为接收者声明的方法,在接口类型的值为值或者指针时,都可以被调用。
      方法接收者结束
    18. p29中var matcher defaultMatcher为什么不是个nil值?

    17. 切片

    1. append
      append

    18. 构造函数

    Result{
    a:"aa"
    }

    19. 每个包都可以单独的导入和使用,以便开发者可以根据自己的需要导入特定的功能。

    包名应该使用简洁、清晰而且全小写的名字。
    程序编译时,会使用声明main包的代码所在目录的目录名作为二进制可执行文件的文件名。

    1. 函数init
      一个包内可以包含多个init函数,init函数用在设备包、初始化变量或者其他要在程序运行前优先完成的引导工作。
    2. 远程导入
      目前的大势所趋是,使用分布式版本控制系统来分享代码
      go get 会循环导入所依赖的包
    3. 命名导入
      包的重命名
    4. 导入
      优先在GO的安装路径中查找,然后再从gopath中查找,gopath中可以配置多个路径。所导入的路径必须都是全路径的,所以能定位到每个文件。
      GOROOT/src/pkg/net/httpGOPATH/src/net/http
    5. go 工具
    6. go build 编译出可执行文件,生成的可执行文件放到当前目录
      go build XX.go
      go build xx/xx/xx (全路径编译)
      go build .
    7. go clean (移除可执行文件)
    8. go run =先编译再执行
    9. go vet 静态检查工具

    数组

    声明和初始化

    1. 数据类型式声明(无等号)
    var array [5][5]int       //var array [5]int{}是非法的,因为[5]int{}不是数据类型,里面包含了初始化的东西
    var array[5][5]*int      //指针形式
    
    1. 赋值形式
    var array = [5][5]int{{1,1},{2,2}}   //var array [5]int 也是非法的,因为5[int]是数据类型
    var array = [5][5]int{{1,1},{2,2}}   //本质和上面是相同的
    array:= [...]{1,2,3}
    array:=[5][5]int{1:{10,10},2:{20,20}}//array:=[5][5]int{1:{0:10},2:{20:20}}
    var array[5]*int{ new(int) }//指针的初始化,是用new完成内存分配并得到指针
    

    数组的使用

    array[i]
    *array[i] //指针数组指向的值

    数组的赋值

    数据类型和size相同的两个数组才能相互赋值,而且赋值是值copy。
    var array [3][2]int
    var array1[2]=array[0]

    指向数组的指针

    var array [5]int
    var pointArray [1]int //指针数组:var array[5]int
    pointArray=&array

    迭代

    1. 从头开始迭代
    for index,value:=range array{
      //value值是copy的,也就是是值复制
    }
    
    1. 自己决定从哪迭代
      for index:=2;index<len(array);index++{
      }

    切片

    切片的底层是数组,不过是动态增长的。数组[]里面是有东西的,但是切片[]里没有东西。

    1. 数组指针
    2. 长度
    3. 容量

    创建和初始化

    var slice []int  //是nil切片,var slice [5]int创建的是数组,不而且是空数组
    slice:=make([]int,3,5) //长度是3,容量是5。make的第一个参数是数据类型
    slice:=[make([]int,3)]长度和容量都是3
    slice:=[]int{1,2,3}
    slice:=[]int{99:1}
    

    切片使用

    slice[i]

    切片的相互赋值(本质是值赋值)

    1.切片相互赋值时,一般采取newSlice:=slice{1,2,2} 形式,使得长度=容量。虽然现在两个slice是共享数组的,但是在append的时候会创建新的数组,对后续修改不影响原切片的数据。

    1. 在切片的容量小于1000个元素时,总是会成倍的增加容量。一旦元素个数超过1000,容量的增长因子会设为1.25,也就是会每次增加25%容量。
    slice:=[]int{1,2,3,4}
    newSlice:=slice{1,2} //底层的数组是共享的
    newSlice:=slice{1,2,3} //长度是1,容量是2
    

    迭代

    和数组相同,value也是值复制。

    多维切片

    slice:=[][]int{{1,2},{3}}//两个切片,怎么使用make定义多维切片?
    append(slice[0],1)//slice[0]本质还是切片

    append

    append是可变参数的。:append([]int,s1,s2,s3)
    ...关键字append([]int,s1,array...)

    len

    数组、切片、通道

    capacity

    数组、切片、通道

    相关文章

      网友评论

          本文标题:2019-03-19

          本文链接:https://www.haomeiwen.com/subject/hlsqtqtx.html