前言
学习任何知识都会有一个学习背景
最近,我们团队乃至我司整个云服务,上go的呼声越来越高!新服务已经开始用go开发,部分现有Java版的服务重构为go也只是时间问题而已,故相关技术积累势在必行!在云网络的分布式服务乃至在一切高并发,分布式后台服务中,golang都有着很大的优势。
据我对国内互联网行业的实际考察,了解,目前国内主流互联网公司都在积极投入go的怀抱……
青云更是全栈使用了go……
还有火的一塌糊涂的docker。
它为云而生。
它为并发而生。
还有go的安全、简洁、高效
有良好的Java、C/C++背景,拿起go很容易。
……
参考:
2017年编程语言排行榜,相比2016,go的飞速上升趋势很明显。而Java有很大份额的缩减……
当然,当前在企业级应用开发中,Java依然占有一席之地。
而个人认为:一个优秀的研发工程师乃至架构师……是不能仅仅局限于一门语言的,语言只是拿来就用的工具,关键还是实战经验和行业内功的修炼。但是对于任何编程语言,不学也不是凭空就会的。
简单说go的历史和应用
历史和概况
Go语言是一个年轻的语言,是Google在2009年发布的第二款开源的编程语言,它是编译型语言,专门针对多处理器系统应用程序的编程进行了优化,因此使用Go编译的程序可以媲美C或C++代码的速度。
go还具有类似Java的垃圾回收机制,且实现了数组安全……故go有机的结合了Java和C/C++,使其本身更加安全、高效的同时还天然支持并行进程。
可以说,go是为并发而生的! 既然它是为并发而生,那么在编写高并发程序时,go写起来也十分简洁。
目前流行的go应用
1、Go语言的杀手级应用就是Docker,Docker应该大家都知道,目前在国内火的一塌糊涂
2、Codis,一种Redis的集权解决管理方案,很大部分go开发,由豆瓣推出。
3、Glow,类似Hadoop,也是一种大数据处理框架,性能非常好,是Hadoop的go的实现。
4、Cockroach数据库,译作蟑螂……意味着该数据库的生存能力很强,是高稳定性业务环境的首选数据库之一……
go的开发环境搭建
可以选择源码安装、标准安装包(win下),第三方工具安装(brew,install,yum等)。
如果是win,就使用标准包。官网有下载地址。
推荐第三方工具安装,简单。
比如Mac下的Homerbrew
安装go
看安装成功之后的提示就能知道,go的运行需要一个环境变量——GOPATH,是go中非常重要的一个环境变量,必须配置好,假如不配,那么安装go的一些工具,或者自己写的包,就会出问题。当然还有GOROOT。
PS:其实在目前的go版本中,这些环境变量已经在安装的时候默认被配置ok了。只需要在机器的环境配置文件中做如下操作即可:
$GOPATH 目录约定有三个子目录:
- src 存放源代码(比如:.go .c .h .s,以及下载安装的一些工具包)
- pkg 编译后生成的文件(比如:.a)
- bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中)
下面查看go的环境变量配置,看看配置的情况如何了。
发现查看环境变量的是go env命令
发现GOROOT、GOPATH等环境变量确实已经在安装go 1.9 时,被默认设置。
然后,按照之前安装go成功之后的提示,在.bash_profile文件中进行如下配置:
最后,source下即可。
PS:各个环境变量含义
$GOROOT$GOARCH$GOOS$GOBIN$GOPATHgo get$GOPATH${GOPATH//://bin:}/bingo get
go的IDE
可以使用LiteIDE,为go量身打造。
熟悉Java的也可以直接使用eclipse或者intelli idea,都有相关插件,本人使用了eclipse。
当然,直接上VIM也是大有人在的。
hello word!
和Java一样,go定义包使用package,导入包使用import,程序入口也是main,函数声明使用func,同Java,Go也需要括号包住函数体,不同的是,语句的结束可以加分号,也可以不加,看个人习惯。比如从Java,C++来的,又是强迫症患者,那么一般都要写分号。如下:
运行go程序,可以使用build命令先编译连接源代码,生成可执行文件(程序文件名本身),方便以后随时拿来运行。
当然,图省事,直接使用go run命令执行也是ok的,但是这样不会生成任何中间结果。
有一个小坑,如果import的包不用,编译会报错。比如,fmt包导入了就必须使用,类似的包括变量等。当注释掉第8行代码,在执行会报错,这点严谨性,比Java强多了。
go的常用关键字(25个)
- var和const :变量和常量的声明
- var varName type 或者 varName : = value
- package and import: 导入
- func: 用于定义函数和方法
- return :用于从函数返回
- defer someCode :在函数退出之前执行
- go : 用于并行
- select 用于选择不同类型的通讯
- interface 用于定义接口
- struct 用于定义抽象数据类型
- break、case、continue、for、fallthrough、else、if、switch、goto、default 流程控制
- chan用于channel通讯
- type用于声明自定义类型
- map用于声明map类型数据
- range用于读取slice、map、channel数据
如上,其实和其他大部分语言都差不多。其中go、chan、以及select是go的特色和重点,又比如map类似Java的hashmap……其他的也没什么新鲜的,接下来快速过。
go程序的组织模式
go程序类似Java,都是通过package组织,然后每个独立的、可运行的go程序的入口必须都是main函数,也就是main包里的main函数。
变量和常量
使用var关键字
var是go最基本的定义变量方式 ,但是它的变量类型是放在了变量名后面。
如下,格式化打印语句
和C语言非常像,其实go就是C语言衍生改进而来,这也是为什么称它为互联网世界中的C语言的原因。
简洁的定义
有时候会觉得总是多写一个var比较烦,那么可以用:=来定义变量(个人感觉也不是想象中的那么简洁……)
这种方式类似Python,省去了写类型的步骤,在go程序中十分常见。但是这样的写法有一个缺点::= 无法用于函数体外部,而var可以。故var也是有存在的必要的。
eclipse直接提示变量y的定义错误,而变量x是ok的。
注意:
1、package和import必须挨着,也就是它们中间不能有其他代码,否则报错。
2、对于没有显式初始化的变量,Go语言总是将零值赋值给该变量。
3、在Go语言中,声明变量的时候,类型名总是在变量名的后面。
const常量
没什么可说的,和C语言一样的用法,唯一不同的就是定义常量时,加类型或者不加都可以,但是注意,最好养成习惯,类型加在常量名后面。
而且,字符串数据类型和Java不太一样,是完全小写的string。下面会说到。
注意:
constvar同理
数据类型
无类型的数值常量可以兼容go内置的任何类型的数值
type(value)
快速过一下,和Java对比,如下常用的几个:
无类型的数值常量可以兼容go内置的任何类型的数值
type(value)
快速过一下,和Java对比,如下常用的几个:
整型
1、go独有的 rune,其实就是代表了无符号32位整型 unit32。
2、有符号整型 int (依赖于os,可能是int32或者int64)
- int8 [-128, 127]
- int16 [-32768, 32767]
- int32 [-2147483648, 2147483647]
- int64 [-9223372036854775808, 9223372036854775807]
也就是说,go是见名知意,go中可以直接定义xxx位的整型。
3、字节 byte,等价于 uint8
4、无符号整型 uint ,依赖于不同os实现,可以是uint32或者uint64
- uint8 [0, 255]
- uint16 [0, 65535]
- uint32 [0, 4294967295]
- uint64 [0, 18446744073709551615]
- uintptr 一个可以恰好容纳指针值的无符号整型(对32位平台是uint32, 对64位平台是uint64)
unsafe.Sizeofsizeof
unsafe
浮点类型
1、float32 ,±3.402 823 466 385 288 598 117 041 834 845 169 254 40x1038 计算精度大概是小数点后7个十进制数
2、float64,±1.797 693 134 862 315 708 145 274 237 317 043 567 981x1038 计算精度大概是小数点后15个十进制数
布尔类型 bool(取值true false)
注意区分Java的boolean/Boolean写法,其实是和C语言一样,这里也说下,学习go,就对比着C语言和Java语言,这样用起来是很快的。
!bb
go可直接定义复数
这也是go独有的, complex32、complex64,在针对一些算法的实现时,非常方便,比如傅里叶变换。
complex32复数,实部和虚部都是float32
complex64复数,实部和虚部都是float64
字符串
常用是string定义,Go语言中的字符串是 UTF-8编码格式(当字符为 ASCII 码时则占用 1 个字节,其它字符根据需要占用 2-4 个字节)。
UTF-8 是被广泛使用的编码格式,是文本文件的标准编码,其它包括 XML 和 JSON 在内,也都使用该编码。由于该编码对占用字节长度的不定性,Go 中的字符串也可能根据需要占用 1 至 4 个字节,这与C++、Java 或者 Python 不同。
Go 这样做的好处是:不仅减少了内存和硬盘空间占用,同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码。
Go语言中字符串的可以使用双引号( " )或者反引号( ` )来创建。
双引号用来创建可解析的字符串,所谓可解析的是指字符串中的一些符号可以被格式化为其他内容,如 "\n" 在输出时候会被格式化成换行符, 如果需要按照原始字符输出必须进行转义。
反引号创建的字符串原始是什么样,那输出还是什么,不需要进行任何转义。
注意
1、Go语言中的部分转义字符 \\ 反斜线
\' 单引号
\" 双引号
\n 换行符
\uhhhh 4个16进制数字给定的Unicode字符
rune
格式化字符串
fmt.Printffmt.Prinfln
常用的格式化指令修饰符如下:
空白#%#o%#x
+-0
字符串处理包
strings
strconv "12345"int
这些直接查文档就可以了。
数组 array slice
array可以类比Python的元组,slice可以类比Python的列表,Java的list,很好理解。
在看下slice,其实它就是一个动态数组
还可以通过从一个数组或在一个已经存在的slice中再次声明slice
其实,现在的z就变为了动态数组slice,类似于指针。
同样slice也可以用简洁方式声明,并且配合关键字make去分配内存空间,make用于内建类型(map、slice 和channel)的内存分配,有些类似C语言的malloc。
小结
1、总的来说,如下图,可以从一个array声明slice,其实就是指针指向。
2、在Go语言中,字符串支持切片操作,但是需要注意的是:
如果字符串都是由ASCII字符组成,那可以随便使用切片进行操作
range
另外获取字符串的长度可能有两种含义,一种是指获取字符串的字节长度,一种是指获取字符串的字符数量。
3、字符串支持以下操作:
指针
通常情况下Go语言中的变量持有相应的值。也就是说,我们可以将一个变量想象成它所持有的值来使用。
其中有些例外:通道、函数、方法、map、切片slice 是引用变量,它们持有的都是引用,也即保存指针的变量。
在go中,值在传递给函数或者方法的时候会被复制一次,对于布尔类型和数值类型来说这so easy,但是对于大型变量却代价很大。而且复制值传参的方式,修改值只是修改了副本,这能保证原始变量不被修改,但也一定程度上增加了修改原始值的麻烦。(和C语言一样的道理,同样go也保留了C语言的指针)
Go语言中有指针,也没什么新鲜东西,同样在每次传递给函数或者方法的只是变量的内存地址,这是非常高效的。而且一个被指针指向的变量可以通过该指针来修改,这就很方便的在函数或者方法中通过指针修改原始变量。
&*
交换两个变量
map
类比Java的hashmap,在go中,常用于json解码等。map和Java的哈希表一样,必须申请内存,使用make,否则报错
执行出错,nil就是null,零值。
panic: assignment to entry in nil map
map和slice不一样,slice可以不make直接用,底层会做处理,但是map不行。
还是使用简洁写法爽,个人也推荐
流程控制
这个东西几乎每个语言都有,下面看常用的几个
if 条件
for 循环语句,两种用法
1、当做普通的for循环
2、当做while循环使用(因为go里没有while关键字)
注意
:=:=
:=for:=afor:=aa
switch case
如果想让switch语句在case执行后不中断,继续执行接下来的流程,可以使用fallthrough(穿透语句)
很重要的一个关键字 range
类似Java的迭代器,可以便捷的遍历容器
:=