go语言简介
Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。
go语言特点
- 背靠大厂,google背书,可靠
- 天生支持并发(最显著特点)
- 语法简单,容易上手
- 内置runtime,支持垃圾回收
- 可直接编译成机器码,不依赖其他库
- 丰富的标准库
- 跨平台编译
go语言的应用领域
- 服务器编程
- 开发云平台
- 区块链
- 分布式系统
- 网络编程
那些公司(项目)在使用go语言
- Google
- k8s
- Facebook
- facebookgo
- 腾讯
- 蓝鲸平台
- 容器技术
- 百度
- 运维项目BFE
- 京东
- 消息推送系统、云存储、京东商城
- 小米
- 运维监控系统、小米互娱、小米商城、小米视频、小米生态链
- 360
- 日志搜索系统Poseidon
go环境安装
在官网 Go:https://golang.org/dl/ 安装过程中记得把添加到环境变量那个给勾上
1 | go version |
go开发工具
- 1.vim
- 2.sublime
- 3.atom
- 4.LiteIDE
- 5.eclipse
- 6.goland
- 7.vscode
go常用命令
命令 | 描述 |
---|---|
go bug | 启动一个用于报告bug的工具。 |
go build | 编译Go程序包及其依赖项。 |
go clean | 删除编译生成的对象文件和缓存文件。 |
go doc | 显示有关包或符号的文档。 |
go env | 打印有关Go环境的信息。 |
go fix | 更新包以使用新的API。 |
go fmt | 使用gofmt重新格式化Go包的源代码。 |
go generate | 通过处理源代码来生成Go文件。 |
go get | 将依赖项添加到当前模块并安装它们。 |
go install | 编译并安装包及其依赖项。 |
go list | 列出包或模块的信息。 |
go mod | 用于模块维护,包括初始化模块、添加和更新依赖项等。 |
go work | 用于工作区维护,例如查看、清理或打印工作区信息。 |
go run | 编译并运行Go程序。 |
go test | 运行包的测试。 |
go tool | 运行指定的Go工具。 |
go version | 打印Go的版本信息。 |
go vet | 检查 Go 源码并报告可疑的错误。 |
Go语言中的标识符、关键字和命名规范
</a>一、标识符
标识符,通俗的讲,就是给变量、常量、函数、方法、结构体、数组、切片、接口起名字
1.1 标识符的组成
在 Go 语言中,标识符的组成和其他编程语言类似,如下:
- 标识符有字母、数字和下划线组成
- 标识符只能以字母或下划线开头,不能以数字开头
- 标识符区分大小写
二、关键字
关键字字,就是在Go语言中内置定义了一些标识符,这些标识符已经存在固定的含义了,在自定义变量名时不能再使用关键字了,Go语言中的关键字包含25个基本关键字和36个预定义标识符
2.1 25个基本关键字
break | default | func | interface | select |
---|---|---|---|---|
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
2.2 36个预定义标识符
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
---|---|---|---|---|---|---|---|---|
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
三、命名规范
命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名
3.1 公有私有的命名规范
-
当命名(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:GetUserName,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);
-
命名如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )
3.2 包名称命名规范
- 保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库不要冲突。包名应该为小写单词,不要使用下划线或者混合大小写
如:
package user
package service
3.3 文件命名规范
- 尽量采取有意义的文件名,简短,有意义,应该为小写单词,使用下划线分隔各个单词。
如:
user_api.go
3.4 结构体命名规范
结构体命名规范采用驼峰法,首字母根据是否提供外部使用决定是否使用大小写
type CustomerOrder struct {
Name string
Address string
}
3.5 接口命名规范
接口命名规范也是采用驼峰法,此外单个函数的结构名以 “er” 作为后缀,例如 Reader , Writer
type Reader interface {
Read(p []byte) (n int, err error)
}
3.6 变量名命名规范
变量名称一般遵循驼峰法,首字母根据访问控制原则大写或者小写,但遇到特有名词时,需要遵循以下规则:
- 如果变量为私有,且特有名词为首个单词,则使用小写,如 appService
- 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头
如:
var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool
3.7 常量命名规范
常量均需使用全部大写字母组成,<并使用下划>并使用下划>线分词
const APP_URL = "https://www.baidu.com"
3.8 异常处理的规范
错误处理的原则就是不能丢弃任何有返回err的调用,不要使用 _ 丢弃,必须全部处理。接收到错误,要么返回err,或者使用log记录下来尽早return:一旦有错误发生,马上返回,尽量不要使用panic,除非你知道你在做什么,错误描述如果是英文必须为小写,不需要标点结尾,采用独立的错误流进行处理
// 错误写法
if err != nil {
// 错误处理
} else {
// 正常代码
}
// 正确写法
if err !=
nil {
// 错误处理
return // 或者继续
}
// 正常代码
3.9 单元测试规范
单元测试文件名命名规范为 example_test.go 测试用例的函数名称必须以 Test 开头,例如:TestExample 每个重要的函数都要首先编写测试用例,测试用例和正规代码一起提交方便进行回归测试 。
</div><div><div></div></div>
<link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-98b95bb57c.css" rel="stylesheet">
<link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-c216769e99.css" rel="stylesheet">
</div>
如何编写go代码
1 | package user |
1 | package main |
声明变量
Go语言中的变量需要声明后才能使用,同一作用域内不支持重复声明。 并且Go语言的变量声明后
声明变量的语法
1 | var identifier type |
- var :声明变量关键字
- identifier:变量名称
- type :变量类型
例如
1 | package main |
变量的初始化
语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认
值,例如:整型和浮点型变量的默认值为0
。字符串变量的默认值为空字符串””。布尔型变量默认为false
。
切片、函数、指针变量的默认为nil
变国初始化语法
1 | var变量名类现=表达式 |
例如
1 | package main |
类型推导
我们在声明变量时,可以根据初始化值进行类型推导,从而省略类型。
1 | package main |
初始化多个变量
可以一次性多个变量,中间用逗号分隔
1 | package main |
短变量声明
在函数内部,可以用:=运算符对变量进行声明和初始化。
1 | package main |
注意这种方式只适合在函数内部,函数外部不能使用
匿名变量
如果我们接收多个变量,有一些变量用不到,可以使用下划线_表示变量名称,这种变量叫做匿名变量。例如
1 | package main |
语言常量
常量,就是在程序编译阶段就确定下来的值,而程序在运行时则无法改变该值。在G。程序中,常量可以是数值类
型(包括整型、浮点型和复数类型)I布尔类型、字符串类型等。
定义常量的语法
定义一个常量使用const关键字,语法格式如下:
1 | const constantName (type]= value |
const
:定义常量关键字
constantName
:常量名称
type
:常量类型
value
:常量的值
1 | package main |
iota
iota比较特殊,可以被认为是一个可被编译器修改的常量,它默认开始值是0,每调用一次加1遇到
const
关
键字时被重置为0.
实例
1 | package main |
运行结果
1 | 0 |
使用-
跳过某些值
1 | package main |
运行结果
1 | a1: 0 |
iota
声明中间插队
1 | package main |
运行结果
1 | a1: 0 |
go语言数据类型
在 Go 编程语言中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小,不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
Go 语言按类别有以下几种数据类型:
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量true或者false。一个简单的例子:var b bool = true。 |
2 | 数字类型整型mt和浮点型float32、float64, G。语言支持整型甲浮点型数字,并且支持复数,其中位的 运算采用补码 |
3 | 字符串类型:字符串就是一串固定长度的字符连接起来的字符序列。G。的字符串是由单个字节连接起来 的。G。语言的字符串的字节使用@e8编码标识Unicode文本 |
4 | 派生类型:包括:⑵指针类型(Pointer) (b)数组类型(c)结构化类型(struct)(d) Channel类型(e)函数类型 ⑴切片类型(g)接口类型(interface) (h) Map类型 |
数字类型
Go也有基于架构的类型,例如:int、Gnt和uintptr
序号 | 类型和描述 |
---|---|
1 | uint8 无符号8位8理(0到255) |
2 | uint16无符号16位整型(0到65535) |
3 | uint32 无符号 32 位整型(0 到 4294967295) |
4 | uint64 无符号 64 位整型(0 到 18446744073709551615) |
5 | int8有符号8位整型(-128到127) |
6 | int16有符号16位整型(-32768到32767) |
7 | int32有符号 32 位整型(-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型(-9223372036854775808 到 9223372036854775807) |
浮点型
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点里数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32位实数和虚数 |
4 | complex128 64位实数和虚数 |
其他数字类型
以下列出了其他更多的雅字类型:
序号 | 类型和描述 |
---|---|
1 | byte 类似 uint8 |
2 | rune类型int32 |
3 | uint32或64位 |
4 | int与uint一样大小 |
5 | uintptr无符号整型,用于存放一个指针 |
语言布尔类型
go 语言中的布尔类型有两个常量值:true和false.布尔类型经常用在条件判断语句,或者循环语句,也可以用在逻辑表达式中。
布尔类型
1 | package main |
运行结果
1 | b1: true |
go语言数字类型
Go语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用不H马。
Go也有基于架构的类型,例如:int
、uint
和uintptr
。
这些类型的长度都是根据运行程序所在的操作系统类型所决定的:
int
和uint
在32位操作系统上,它们均使用32位(4个字节),在64位操作系统上,它们均使用64位(8个字节)。uintptr
的长度被设定为足够存放一个指针即可。
Go语言中没有float
类型。(Go语言中只有float32
和float64
)没有double类型。
与操作系统架构无关的类型都有固定的大小,并在类型的名称中就可以看出来:
整数:
- jnt8 (-128 -> 127)
- intl6 (-32768 -> 32767)
- int32 (-2,147,483,648 -> 2,147,483,647)
- int64 (-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)
无符号整数:
- uint8 (0 -> 255)
- uintl6 (0 -> 65,535)
- Uint32 (0 -> 4,294,967,295)
- uint64 (0 -> 18,446,744,073,709,551,615)
浮点型(IEEE-754标准):
- float32 (+- le-45 -> +-3.4 * le38)
- float64 (+- 5 le-324 -> 107 le308)
int类型是计算最快的一种类型
整型的零值是0,浮点型的零值是0.0
实例
下面实例演示了,各个数字类型的长度和取值范围
1 | package main |
运行结果
1 | int 8B -128~127 |
以二进制.八进制或十六进制浮点数的格式定义数字
1 | package main |
运行结果
1 | 10 |
浮点型
GO语言支持两种浮点型数:float32
和float64
。这两种浮点型数据格式遵循IEEE 754
标准:float32
的浮
点数的最大范围约为3.4e38
,可以使用常量定义:math.MaxFloat32
。float64
的浮点数的最大范围约为1.8e308
,可以使用一个常量定义:math.MaxFloat64
打印浮点数时,可以使用fmt
包配合动词%f ,代码如下:
1 | package main |
复数
complex64 和 complex128
1 | package main |
复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。
golang字符串
Go语言字符串是任意字节常量序号
go语言字符串字面量
在GO语言中,字符串字面量使用双引号 或者反引号 来创建.双引号用来创建可解析的字符串,支持转义,
但不能用来引用多行;反引号用来创建原生的字符串字面量,可能由多行组成,但不支持转义,并且可以包含除了
反引号外其他所有字符.双引号创建可解析的字符串应用最广泛,反引号用来创建原生的字符串则多用于书写多行
消息,HTML以及正则表达式
1 | package main |
运行结果
1 | str1: hello world |
GO语言字符串连接
使用加号
虽然Go语言中的字符串是不可变的,但是字符串支持+级联操作和+二追加操作,例如:
1 | package main |
golang里面的字符串都是不可变的,每次运算都会产生一个新的字符串,所以会产生很多临时的无用的字符
串,不仅没有用,还会给gc带来额外的负担,所以性能比较差
使用fmt.Sprintf()
函数
1 | package main |
运行结果
1 | msg: tom,20 |
内部使用[]byte实现,不像直接运算符这种产生很多临时的字符串,但是内部的逻辑比较复杂,有很多额外的判断,还用到了interface,所以性能也不是很好
strings.Join()
1 | package main |
join会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填
入,在已有一个数组的情况下,这种效率会很高,但是本来没有,去构造这个数据的代价也不小
buffer.WriteString()
1 | package main |
这个比较理想,可以当成可变字符使用,对内存的增长也有优化,如果能预估字符串的长度,还可以用 buffer.Grow()接口来设置
capacity
语言字符串转义字符
Go语言的字符串常见专义符包含回车、换行、单双引号、制表符等,如下表所示。
转义符 | 含义 |
---|---|
\r | 回车符(返回行首) |
\n | 换行符(直接跳到下一行的同列位置) |
\t | 制表符 |
\’ | 单引号 |
\” | 双引号 |
\\ | 反斜杠 |
GO语言字符串切片操作
1 | package main |
运行结果
1 | 108 |
go语言字符串常用方法
方法 | 介绍 |
---|---|
len (str) | 求长度 |
+或fmt.Sprintf | 拼接字符串 |
strings.Split | 分割 |
strings.Contains | 判断是否包含 |
strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 |
strings.Index(),strings.LastIndex() | 子串出现的位置 |
strings.Join(a[]string,sep string) | join操作 |
实例
1 | package main |
运行结果
1 | strings.Split(s, ""): [h e l l o w o r l d !] |
golang格式化输出
下面实例使用到的结构体
1 | type Website struct { |
占位符
- 普通占位符
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%v | 相应值的默认格式.在打印结构体时.”加号”标汜(%+v)会添加字段名 | fmt.Printf(“%v”, site) fmt.Printf(“%+v”, site) | duoke360} {Name:duoke360 |
%#v | 相应值和类型 | fmt.Printf(“%#v”, site) | main.Webslte{Name:”duoke360”} |
%T | 相应值的类型 | fmt.Printf(“%T”, site) | main.Webslte |
%% | 字面上的百分号,并非值的占位符 | fmt.Printf(“%%”) | % |
- 布尔占位符
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%t | 单词true和false | fmt.Printf(“%t”, true) | true |
- 整数占位符
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%b | 二进制 | fmt.Printf(“%b”, 5) | 101 |
%c | 相应的Unicode码 | fmt.Printf(“%c”, 0x4E2D) | 中 |
%d | 十进制 | fmt.Printf(“%d”, 0x12) | 18 |
%o | 八进制 | fmt.Printf(“%o”, 10) | 12 |
%q | 单引号围绕的字符字面值,由go语言安全的专义 | fmt.Printf(“%q”, 0x4E2D) | ‘中’ |
%x | 十六进制,字母形式为小写 a-f | fmt.Printf(“%x”, 13) | d |
%X | 十六进制,字母形式为大写 A-F | fmt.Printf(“%X”, 13) | D |
%U | Unicode格式,U+1234,等同于”U+%04X” | fmt.Printf(“%t”, true) | U+4E2D |
- 浮点数和复数的组成部分(实部和虚部)
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%b | 无小数部分的,指数为2的幂的科学计数法,与strconv.FormatFloat的’b’转换格式一致例如-123456p-78 | ||
%e | 科学计数法,例如-1234.456e+78 | fmt.Printf(“%e”, 10.2) | 1.020000e+01 |
%E | 科学计数法,例如-1234.456E+78 | fmt.Printf(“%E”, 10.2) | 1.020000E+01 |
%f | 有小数点而无指数,例如123.456 | fmt.Printf(“%f”, 10.2) | 10.200000 |
%g | 根据情况选择%e或%f以产生更紧凑的(无末尾的0) | fmt.Printf(“%g”, 10.20) | 10.2 |
%G | 根据情况选择%E或%f以产生更紧凑的(无末尾的0) | fmt.Printf(“%G”, 10.20+2i) | 10.20+2i |
- 字符串与字节切片
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%s | 输出字符串表示(string类型或[]byte) | fmt.Printf(“%s”, []byte(“多课网”)) | 多课网 |
%q | 双引号围绕的字符串,由Go语法安全地转义 | fmt.Printf(“%q”, “多课网”) | “多课网” |
%x | 十六进制,小写字母,每字节两个字符 | fmt.Printf(“%x”, “golang”) | 676f6c616e67 |
%X | 十六进制,小写字母,每字节两个字符 | fmt.Printf(“%X”, “golang”) | 676F6C616E67 |
- 指针
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%p | 十六进制表示,前缀θx | fmt.Printf(“%p”,&site) | 0x4f57f0 |
GO语言运算符
Go语言内置的运算符有:
- 1.算术运算符
- 2.关系运算符
- 3.逻辑运算符
- 4.位运算符
- 5.赋值运算符
算术运算符
运算符 | 描述 |
---|---|
+ | 相加 |
- | 相减 |
* | 相乘 |
/ | 相除 |
% | 取余 |
注意: ++
(自增) 和--
(自减) 在Go语言中是单独的语句,并不是运算符。
实例
1 | package main |
关系运算符
运算符 | 描述 |
---|---|
== | 检查两个值是否相等,如果相等返回True否则返回False。 |
!= | 检查两个值是否不相等,如果不相等返回True否则返回False. |
> | 检查左边值是否大于右边值,如果是返回True否则返回False。 |
>= | 检查左边值是否大于等于右边值,如果是返回True否则返回False。 |
< | 检查左边值是否小于右边值,如果是返回True否则返回False。 |
<= | 检查左边值是否小于等于右边值,如果是返回True否则返回False。 |
实例
1 | package main |
逻辑运算符
运算符 | 描述 | ||
---|---|---|---|
&& | 逻辑AND运算符。如果两边的操作数都是True,则为True,否则为False。 | ||
\ | \ | 逻辑OR运算符。如果两边的操作数有一个True,则为True,否则为False。 | |
! | 逻辑NOT运算符。如果条件为True,则为False,否则为True。 |
实例
1 | package main |
位运算符
位运算符对整数在内存中的二进制位进行操作。
运算符 | 描述 | |
---|---|---|
& | 参与运算的两数各对应的二进位相与。(两位均为1才为1)。 | |
\ | 参与运算的两数各对应的二进位相或。(两位有一 个为1就为1)。 | |
^ | 参与运算的两数各对应的_进位相异或,当两对应的二进位相异时,结果为1。(两位不- 样则为。 | |
<< | 左移n位就是乘以2的n次方。“a<<b”是把a的各 二进位全部左移b位,高位丢弃,低位补0。 | |
>> | 右移n位就是除以2的n次方。“a>>b”是把a的各二 进位全部右移b位。 |
实例
1 | package main |
赋值运算符
运算符 | 描述 | |
---|---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 | |
+= | 相加后再赋值 | |
-= | 相减后再赋值 | |
*= | 相乘后再赋值 | |
/= | 相除后再赋值 | |
%= | 求余后再赋值 | |
<<= | 左移后赋值 | |
>>= | 右移后赋值 | |
\ | = | 按位与后赋值 |
^= | 按位异或后赋值 |
实例
1 | package main |
go语言中的流程控制
go语言中的条件
条件语句是用来判断给定的条件是否满足(表达式值是否为true或者false ),并根据判断的结果(真或假)决定执行的语句,go语言中的条件语句也是这样的。
go语言中的条件语句包含如下几种情况
- if语句: if语句由一个布尔表达式后紧跟一 个或多个语句组成。
- …else 语句: if语句后可以使用可选的else 语句,else 语句中的表达式在布尔表达式为false 时执行
- if 嵌套语句:你可以在if或else if语句中嵌入一个或多个if或else if语句。
- switch 语句: switch 语句用于基于不同条件执行不同动作。
- select 语句: select语句类似于switch
语句,但是select会随机执行一个可运行的case.如果没有case可运行,它将阻塞,直到有case可运行。
go语言中的循环语句
go语言中的循环只有for循环,去除了while、do while 循环,使用起来更加简洁。
- for循环。
- for range循环。
go语言中的流程控制关键字
- break
- continue
- goto
go语言if语句语法
1 | if布尔表达式{ |
注意:在go语言中布尔表达式不用使用括号。
go语言if语句实例演示
1 | package main |
运行结果
1 | flag is true |
初始变量可以声明在布尔表达式里面, 注意它的作用域
1 | package main |
注意:不能使用0或非0表示真假
1 | func test4() { |
go语言if语句使用提示:
- 1.不需使用括号将条件包含起来
- 2.大括号{}必须存在,即使只有一行语句
- 3.左括号必须在if或else的同- -行
- 4.在if之后,条件语句之前,可以添加变量初始化语句,使用;进行分隔
golang中的if else语句
go语言中的if else语句可以根据给定条件二选一。
go语言的if else语句语法
1 | if 布尔表达式 { |
go语言if else语句实例
1 | package main |
特殊语法
1 | package main |
go语言if语句使用提示:
- 1.不需使用括号将条件包含起来
- 2.大括号{}必须存在,即使只有一行语句
- 3.左括号必须在if或else的同一行
- 4.在if之后,条件语句之前,可以添加变量初始化语句,使用:进行分隔
- 5.在有返回值的函数中,最终的return不能在条件语句中
golang中的if else if语句
go语言if语句可以进行多重嵌套使用,进行多重判断。
go语言中的if else if语法
1 | if 布尔表达式1 { |
go语言中的if else if语法实例
1 | package main |
特殊语法
1 | package main |
golang switch语句
go语言中的switch
语句,可以非常容易的判断多个值的情况。
go语言中switch语句的语法
1 | switch var1 { |
go语言中switch语句实例
1 | package main |
多条件匹配
1 | package main |
case可以是条件表达式
1 | package main |
fallthrough
可以可以执行满足条件的下一个case
1 | package main |
运行结果
1 | 100 |
golang for循环语句
go语言中的for
循环,只有for
关键字,去除了像其他语言中的while
和do while
.
go语言for循环语法
1 | for 初始语句;条件表达式;结束语句{ |
注意: for表达式不用加括号
go语言for循环实例
1 | package main |
初始条件,可以写到外面
1 | package main |
初始条件和结束条件都可以省略
1 | package main |
这种情况类似其他语言中的while
循环
1 | package main |
for循环可以通过break
、goto
、 return
、 panic
语句强制退出循环
golang for range循环
Go语言中可以使用for range
遍历数组、切片、字符串、map 及通道(channel) 。通过for range
遍历的返回值有以下规律:
- 1.数组、切片I字符串返回索弓和值。
- 2.map返回键和值。
- 3.通道(channel) 只返回通道内的值。
go语言for range实例
1 | package main |
golang流程控制关键字break
break
语句可以结束for
、switch
和select
的代码块。
go语言使用break注意事项
- 1.单独在
select
中使用break和不使用break没有啥区别。 - 2.单独在表达式
switch
语询,并且没有fallthough
,使用break
和不使用break
没有啥区别。 - 3.单独在表达式
switch
语句,并且有fallthough
,使用break
能够终止fallthough
后面的case
语句的执行 - 4.带标签的
break
,可以跳出多层select
/switch
作用域。让break
更加灵活,写法更加简单灵活,不需要使用控制变量- -层-
层跳出循环,没有带break
的只能跳出当前语句块。
go语言break关键字实例
1 | package main |
golang关键字continue
continue
只能用在循环中,在go中只能用在for
循环中,它可以终止本次循环,进行下一-次循环。
在continue
语句后添加际签时,表示开始标签对应的循环。
go语言continue实例
1 | package main |
golang流程控制关键字goto
goto
语句通过标签进行代码间的无条件跳转。goto
语句可以在快速跳出循环、避免重复退出上有-定的帮助。Go语言中使用goto语句能简化一些代码的实现过程。
例如双层嵌套的for循环要退出时:
go语言关键字goto实例
1 | package main |
golang数组
数组是相同数据类型的一组数据的集合,数组-定义长度不能修改,数组可以通过下标(或者叫索引)来访问元素。
go语言数组的定义
1 | var variable_ name [SIZE] variable_type |
1 | package main |
使用初始化列表
1 | package main |
运行结果
1 | a: [3]int |
使用初始化列表,就是将值写在大括号里面。
省略数组长度
数组长度可以省略,使用
...
代替,加初始化值得数量自动推断,例如:
1 | package main |
运行结果
1 | a: [3]int |
指定索引值的方式来初始化
可以通过指定所有的方式来初始化,未指定所有的默认未零值。
1 | package main |
运行结果
1 | a: [3]int |
go语言访问数组元素
可以通过下标的方式,来访问数组元索。数组的最大下标为数组长度1,于这个下标会发生数组越界。
1 | package main |
运行结果
1 | a[0]: %!V(int=100) |
根据数组长度遍历数组
可以根据数组长度,通过for循环的方式来遍历数组,数组的长度可以使用len函数获得。
实例
1 | package main |
运行结果
1 | 1 |
使用for range
数组
还可以使用for range
循环来遍历数组, range返回数组 下标和对应的值
实例
1 | package main |
golang切片
前面我们学习了数组,数组是固定长度,可以容纳相同数据类型的元素的集合。当长度固定时,使用还是带来一些限制,比如:
我们申请的长度太大浪费内存,太小又不够用。
鉴于.上述原因,我们有了go语言的切片,可以把切片理解为,可变长度的数组,其实它底层就是使用数组实现的,增加了自动扩容功能。切片(
Slice) 是一个拥有相同类型元素的可变长度的序列。
go语言切片的语法
声明一个切片和声明一个数组类似,只要不添加长度就可以了
1 | var identifier []type |
go语言切片实例
1 | package main |
运行结果
1 | names: [] |
go语言切片的长度和容量
切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。
实例
1 | package main |
运行结果
1 | names: [%!V(string=tom) %!V(string=kite)] |
golang切片的初始化
切片的初始化方法很多,可以直接初始化,也可以使用数组初始化等。
直接初始化
1 | package main |
使用数组初始化
1 | package main |
使用数组的部分元素初始化(切片表达式)
切片的底层就是一个数组, 所以我们可以基于数组通过切片表达式得到切片。切片表达式中的low和high表示一个索引范围(
左包含,右不包含),得到的切片 长度=high-low,容量等于得到的切片的底层数组的容量。
1 | package main |
运行结果
1 | s1: [3 4 5] |
空(nil)切片
一个切片在末初始化之前默认为nil,长度为0,容量为0.
1 | package main |
运行结果
1 | true |
go语言切片的遍历
切片的遍历和数组的遍历非常类似,可以使用for循环索弓|遍历,或者for range循环。
for循环索引遍历
1 | package main |
运行结果
1 | s1[0]: 1 |
for range循环
1 | package main |
运行结果
1 | s1[0]: 1 |
go语言切片元素的添加和删除copy
切片是一个动态数组,可以使用append()函数添加元素,go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。由于,切片是引用类型,通过赋值的方式,会修改原有内容,go提供了
copy()函数来拷贝切片
添加元素
1 | package main |
运行结果
1 | s1: [1 2 3 4 5] |
删除元素
1 | package main |
运行结果
1 | s1: [1 2 4 5] |
公式:要从切片a中删除索引为index
的元素,操作方法是a = append(a[ :index],a[index+1:]...)
修改元素
1 | package main |
运行结果
1 | s1: [1 100 3 4 5] |
查询元素
1 | package main |
运行结果
1 | 3 |
拷贝切片
1 | package main |
运行结果
1 | s1: [100 2 3] |
golang map
map是一种key:value
键值对的数据结构容器。map内部实现是哈希表( hash
)。
map最重要的一点是通过key来快速检索数据,key 类似于索引,指向数据的值。
map是引用类型的。
map的语法格式
可以使用内建函数make也可以使用map关键字来定义map
1 | /*声明变量,默认map是nil */ |
map_variable
: map变量名称key_data_type
: key的数据类型value_data_type
:值得数据类型
实例
1 | package main |
运行结果
1 | m1: map[age:20 name:tom] |
访问map
1 | package main |
判断某个键是否存在
go语言中有个判断map中键是否存在的特殊写法,格式如下:
1 | value,ok := map[key] |
1 | package main |
运行结果
1 | m2: tom,true |
go语言遍历map
可以使用for range
循环进行map遍历,得到key和value值。
遍历key
1 | package main |
运行结果
1 | name |
遍历key和value
1 | package main |
运行结果
1 | age 20 |
golang函数
函数的go语言中的一级公民, 我们把所有的功能单元都定义在函数中,
可以重复使用。函数包含函数的名称、参数列表和返回值类型,这些构成了函数的签名(signature) 。
go语言中函数特性
- 1.go语言中有3种函数:普通函数、匿名函数(没有名称的函数)、方法(定义在struct.上的函数)。
- 2.go语言中不允许函数重载(overload), 也就是说不允许函数同名。
- 3.go语言中的函数不能嵌套函数,但可以嵌套匿名函数。
- 4.函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
- 5.函数可以作为参数传递给另一个函数。
- 6.函数的返回值可以是一个函数。
- 7.函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
- 8.函数参数可以没有名称。
go语言中函数的定义和调用
函数在使用之前必须先定义,可以调用函数来完成某个任务。函数可以重复调用,从而达到代码重用。
go语言函数定义语法
1 | func function. name( [parameter list] ) [return_types] |
语法解析:
- func :函数由func 开始声明
- function. name :函数名称,函数名和参数列表-起构成了函数签名。
- [parameter list] :参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return. types :返回类型,函数返回- -列值。 return. types 是该列值的数据类型。有些功能不需要返回值,这种情况下return.
- types不是必须的。
- 函数体:函数定义的代码集合。
go语言函数定义实例
定义一个求和函数
1 | func sum(a int, b int) (ret int) { |
定义一个比较两个数大小的函数
1 | func compare(a int, b int) (max int) { |
golang函数的返回值
函数可以有0或多个返回值,返回值需要指定数据类型,返回值通过return关键字来指定。
return可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。go中的函数可以有多个返回值。
1.return 关键字中指定了参数时,返回值可以不用名称。如果return省略参数,则返回值部分必须带名称
2.当返回值有名称时,必须使用括号包围,逗号分隔,即使只有一个返回值
3.但即使返回值命名了,return 中也可以强制指定其它返回值的名称,也就是说return的优先级更高
4.命名的返回值是预先声明好的, 在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变量重复定义
5.return中可以有表达式,但不能出现赋值表达式,这和其它语言可能有所不同。例如return a+b是正确的,但return c=a+b 是错误的。
go语言函数返回值实例
没有返回值
1 | func f1() { |
有一个返回值
1 | func sum(a int,b int) (ret int) { |
多个返回值,且在return中指定返回的内容
1 | func f2() (name string, age int) { |
多个返回值,返回值名称没有被使用
1 | func f3() (name string, age int) { |
return覆盖命名返回值,返回值名称没有被使用
1 | func f4() (name string, age int) { |
Go中经常会使用其中一个返回值作为函数是否执行成功、是否有错误信息的判断条件。
例如return value, exists、return value,ok、 return value,err
等。当函数的返回值过多时,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。例如,同类型的返回值可以放进slice中,不同类型的返回值可以放进map中。
但函数有多个返回值时,如果其中某个或某几个返回值不想使用,可以通过下划线_来丢弃这些返回值。
例如下面的f1函数两个返回值,调用该函数时,丢弃了第二个返回值b,只保留了第一个返回值a赋值给 了变量a。
1 | package main |
运行结果
1 | 2 |
golang函数的参数
go语言函数可以有0或多个参数,参数需要指定数据类型。
声明函数时的参数列表叫做形参,调用时传递的参数叫做实参。
go语言是通过传值的方式传参的,意味着传递给函数的是拷贝后的副本,所以函数内部访问、修改的也是这个副本。
go语言可以使用变长参数,有时候并不能确定参数的个数,可以使用变长参数,可以在函数定义语句的参数部分使用ARGS…
TYPE的方式。这时会将… 代表的参数全部保存到一个名为ARGS的slice中,注意这些参数的数据类型都是TYPE。
go语言函数的参数实例
1 | package main |
演示参数传递,按值传递
1 | func f1(a int) { |
运行结果
1 | a1: 200 |
从运行结果可以看到,调用函数1后,a的值并没有被改变,说明参数传递是拷贝了一个副本,也就是拷贝了1份新的内容进行运算。
map、slice、 interface、 channel
这些数据类型本身就是指针类型的, 所以就算是拷贝传值也是拷贝的指针,拷贝后的参数仍然指向底层数据结构,所以修改它们可能会影响外部数据结构的值。
1 | package main |
输出结果
1 | a1: [200] |
golang函数类型与函数变量
可以使用type
关键字来定义一个函数类型,语法格式如下:
上面语句定义了一个fun函数类型,它是一种函数类型, 这种函数接收两个int类型的参数并且返回一个int类型的返回值。
下面我们定义两个这样结构的两个函数,一个求和,一个比较大小
1 | func sum(a int, b int) int { |
下面定义一个fun函数类型,把sum和max赋值给它
1 | package main |
运行结果
1 | 3 |
golang高阶函数
go语言的函数,可以作为函数的参数,传递给另外一个函数,可以可以作为,另外一个函数的返回值返回。
go语言函数作为参数
1 | package main |
运行结果
1 | tom |
go语言函数作为返回值
1 | package main |
运行结果
1 | 6 |
golang匿名函数
go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现一下简单功能调用。
所谓匿名函数就是,没有名称的函数。
语法格式如下:
1 | fubc (参 数列表)(返回值) |
当然可以既没有参数,可以没有返回值
匿名函数实例
1 | package main |
运行结果
1 | 2 |
golang闭包
闭包可以理解成定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。或者说是函数和其引用环境的组合体。
闭包指的是一个函数和与其相关的引用环境组合而成的实体。 简单来说,闭包=函 数+引用环境。首先我们来看一个例子:
1 | package main |
运行结果
1 | 10 |
变量f
是一个函数并且它引用了其外部作用域中的x变量,此时f就是一个闭包。在f的生命周期内,变量x也一直有效。闭包进阶示例1:
闭包其实并不复杂,只要牢记闭包=雨数+引用环境
。
golang递归
函数内部调用函数自身的函数称为递归函数。
使用递归函数最重要的三点:
- 1.递归就是自己调用自己。
- 2.必须先定义函数的退出条件,没有退出条件,递归将成为死循环。
- 3.go语言递归函数很可能会产性一大堆的goroutine, 也很可能会出现栈空间内存溢出问题。
go语言递归实例
1 | package main |
运行结果
1 | 120 |
斐波那契数列
它的计算公式为f(n)=f(n-1)+f(n-2)且f(2)=f(1)=1
1 | package main |
1 | 5 |
golang defer语句
go语言中的defer
语句会将其后面跟随的语句进行延迟处理。在defer
归属的函数即将返回时,将延迟处理的语句按defer
定义的
逆序进行执行,也就是说,先被defer
的语句最后被执行,最后被defer
的语句,最先被执行。
defer特性
- 1.关键字defer用于注册延迟调用。
- 2.这些调用直到return 前才被执。因此,可以用来做资源清理。
- 3.多个defer语句,按先进后出的方式执行。
- 4.defer 语句中的变量,在defer声明时就决定了。
defer用途
- 1.关闭文件句柄
- 2.锁资源释放
- 3.数据库连接释放
go语言defer语句实例
1 | package main |
运行结果
1 | start |
golang init函数
golang有一个特殊的函数init
函数,先于main
函数执行,实现包级别的一些初始化操作。
init函数的主要特点
- init函数先于mpin函数自动执行,不能被其他函数调用;
- init函数没有输入参数、 返回值;
- 每个包可以有多个init函数;
- 包的每个源文件也可以有多个init函数,这点比较特殊;
- 同一个包的init执行顺序, golang没有明确定义,编程时要注意程序不要依赖这个执行顺序。
- 不同包的init函数按照包导入的依赖关系决定执行顺序。
golang初始化顺序
初始化顺序:变量初始化->init()->main()
实例
1 | package main |
运行结果
1 | initValue |
golang指针
Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个 指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。
类型指针不能进行偏移和运算。
Go语言中的指针操作非常简单,只需要记住两个符号: &
(取地址) 和*
(根据地址取值) 。
指针地址和指针类型
每个变量在运行时都拥有一个地址,
这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行取地址操作。Go语言中的值类型(int、 float. bool.
string、 array、 struct)
都有对应的指针类型,如: *int、 *int64、 *string
等。
指针语法
一个指针变量指向了一个值的内存地址。(也就是我们声明了一 个指针之后,可以像变量赋值一样,把一个值的内存地址放入到指针当中。)
类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:
1 | var var_name *var-type |
- var-type :为指针类型
- var_name :为指针变量名
- *:用于指定变量是作为一个指针。
指针声明实例
1 | var ip *int /*指向整型*/ |
指针使用实例
1 | package main |
运行结果
1 | a变量的地址是: c00000a0d8 |
golang指向数组的指针
定义语法
1 | var ptr [MAX]*int; |
实例演示
1 | package main |
1 | [<nil> <nil> <nil>] |
golang类型定义和类型别名
在介绍结构体之前,我们先来看看什么是类型定义和类型别名。
go语言类型定义
类型定义的语法
1 | type NewType Type |
实例.
1 | package main |
运行结果
1 | i: 100 i: main.MyInt |
go语言类型定义和类型别名的区别
- 1.类型定义相当于定义了一个全新的类型,与之前的类型不同;但是类型别名并没有定义一个新的类型,而是使用一个别名来替换之前的类型
- 2.类型别名只会在代码中存在,在编译完成之后并不会存在该别名
- 3.因为类型别名和原来的类型是一致的,所以原来类型所拥有的方法,类型别名中也可以调用,但是如果是重新定义的一个类型,那么不可以调用之前的任何方法