2019-03-19

2018-10-25  本文已影响0人  YDDMAX_Y

语法简单,上手快

性能高,编译快,开发效率也不低
并发友好(提供原生并发元语和多核支持)、没有回调地狱的高性能网络库(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

数组、切片、通道

上一篇下一篇

猜你喜欢

热点阅读