目录
1. Go语言介绍
Go语言是什么
2009年11月10日,Go语言正式成为开源编程语言家庭的一员。
Go语言(或称Golang)是云计算时代的C语言。Go语言的诞生是为了让程序员有更高的生产效率,Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。
开发人员在为项目选择语言时,不得不在快速开发和性能之间做出选择。C和C++这类语言提供了很快的执行速度,而 Ruby 和 Python 这类语言则擅长快速开发。Go语言在这两者间架起了桥梁,不仅提供了高性能的语言,同时也让开发更快速。
Go语言优势
- 不需要额外的依赖库,部署简单,就是扔一个文件上去就完成了
- 静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高
- 语言层面支持并发,这个就是Go最大的特色,天生的支持并发。Go就是基因里面支持的并发,可以充分的利用多核,很容易的使用并发
- 内置runtime,支持垃圾回收,这属于动态语言的特性之一吧,虽然目前来说GC(内存垃圾回收机制)不算完美,但是足以应付我们所能遇到的大多数情况,特别是Go1.1之后的GC
- 丰富的标准库,Go目前已经内置了大量的库,特别是网络库非常强大。
- 内置强大的工具,Go语言里面内置了很多工具链,最好的应该是gofmt工具,自动化格式化代码,能够让团队review变得如此的简单,代码格式一模一样,想不一样都很困难。
- 跨平台编译,如果你写的Go代码不包含cgo,那么就可以做到window系统编译linux的应用,如何做到的呢?Go引用了plan9的代码,这就是不依赖系统的信息。
- 内嵌C支持,Go里面也可以直接包含C代码,利用现有的丰富的C库。
Go适合用来做什么
- 服务器编程,以前你如果使用C或者C++做的那些事情,用Go来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。
- 分布式系统,数据库代理器等。
- 网络编程,这一块目前应用最广,包括Web应用、API应用、下载应用。
- 内存数据库,如google开发的groupcache,couchbase的部分组建。
- 云平台,目前国外很多云平台在采用Go开发,CloudFoundy的部分组建,前VMare的技术总监自己出来搞的apcera云平台。
2. Go环境搭建
- 这里选择windows环境安装
对于Windows用户,Go语言提供两种安装方式(源码安装除外):
- MSI安装:程序会自动配置你的安装
- ZIP安装:需要你手动设置一些环境变量
地址:Downloads - The Go Programming Language
安装提示自动完成安装,且会自动加入到环境变量中
- 验证是否成功安装
通过Sublime 编译运行go程序
可以通过sublime编写和运行go代码,或者通过LiteIDE, LiteIDE是一款简单,开源,跨平台的 Go IDE。
官网:LiteIDE X,发行版下载地址:liteide - Browse Files at SourceForge.net
- 这里通过sublime编写go程序,选择如下
配置文件内容如下
{ "cmd": ["go", "run", "$file_name"], "file_regex": "^[ ]*File \"(…*?)\", line ([0-9]*)", "working_dir": "$file_path", "selector": "source.go" }
ctrl+s,改名保存
再选择默认编译项
直接按住“ctrl+b”运行
学习资料
- Go语言官网(需要翻墙):https://golang.org/
- go中文社区:首页 - Go语言中文网 - Golang中文社区
- go中文在线文档:Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国
3. 第一个Go程序
新建文件hell.go
//go语言以包作为管理单位,每个文件必须先声明包,程序必须有个main包
package main
import (
"fmt"
)
//程序的入口,程序从main函数开始执行
func main() { //这里左括号不能换行
fmt.Println("Hello Go!") //go语句结尾没有分号
}
- 代码分析
每个Go源代码文件的开头都是一个package声明,表示该Go代码所属的包。包是Go语言里最基本的分发单位,也是工程管理中依赖关系的体现。
要生成Go可执行程序,必须建立一个名字为main的包,并且在该包中包含一个叫main()的函数(该函数是Go可执行程序的执行起点)。
Go语言的main()函数不能带参数,也不能定义返回值。
在包声明之后,是一系列的import语句,用于导入该程序所依赖的包。由于本示例程序用到了Println()函数,所以需要导入该函数所属的fmt包。
所有Go函数以关键字func开头。一个常规的函数定义包含以下部分:
func 函数名(参数列表)(返回值列表) {
// 函数体
}
Go程序的代码注释与C++保持一致,即同时支持以下两种用法:
- /* 块注释 */ // 行注释
-
/* 块注释 */ // 行注释
Go程序并不要求开发者在每个语句后面加上分号表示语句结束,这是与C和C++的一个明显不同之处。
注意:
- 强制左花括号{的放置位置,如果把左花括号{另起一行放置,这样做的结果是Go编译器报告编译错误。
- 如果在 LiteIDE中编写代码,则一个文件夹中只能有一个main函数不然直接运行会报错,但是通过命令行go run 运行则不会
通过命令行运行程序
go build xx.go //此命令只会编译代码生成exe程序,但是不会运行exe程序 go run xx.go //会运行,但是不会生成exe程序(实际上在运行的时候能通过任务管理器看到xx.exe)
4. 基础语法
4.1 数据类型
数据类型的作用:告诉编译器这个数(变量)应该多大的内存储存
命名规则
Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。大写字母和小写字母是不同的:heapSort和Heapsort是两个不同的名字。
Go语言中类似if和switch的关键字有25个(均为小写)。关键字不能用于自定义名字,只能在特定语法结构中使用。
此外,还有大约30多个预定义的名字,比如int和true等,主要对应内建的常量、类型和函数。
变量
变量相当于是对一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名来使用这块存储空间。
- 变量声明
使用var声明变量,需标注数据类型
var 变量名 类型
var v1 int
var v2 int
//一次定义多个变量
var v3, v4 int
var (
v5 int
v6 int
)
- 变量声明了要使用(比如println输出),否则报错
- 只是声明但没有初始化的变量,默认为0
- 变量初始化
就是声明变量时同时赋值, var关键字可以保留,但不再是必要的元素,如下所示:
var v1 int = 10 // 方式1 var v2 = 10 // 方式2,编译器自动推导出v2的数据类型 v3 := 10 // 方式3,编译器自动推导出v3的数据类型
- 变量多重赋值
即同时对多个变量赋值
package main
import ("fmt")
func main() {
//a := 10
//b := 20
//c := 30
a, b, c := 10, 20, 30 //多重赋值
fmt.Println(a,b,c)
}
- 变量值交换
package main
import ("fmt")
func main() {
//传统的两个变量值交换的方法
var a int = 10
var b int = 20
var tmp int
tmp = a
a = b
b = tmp
fmt.Println(a,b) //输出 20 10
//go语言中特有的方法
i, j := 100, 200
i, j = j, i //把j赋值给i,把i赋值给j
fmt.Println(i,j) //输出 200 100
}
- 自动推导数据类型和赋值的区别
以:=方式赋值的变量不能再以相同的方式重新赋值,否则报错‘no new variables on left side of :=’
package main
import ("fmt")
func main() {
var a int //可以无限次重复赋值
a = 10
a = 20
c := 100
//c := 200 //以:=方式赋值的变量不能再重新赋值,否则报错‘no new variables on left side of :=’
//c = 200,但是可以这样重新赋值
fmt.Println(a,c) //输出 20 100
}
- 匿名变量
_(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃,配合函数来使用才有优势
package main
import ("fmt")
func main() {
i := 10
j := 20
tmp,_ := i,j //相当于将j的值丢弃
fmt.Println(tmp) //输出10
}
- 多个变量的定义
可以用如下简便的方式对多个不同类型的变量进行定义,定义的同时也可以赋值
package main
import ("fmt")
func main() {
//不同类型变量的声明
//var a int
//var b float64
var ( //新写法
a int = 1
b float64 = 3.14
//a = 1 自动推导类型
//b = 3.14
)
fmt.Println("a",a)
fmt.Println("b",b)
}
打印输出
- Println:自带换行功能
- Printf:可以进行格式化输出
package main import ("fmt") func main() { fmt.Println("hello world") //自动加换行 a := 10 b := 20 fmt.Printf("a = %d, b = %d\n",a,b) // %d 以为字符方式打印、 // %c 以字符方式打印 // %s 以字符串方式打印 // %f 以浮点方式打印 // %T 查看变量类型 fmt.Println("hello world") }
常量
- 变量:程序运行期间,可以改变的量,变量声明需要var
- 常量:程序运行期间,不可以改变的量,常量声明需要const
常量可以是数值类型(包括整型、浮点型和复数类型)、布尔类型、字符串类型等。
package main import ("fmt") func main() { const a = 10 //或者const a int = 10 //a = 20 //常量不允许修改 //const a := 10 //报错,常量不能使用:= fmt.Println(a) }
iota枚举
常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。
在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加1。
package main import ("fmt") func main() { const ( x = iota y = iota z = iota w // 这里隐式地说w = iota,因此w == 3。其实上面y和z可同样不用"= iota" ) fmt.Printf(" x = %d, y = %d, z = %d",x,y,z) //输出 x = 0, y = 1, z = 2 const ( a = iota b,c,d = iota,iota,iota //b,c,d在同一行值相同 e = iota ) fmt.Printf(" a = %d, b = %d, c = %d, d = %d, e = %d",a,b,c,d,e) //输出 a = 0, b = 1, c = 1, d = 1, e = 2 }
基础数据类型分类
Go语言内置以下这些基础类型:
类型 | 名称 | 长度 (字节) | 零值 (初始值) | 说明 |
bool | 布尔类型 | 1 | false | 其值不为真即为家,不可以用数字代表true或false |
byte(字符类型,放英文字符) | 字节型 | 1 | 0 | uint8别名 |
rune(放中文字符) | 字符类型 | 4 | 0 | 专用于存储unicode编码,等价于uint32 |
int, uint | 整型 | 4或8 | 0 | 32位或64位 |
int8, uint8 | 整型 | 1 | 0 | -128 ~ 127, 0 ~ 255 |
int16, uint16 | 整型 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 |
int32, uint32 | 整型 | 4 | 0 | -21亿 ~ 21 亿, 0 ~ 42 亿 |
int64, uint64 | 整型 | 8 | 0 | |
float32 | 浮点型 | 4 | 0.0 | 小数位精确到7位 |
float64 | 浮点型 | 8 | 0.0 | 小数位精确到15位 |
complex64 | 复数类型 | 8 | ||
complex128 | 复数类型 | 16 | ||
uintptr | 整型 | 4或8 | ⾜以存储指针的uint32或uint64整数 | |
string | 字符串 | "" | utf-8字符串 |
- 布尔类型
// 1. 声明变量 var a bool a = true fmt.Println("a = ", a) // a = true // 2. 自动推导类型 var b = false fmt.Println("b = ", b) // b = false // 3. c := false fmt.Println("c = ", c) // c = false
- 整型
就是整数类型
var a int a = 123 b := 234 fmt.Printf("a = %d , b = %d" ,a , b)
- 浮点型
就是小数类型
var a float32 a = 12 b := 12.0 // 如果不加小数点, fvalue2会被推导为整型而不是浮点型 var c = 3.14 fmt.Printf("a = %f, b = %f, c = %f\r", a, b, c) //a = 12.000000 , b = 12.000000 fmt.Println(a, b, c) //输出 12 12 3.14
- 字符类型
在Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名),代表utf-8字符串的单个字节的值;另一个是rune,代表单个unicode字符
ps:字符需单引号包裹,如a = 'b'。且只能有一个字符,转义字符除外
var ch1, ch2, ch3 byte //声明字符类型 ch1 = 'a' //字符赋值 ch2 = 97 //字符的ascii码赋值 fmt.Printf("ch1 = %c, ch2 = %c", ch1, ch2) //ch1 = a, ch2 = a, ch3 =
- 字符串
在Go语言中,字符串也是一种基本类型:
var str1 string str1 = "abc" fmt.Printf("ch1 = %s\n",str1) //ch1 = abc //自动推导类型 str2 := "like" fmt.Printf("str2 属于'%T'类型\n",str2) //str2 属于'string'类型 //获取字符串长度 fmt.Println("len(str2) = ",len(str2)) //len(str2) = 4
- 复数类型
复数实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部(real),一个表示虚部(imag)。
fmt包的格式化输出输入
有时候需要在一个很长的字符串中添加一个变量。则需要用到格式化输出法。上面讲到的printf就带格式化输出功能,现在来重点讲一下printf
格式 | 含义 |
%% | 一个%字面量 |
%b | 一个二进制整数值(基数为2),或者是一个(高级的)用科学计数法表示的指数为2的浮点数 |
%c | 字符型。可以把输入的数字按照ASCII码相应转换为对应的字符 |
%d | 一个十进制数值(基数为10) |
%e | 以科学记数法e表示的浮点数或者复数值 |
%E | 以科学记数法E表示的浮点数或者复数值 |
%f | 以标准记数法表示的浮点数或者复数值 |
%g | 以%e或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
%G | 以%E或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
%o | 一个以八进制表示的数字(基数为8) |
%p | 以十六进制(基数为16)表示的一个值的地址,前缀为0x,字母使用小写的a-f表示 |
%q | 使用Go语法以及必须时使用转义,以双引号括起来的字符串或者字节切片[]byte,或者是以单引号括起来的数字 |
%s | 字符串。输出字符串中的字符直至字符串中的空字符(字符串以'\0‘结尾,这个'\0'即空字符) |
%t | 以true或者false输出的布尔值 |
%T | 使用Go语法输出的值的类型 |
%U | 一个用Unicode表示法表示的整型码点,默认值为4个数字字符 |
%v | 使用默认格式输出的内置或者自定义类型的值,或者是使用其类型的String()方式输出的自定义值,如果该方法存在的话 |
%x | 以十六进制表示的整型值(基数为十六),数字a-f使用小写表示 |
%X | 以十六进制表示的整型值(基数为十六),数字A-F使用小写表示 |
a := 15 b := "abc" c := 'a' d := 3.14 //获取变量类型,字符类型本质上是整型 fmt.Printf("%T,%T,%T,%T\n",a,b,c,d) //int,string,int32,float64 //输出变量 fmt.Printf("a = %d, b = %s, c = %c, d = %f\n", a, b, c, d) //a = 15, b = abc, c = a, d = 3.140000 //%v自动匹配格式输出,但并不是很智能,下面将字符类型输出为整型了 fmt.Printf("a = %v, b = %v, c = %v, d = %v", a, b, c, d) //a = 15, b = abc, c = 97, d = 3.14
键盘输入
程序运行时,从键盘获取用户输入
var a int fmt.Printf("请输入变量a:") //阻塞等待用户的输入,将输入赋值给变量a fmt.Scan(&a) fmt.Println("a = ", a)
类型转换
Go语言中不允许隐式转换,所有类型转换必须显式声明,而且转换只能发生在两种相互兼容的类型之间。
var flag bool flag = true fmt.Printf("flag = %t\n",flag) //输出flag = true //bool类型不能转换为int,int类型也不能转换为bool,这种不能转换的类型叫不兼容类型 //fmt.Printf("flag = %d\n",flag) //输出flag = %!d(bool=true) var ch byte = 'a' //将ch转换为int类型,再赋值给a var a int = int(ch) fmt.Println("a = ", a) //97 fmt.Printf("类型为:%T",int(ch)) //int
类型别名
可以给数据类型如int、bool、byte取一个别名
type 别名 变量类型
package main import ("fmt") func main() { type bigint int64 //给int64取一个别名 var a bigint //等价于 var a int 64 fmt.Printf("a的类型为%T\n", a) //a的类型为main.bigint //同时给多个类型定义别名 type ( long int64 bo bool ) var b long = 11 var c bo = true fmt.Printf("b = %d, c = %t\n", b, c) //b = 11, c = true }
4.2 运算符
算术运算符
运算符 | 术语 | 示例 | 结果 |
+ | 加 | 10 + 5 | 15 |
- | 减 | 10 - 5 | 5 |
* | 乘 | 10 * 5 | 50 |
/ | 除 | 10 / 5 | 2 |
% | 取模(取余) | 10 % 3 | 1 |
++ | 后自增,没有前自增 | a=0; a++ | a=1 |
-- | 后自减,没有前自减 | a=2; a-- | a=1 |
关系运算符
运算符 | 术语 | 示例 | 结果 |
== | 相等于 | 4 == 3 | false |
!= | 不等于 | 4 != 3 | true |
< | 小于 | 4 < 3 | false |
> | 大于 | 4 > 3 | true |
<= | 小于等于 | 4 <= 3 | false |
>= | 大于等于 | 4 >= 1 | true |
逻辑运算符
运算符 | 术语 | 示例 | 结果 |
! | 非 | !a | 如果a为假,则!a为真;如果a为真,则!a为假。 |
&& | 与 | a && b | 如果a和b都为真,则结果为真,否则为假。 |
|| | 或 | a || b | 如果a和b有一个为真,则结果为真,二者都为假时,结果为假。 |
位运算符
位运算符用的不多,所以就不介绍
运算符 | 术语 | 说明 | 示例 |
& | 按位与 | 参与运算的两数各对应的二进位相与 | 60 & 13 结果为12 |
| | 按位或 | 参与运算的两数各对应的二进位相或 | 60 | 13 结果为61 |
^ | 异或 | 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1 | 60 ^ 13 结果为240 |
<< | 左移 | 左移n位就是乘以2的n次方。 左边丢弃,右边补0。 | 4 << 2 结果为16 |
>> | 右移 | 右移n位就是除以2的n次方。 右边丢弃,左边补位。 | 4 >> 2 结果为1 |
赋值运算符
运算符 | 说明 | 示例 |
= | 普通赋值 | c = a + b 将 a + b 表达式结果赋值给 c |
+= | 相加后再赋值 | c += a 等价于 c = c + a |
-= | 相减后再赋值 | c -= a 等价于 c = c - a |
*= | 相乘后再赋值 | c *= a 等价于 c = c * a |
/= | 相除后再赋值 | c /= a 等价于 c = c / a |
%= | 求余后再赋值 | c %= a 等价于 c = c % a |
<<= | 左移后赋值 | c <<= 2 等价于 c = c << 2 |
>>= | 右移后赋值 | c >>= 2 等价于 c = c >> 2 |
&= | 按位与后赋值 | c &= 2 等价于 c = c & 2 |
^= | 按位异或后赋值 | c ^= 2 等价于 c = c ^ 2 |
|= | 按位或后赋值 | c |= 2 等价于 c = c | 2 |
其他运算符
运算符 | 术语 | 示例 | 说明 |
& | 取地址运算符 | &a | 变量a的地址 |
* | 取值运算符 | *a | 指针变量a所指向内存的值 |
4.3 流程控制
Go语言支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构。
- 顺序结构:程序按顺序执行,不发生跳转。
- 选择结构:依据是否满足条件,有选择的执行相应功能。
- 循环结构:依据条件是否满足,循环多次执行某段代码。
选择结构
if语句
package main import ("fmt") func main() { //if a := 123 if a == 123{ //左括号和if在同一行 fmt.Println("if") } //if支持一个初始语句,初始化语句和判断条件以分号分隔 if a := 10; a == 10{ //把初始化和条件判断写在一起 fmt.Println("a == 10") } //if else if a := 20; a == 10{ fmt.Println("a == 10") }else{ fmt.Println("a == 20") } //if esle if else if a := 30; a == 10{ fmt.Println("a == 10") }else if a == 30{ fmt.Println("a == 30") }else{ fmt.Println("ok") } }
switch语句
Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch,但是可以使用fallthrough强制执行后面的case代码
package main import ("fmt") func main() { num := 4 switch num{ //switch后面写的是变量本身 //switch num := 4; num { //支持一个初始化语句,初始化语句和变量本身,以分号分隔 //switch { //可以不加变量 //case num > 10: //条件写在case这里 case 1: fmt.Println("1") case 2: fmt.Println("2") case 3,4,5: fmt.Println("3") default: fmt.Println("ok") } }
循环结构
go语言中循环有for和range语句,没有while和do while语句。
for
// for 初始化条件; 判断条件; 条件变化{ // } package main import ("fmt") func main() { //计算1+2+3 ... 100累加 sum := 0 for i:=1; i<=100; i++{ sum += i } fmt.Println("sum = ", sum) //1050 }
死循环
i := 0 for { i++ .... }
range
关键字 range 会返回两个值,第一个返回值是元素的数组下标,第二个返回值是元素的值
package main
import ("fmt")
func main() {
str := "abcdef"
//迭代打印每个元素的值,默认返回两个值:一个是元素的位置i,一个是元素本身data
for i, data := range str {
fmt.Printf("str[%d]=%c\n", i, data)
}
fmt.Println("------------------")
//也可以这样写,第2个返回值默认丢弃,只返回元素的位置
for i := range str {
//for i, _ := range str { 使用匿名变量的方式
fmt.Printf("str[%d]=%c\n", i, str[i])
}
}
跳转语句
break和continue
在循环里面有两个关键操作break和continue
- break:跳出整个循环
- continue:跳过本次循环,接着继续执行下次循环
package main
import ("fmt")
func main() {
for i := 0; i < 5; i++ {
if 2 == i {
//break //break操作是跳出整个循环,即结束循环
continue //continue是跳过本次循环,然后继续下一次循环
}
fmt.Println(i)
}
}
goto
goto用于跳转,goto可以用在任何地方,但是必须在函数内部。
package main
import ("fmt")
func main() {
fmt.Println("1111111111111111111")
goto End //goto是关键字,End是用户起的名字,也叫标签
fmt.Println("222222222222222222")
End:
fmt.Println("333333333333333333")
}