为什么需要一门新的语言
语言分类为编译型语言(C, c++, Java,C#,objective-c等)和动态解析型语言(PHP,Python,Ruby,lua,javaScript等);
语言最早的历史:C是最面向汇编代码的,原则上每一行C代码都可以精准的映射到汇编指令上,因此对于操作系统底层的操控来看最为精准。而C++是在C之上发展起来的面向对象语言,所以说兼顾了对系统底层最精准的把控和一些面向对象的理念;
编程哲学和语言特性:c++,java等系统语言根本编程哲学为面向对象(衍生多种流派),所以在设计模式上适合大型系统的开发,而另一大编程哲学则是简单;C和c++:贴近底层操作精准,最为重要的为指针的概念,使其对内存的操作可以精准完成,但是指针用好了是神器,用差了是垃圾,所以非常完善的测试框架对于语言高级特性的把握,十分关键。否则大型项目内存泄漏和崩溃漏洞遍地都是。新型开发语言如果不是面向对象,则设计模式不适合大型复杂系统开发,如果没有指针等概念,则使其对内存的精准操作变得不可能完成;而其他的特性还包括了 静态的,强类型的.动态解析型语言:简单
性能不是问题,python有一个很大的libs生态系统,最终用来粘合代码;
Go语言的编程哲学:
- 并行与分布式的支持。多核化和集群化是互联网时代的主要特征。
- 软件工程支持。工程规模不断扩大是产业发展的必然趋势
- 编程哲学重塑:足够简单
语言特性
并发与分布式
并发与分布式编程体现在物理层面便是多核与集群化;进程线程协程都抽象为执行体的概念,区别为:进程是系统进行资源分配和调度的基本单位,是操作系统的基础,进程是程序的实体,是一个具有独立功能的程序关于某个数据集合的一次运行活动,他可以申请和拥有系统资源是一个动态的概念一个活动的实体。线程是进程内一个相对独立的,可调度的执行单元,是系统独立调度和分配CPU的基本单位,切换由CPU时间片调度算法分配,不受自己控制;而协程其切换由自己控制,也就是由当前协程切换到其他协程由当前协程控制;
时间片很小,所以看到的线程处理的很快算作为了并发,但核数与线程数一致才算做并发,网络通信和本地读写都会阻塞并发执行;GO在语言支持协程,关键字go,语言标准库提供了所有系统调用操作(syscall),当然也包括了同步IO操作;
执行体之间的通信: 1.互斥和同步 2.消息传递 并发编程模型也就有了两种:共享内存模型和消息传递模型(依赖于类似消息队列或者进程邮箱的方式),golang采用了消息传递模型,其消息对立叫做通道
软件工程
工程规模的扩大,多数软件需要一个团队去完成,团队协作过程中,规范化提现在各个层面:代码风格规范,错误处理规范,包管理,契约规范,单元测试规范,功能开发的流程规范
Golang是第一个将代码风格强制统一的语言和首创的错误处理规范
编程哲学
多种流派:面向过程,面向对象,函数式,面向消息;golang 如果一个特性并不对解决任何问题有显著的价值,那么go就不提供它。非侵入式接口特性。
保留在看
go语言特性(静态类型语言)
- 自动垃圾回收
所有的内存分配动作都会在运行时被记录,同时任何对该内存的使用也都会被记录,然后垃圾回收器会对所有已经分配的内存进行跟踪监测,一旦有些内存已经不在被任何人使用,就阶段性的回收这些没人用的内存。
- 更丰富的内置类型
除了常用的其他静态语言支持的简单内置类型外,golang支持字典类型map 和数组切片Slice(可动态增长的数组);
- 函数多返回值
可以用下划线作为占位符忽略不关心的返回值
- 错误处理
Defer关键字用于标准的错误处理流程
- 匿名函数和闭包
Go语言中所有的都是值传递
- 类型和接口
Go语言的类型定义非常接近C语言中的struct,直接沿用了struct关键字,没有沿袭C++和java的传统去设计一个超级复杂的类型系统,只支持最基本的类型组合功能;
以前实现接口前必须定义该接口,且将类型和接口紧密绑定;go语言支持非侵入式的接口,在用的时候再去转换;
- 并发编程
Go语言使用了协程goroutine而不是裸用操作系统的并发机制,以及使用消息传递来共享内存而不是使用共享内存来通信,实现CSP通信顺序进程模型来作为goroutine间的通信方式。Go语言用channel通道了来轻巧的实现CSP模型
- 反射
反射可以获取对象的详细信息,并可动态操作对象。反射最常见的使用场景是做对象的序列化;
Java中Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。核心就是先得到代表的字节码的Class,Class类用于表示.class文件
- 语言交互性
Cgo即是语言特性也是工具名称
Go工程管理
包是go语言里最基本的分发单位,也是工程管理中依赖关系的提现;要生成GO可执行程序,必须建立一个名字为main的包,并且包含一个叫main()的函数;且不要求分号结尾
Go的编译环境,默认安装在/usr/local/go目录下;go run 编译 链接 运行 合为一步;没有任何中间文件和可执行文件,go build 生成可执行文件;6g 61才是真正的编译器和链接器,go 是明令行工具
工程管理,go test 单元测试
打印日志 和GDB调试器
Go顺序编程
变量:本质就是申请一块数据存储空间
关键字 var,但在初始化时关键字不再是必要元素 :=
常量:编译期间就已知不可改变的值
无类型常量。关键字 const,大写字母开头的常量包外可见,小写则为包内私有
类型,go语言都是值传递,而java是份基础类型和引用类型
基础类型:
- 布尔 bool
- 整型 int8/byte/int16/int/uint/unitptr
- 浮点类型 float32/float64 不是精确的表达式,不推荐用==
- 复数类型 complex64/complex128 实部real 与虚部imag
- 字符串string C/C++不存在原生的字符串类型 go语言仅支持UTF-8 和Unicode编码
- 字符类型 rune 一个是byte(时间是uint8的别名)代表utf-8的单个字节的值,rune代表单个unicode字符
- 错误类型 error
复合类型:
- 指针 pointer
- 数组array 值类型
- 切片slice 一个指向原生数组的指针,数组切片中的元素个数,数据切片已分配的内存空间
- 字典map
- 通道chan
- 结构体struct
- 接口 interface
流程控制
- 条件语句
不允许包含return
- 选择语句
Fallthrough关键字,才会继续执行紧跟的下一个case
- 循环语句
只支持for关键字,不支持逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量,break 支持更高级的层次控制
- 跳转语句
Goto 跳转到本函数内的某个标签
函数
小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用
不定参数 …interface{} 本质是数组切片
多返回值
匿名函数和闭包(包含自由变量的代码块,要执行的代码块为自由变量提供绑定的计算环境)自由变量=未绑定到特定对象;闭包的价值在于可以作为函数对象或者匿名函数;闭包的实现确保只要闭包还在使用,被闭包引用的变量就会一直存在;
错误处理
Error类型,实际是接口
Defer语句遵循先进后出的原则
Panic() 错误处理流程, recover()终止错误处理流程
Go面向对象编程
整个类型系统通过接口串联,浑然一体
类型系统:类型系统才是一门编程语言的地基,一个语言的类型体系结构;
- 基础类型
- 复合类型
- 可以指向任何对象的类型(any类型)
- 值语义和引用语义
- 面向对象:即所有具备面向对象特征的类型
- 接口
Java中存在两套完全独立的类型系统,一套是值类型系统,主要是基本类型基于值语义;一套以objec类型为根的对象类型系统,基于引用语义;相比之下go语言大多数类型为值语义,any类型为interface{};
- 为类型添加方法 (其他语言隐藏了第一个参数)
C++的面向对象让人迷惑的原因就在于其隐藏的this指针,一旦显露,大家看到的就是面向过程编程;而对于go语言来说
方法施加的目标(对象)显示传递,没有被隐藏起来
方法施加的目标(对象)不需要非得是指针,也不用非得叫this ,但是风险就是值传递和引用传递
- 值语义和引用语义
引用类型:切片,map,channel,接口 本质上是指针
- 结构体
Go语言中的结构体和其他语言的类有同等地位;go不支持继承在内的大量面向对象的特性,只保留的组合最基础的特性。组合只是复合类型的基础;go语言除了指针类型都可以定义自己的方法,所以结构体只是很普通的复合类型;
- 初始化
一般通过全局的创建函数来完成;
- 匿名组合(变向实现了继承):特点:虚基类
被组合的类型所包含的方法都升级成了这个组合类型的方法,但其实他们被组合方法调用时接收者并没有改变;
匿名组合类型相当于以其类型名称(去掉包名)作为成员变量的名字
- 可见性(访问性是包一级的,大小写)
- 接口:契约-公共特性的抽象(需求方和实现方)
侵入式接口:实现类需要明确声明自己实现了某个接口;
非侵入式接口:一个类只要实现了接口所要求的所有函数,我们就说该类实现了这个接口
接口赋值:对象实例赋值给接口(go在方法添加时不要求对象非得是指针,但在实例赋值给接口时会根据值传递的来生成一个指针的方法),将一个接口赋值给另一个接口(子集)
接口查询:
类型查询:type
接口组合:
- Any类型:空接口 inter{}
Go并发编程
并发意味着程序在运行时有多个执行上下文,对应着多个调用栈;每一个进程在运行时都有自己的调用栈和堆,有一个完整的上下文,操作系统在调度进程时,会保存被调度进程的上下文环境,等到进程获得时间片后,在恢复该进程的上下文到系统中。
多进程:操作系统层面进行并发的基本模式,也是开销最大的模式(因为所有的进程都是内核管理的)
多线程:依然属于系统层面的并发模式,但开销小很多;
基于回调的非阻塞/异步IO:多线程会很快耗尽系统的CPU和内存资源,通过事件驱动的方式使用异步IO,使服务器持续运转,并且尽可能的少用线程;
协程:本质上是一种用户态线程,不需要操作系统来进行抢占式调度,且在真正的实现中寄存于线程。最大的优点在于轻量级,可以轻松创建上百万个而不会导致资源枯竭,而线程和进程最多也不会超过1W个;go语言标准库提供的所有系统调用操作都会让出CPU给其他gorountine;(充分利用了CPU与其他硬件设备固有的异步性)
并发编程的难度在于协调,而协调就要通过交流,所以并发单元间的通信是最大的问题了;(共享数据和消息)线程之间通信只能采用共享内存的方式
消息传递系统:对线程间共享状态的各种操作都被封装在线程之间传递的消息中;发送消息时对状态进行复制(开销大),在消息传递的边界上交出这个状态的所有权。
Channel是语言级别提供的gorountine间的通信方式,channel是进程内的通信方式,如果需要跨进程通信,建议用分布式系统的方法来解决,比如SOCKET或者http等通信协议,channel是类型相关的;
Select 限制 case 语句里面必须是一个channel操作,而且可以实现超时机制
缓冲机制:创建channel时规定缓冲区大小;
Channel 本身也是原生类型,本身可传递的特性从而实现unix管道特性;
单向channel只能用于发送和接收数据,这其实只是一种使用限制;从设计的角度来看,代码都应该遵循最小权限原则,其中使用又借用了类型转换
多核并行化,go编译器还不能很智能的去发现和利用多核优势,实际创建的gorountien都运行在一个CPU核心上,在一个goroutine得到时间片的时候,其他goroutine都会处于等待状态,所以可以通过设置环境变量GOMAXPROCS的值来控制使用多少个CPU核心;
出让时间片:gosched()
虽然利用channel进行通信,但是不可避免的会存在多个goroutine之间共享数据,所以提供了同步锁;
全局唯一性操作:once`
Go网络编程
略
Go安全编程
略
Go进阶
反射
Type 和value (reflect包)
语言交互性
支持c语言
链接符号
Gdb
Gorountine机理(协程机理)
协程的特点:
- 能够在单一的系统线程中模拟多个任务的并发执行
- 在一个特定时间只有一个任务运行,即并非真正的并行
- 被动的任务调度方式,即任务没有主动抢占时间片的说法。当一个任务正在执行时,外部没有办法终止它。要进行任务切换,只能通过自身主动让出CPU使用权
- 每个协程都有自己的堆栈和局部变量
- 每个协程的3种状态:挂起,运行和停止
协程库:
- 任务及任务管理
- 任务调度器
- 异步IO
- Channel
Channel
- 内存缓存,用于存放元素
- 发送队列
- 接受队列