第一阶段:Go 快速入门(上篇)
一、Go语言特性
1.1 Go语言介绍
Go 即 Golang,是 Google 公司 2009 年 11 月正式对外公开的一门编程语言。
根据 Go 语言开发者自述,近 10 多年,从单机时代的 C 语言到现在互联网时代的 Java,
都没有令人满意的开发语言,而 C++往往给人的感觉是,花了 100%的经历,却只有 60%的开发效率,产出比太低,Java 和 C#的哲学又来源于 C++。
并且,随着硬件的不断升级,这些 语言不能充分的利用硬件及 CPU。
因此,一门高效、简洁、开源的语言诞生了。
Go 语言不仅拥有静态编译语言的安全和高性能,而且又达到了动态语言开发速度和易 维护性。
有人形容 Go 语言:Go = C + Python , 说明 Go 语言既有 C 语言程序的运行速度,又能达到 Python 语言的快速开发。
Go 语言是非常有潜力的语言,是因为它的应用场景是目前互联网非常热门的几个领域比如 WEB 开发、区块链开发、大型游戏服务端开发、分布式/云计算开发。
国内比较知名的B 站就是用 Go 语言开发的,像 Goggle、阿里、京东、百度、腾讯、小米、360 的很多应用也是使用 Go 语言开发的。
1.2 Go语言解决的问题
多核硬件架构;
超大规模分布式计算集群;
Web 开发模式导致的前所未有的开发规模和更新速度。
二、Go 运行环境
确定go版本信息
1 | Go语言版本:go1.14.2 |
2.1 下载Golang Ide
https://www.jetbrains.com/go/download/#section=windows
1.liteIDE 运行速度快,代码提示特别好用,但是调试功能不太好用
2.VSCode 调试功能好用,但是代码提示非常一般,写起来特别费劲
3.GoLand 各项功能非常完善,但是是收费的,并且占用资源较多
2.2 配置
配置鼠标滚动修改字体大小
字体的更改方法: File -> Settings -> Editor -> Font -> Size, 推荐选18或者20
主题的更改方法: File -> Settings -> Editor -> Color Scheme -> Scheme, 推荐选Colorful Darcula
{width=”6.232062554680665in” height=”2.3583103674540684in”}
2.3 创建项目
{width=”6.179955161854768in” height=”1.7403915135608048in”}
2.4 解决部分包无法下载问题
使用golang 开发有时会遇到 golang unrecognized import path “golang.org/x” 之类的错误。
原因是无法访问golang.org网站去下载对应的文件。
如果使用的开发IDE是goland,那么 打开 FILE -> setting -> Go Modules 选项
在proxy 选项上填写 “ https://goproxy.io ,direct”
{width=”6.232061461067366in” height=”3.4838331146106736in”}
2.5 添加自动格式化工具
{width=”6.179955161854768in” height=”3.1264523184601924in”}
三、Go 基本语法
3.1 变量定义方法
1、var定义变量
var 变量名 类型 = 表达式
1 | var name string = "zhangsan" |
2、类型推导方式定义变量
- a 在函数内部,可以使用更简略的 := 方式声明并初始化变量。
- 注意:短变量只能用于声明局部变量,不能用于全局变量的声明
1 | // 变量名 := 表达式 |
3、一次定义多个变量
1 | package main |
4、批量声明变量
1 | package main |
3.2 常量定义
声明了 pi 和 e 这两个常量之后,在整个程序运行期间它们的值都不能再发生变化了。
1 | const pi = 3.1415 |
const 同时声明多个常量时,如果省略了值则表示和上面一行的值相同。
1 | const ( |
3.3 fmt包
Println:
- 一次输入多个值的时候 Println 中间有空格
- Println 会自动换行,Print 不会
Print:
- 一次输入多个值的时候 Print 没有 中间有空格 Print 不会自动换行
- Printf 是格式化输出,在很多场景下比 Println 更方便
Printf
- Printf 是格式化输出,在很多场景下比 Println 更方便
1 | package main |
3.4 Init函数和main函数
1、init函数
- go语言中init函数用于包(package)的初始化,该函数是go语言的一个重要特性。
有下面的特征:
init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等
每个包可以拥有多个init函数
包的每个源文件也可以拥有多个init函数
同一个包中多个init函数的执行顺序go语言没有明确的定义(说明)
不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序
init函数不能被其他函数调用,而是在main函数执行之前,自动被调用
2、main函数
1 | // Go语言程序的默认入口函数(主函数):func main() |
3、init函数和main函数的异同
相同点:
- 两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用。
不同点:
init可以应用于任意包中,且可以重复定义多个。
main函数只能用于main包中,且只能定义一个。
两个函数的执行顺序:
- 对同一个go文件的
init()
调用顺序是从上到下的。
- 对同一个go文件的
对同一个package中不同文件是按文件名字符串比较”从小到大”顺序调用各文件中的
init()
函数。- 对于不同的
package
,如果不相互依赖的话,按照main包中"先import
的后调用"的顺序调用其包中的init()
- 对于不同的
如果
package
存在依赖,则先调用最早被依赖的package
中的init()
,最后调用main
函数。- 如果
init
函数中使用了println()
或者print()
你会发现在执行过程中这两个不会按照你想象中的顺序执行。 - 这两个函数官方只推荐在测试环境中使用,对于正式环境不要使用
- 如果
4、init()函数介绍
- 在Go语言程序执行时导入包语句会自动触发包内部
init()
函数的调用。 - 需要注意的是:
init()
函数没有参数也没有返回值。 init()
函数在程序运行时自动被调用执行,不能在代码中主动调用它。- 包初始化执行的顺序如下图所示:
{width=”4.50667104111986in” height=”3.991431539807524in”}
3.5 golang中关键字
var和const
:变量和常量的声明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数据
3.6 命名规范
Go是一门区分大小写的语言。命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名。 Go 语言从语法层面进行了以下限定:任何需要对外暴露的名字必须以大写字母开头,不需要对外暴露的则应该以小写字母开头。当命名(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Analysize,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);命名如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )
包名称
- 保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库
不要冲突。包名应该为小写单词,不要使用下划线或者混合大小写。
- 保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库
package
domain
package
main
- 文件命名
- 尽量采取有意义的文件名,简短,有意义,应该为小写单词,使用下划线分隔各个单词。
approve_service.go
- 结构体命名
- 采用驼峰命名法,首字母根据访问控制大写或者小写struct 申明和初始化格式采用多行,例如
下面:
- 采用驼峰命名法,首字母根据访问控制大写或者小写struct 申明和初始化格式采用多行,例如
1 | type MainConfig struct { |
- 接口命名命名规则基本和上面的结构体类型单个函数的结构名以 “er” 作为后缀,例如 Reader ,
Writer 。- 命名以“er”结尾,如:Writer,xxxHandler,Helper,Manager等
- 接口方法声明 = 方法名+方法签名如:methodA (param1, param2)outputTypeList
1 | type Reader interface { |
- 变量命名
- 和结构体类似,变量名称一般遵循驼峰法,首字母根据访问控制原则大写或者小写
- 但遇到特有名词时,需要遵循以下规则:如果变量为私有,且特有名词为首个单词,则使用小
写如 appService - 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头
1 | var isExist bool |
- 常量命名常量均需使用全部大写字母组成,并使用下划线分词
- constAPP_URL
=
“ https://www.baidu.com “ - 如果是枚举类型的常量,需要先创建相应类型:
- constAPP_URL
1 | type Scheme string const ( |
四、基本数据类型
4.1 内置类型
1、值类型:
1 | bool |
2、引用类型:(指针类型)
1 | slice // 序列数组(最常用) |
4.2 内置函数
- Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。
- 它们有时可以针对不同的类型进行操作,例如:len、cap 和 append,或必须用于系统级的操作,例如:panic。
- 因此,它们需要直接获得编译器的支持。
1 | append // 用来追加元素到数组、slice中,返回修改后的数组、slice |
4.2 基本类型介绍
类型 | 长度(字节) | 默认值 | 说明 |
---|---|---|---|
bool | 1 | false | |
byte | 1 | 0 | uint8 |
rune | 4 | 0 | Unicode Code Point, int32 |
int, uint | 4或8 | 0 | 32 或 64 位 |
int8, uint8 | 1 | 0 | -128 ~ 127, 0 ~ 255,byte是uint8 的别名 |
int16, uint16 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 |
int32, uint32 | 4 | 0 | -21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名 |
int64, uint64 | 8 | 0 | |
float32 | 4 | 0.0 | |
float64 | 8 | 0.0 | |
complex64 | 8 | ||
complex128 | 16 | ||
uintptr | 4或8 | 以存储指针的 uint32 或 uint64 整数 | |
array | 值类型 | ||
struct | 值类型 | ||
string | "" | UTF-8 字符串 | |
slice | nil | 引用类型 | |
map | nil | 引用类型 | |
channel | nil | 引用类型 | |
interface | nil | 接口 | |
function | nil | 函数 |
五、数字
5.1 数字类型
1、Golang数据类型介绍
- Go 语言中数据类型分为:基本数据类型和复合数据类型
- 基本数据类型有:
- 整型、浮点型、布尔型、字符串
- 复合数据类型有:
- 数组、切片、结构体、函数、map、通道(channel)、接口
2、整型分为两大类
- 有符号整形按长度分为:int8、int16、int32、int64
- 对应的无符号整型:uint8、uint16、uint32、uint64
{width=”6.232061461067366in” height=”4.323268810148732in”}
- 关于字节:
- 字节也叫 Byte,是计算机数据的基本存储单位。8bit(位)=1Byte(字节) 1024Byte(字节)=1KB
- 1024KB=1MB 1024MB=1GB
- 1024GB=1TB 。在电脑里一个中文字是占两个字节的。
5.2 数字定义
1、定义数字类型
1 | package main |
2、reflect.TypeOf查看数据类型
1 | package main |
5.3 布尔值
- Go 语言中以 bool 类型进行声明布尔型数据,布尔型数据只有 true(真)和 false(假)两个值。
- 注意:
- 1.布尔类型变量的默认值为 false。
- 2.Go 语言中不允许将整型强制转换为布尔型.
- 3.布尔型无法参与数值运算,也无法与其他类型进行转换。
1 | package main |
六、字符串
6.1 字符串
1、字符串
- Go 语言里的字符串的内部实现使用 UTF-8 编码。
- 字符串的值为双引号(“)中的内容,可以在 Go 语言的源码中直接添加非 ASCII 码字符
1 | s1 := "hello" |
2、多行字符串
- 反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。
1 | package main |
3、byte和rune
- Go 语言的字符有以下两种
1 | uint8类型,或者叫 byte 型:代表了ASCII码的一个字符。 |
- 字符串底层是一个byte数组,所以可以和[]byte类型相互转换。
- 字符串是不能修改的 字符串是由byte字节组成,所以字符串的长度是byte字节的长度。
- rune类型用来表示utf8字符,一个rune字符由一个或多个byte组成。
1 | package main |
6.2 字符串的常用操作
方法 | 介绍 |
---|---|
len(str) | 求长度 |
+或fmt.Sprintf | 拼接字符串 |
strings.Split | 分割 |
strings.Contains | 判断是否包含 |
strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 |
strings.Index(),strings.LastIndex() | 子串出现的位置 |
strings.Join(a[]string, sep string) | join操作 |
1、len(str)
1 | package main |
2、**+(拼接)**
1 | package main |
3、strings.Split()
1 | package main |
4、strings.Join()
1 | package main |
5、单引号
- 组成每个字符串的元素叫做“字符”,可以通过遍历字符串元素获得字符,字符用单引号(’)
- uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符
- rune 类型,代表一个 UTF-8 字符
1 | package main |
6.3 字符串遍历
1、遍历字符串
1 | package main |
2、修改字符串
- 要修改字符串,需要先将其转换成[]rune 或[]byte,完成后再转换为 string。
- 无论哪种转换,都会重新分配内存,并复制字节数组。
- 将“美国第一”改成“中国第一”
1 | package main |
6.4 转String
strconv
{width=”6.190001093613298in” height=”5.740001093613298in”}
6.5 string与int转换
1 | package main |
七、数组 Array
7.1 数组介绍
1、Array介绍
- 数组是指一系列同一类型数据的集合 。
- 数组中包含的每个数据被称为数组元素(element),这种类型可以是任意的原始类型,比如 int、
string 等 - 一个数组包含的元素个数被称为数组的长度。
- 在 Golang 中数组是一个长度固定的数据类型,数组的长度是类型的一部分,也就是说 [5]int 和
[10]int 是两个不同的类型 。 - Golang中数组的另一个特点是占用内存的连续性,也就是说数组中的元素是被分配到连续的内存地
址中的,因而索引数组元素的速度非常快。 - 和数组对应的类型是 Slice(切片),Slice 是可以增长和收缩的动态序列,功能也更灵活
- 但是想要理解 slice 工作原理的话需要先理解数组,所以本节主要为大家讲解数组的使用。
{width=”6.232061461067366in” height=”3.5745767716535433in”}
2、数组定义
1 | var 数组变量名 [元素数量]T |
- 比如:var a [5]int, 数组的长度必须是常量,并且长度是数组类型的一部分
- 一旦定义,长度不能变。 [5]int 和[4]int 是不同的类型。
1 | package main |
7.2 数组的遍历
1、普通遍历数组
1 | package main |
2、k,v遍历数组
1 | package main |
八、切片 Sice
8.1 切片基础
1、切片的定义
- 切片(Slice)是一个拥有相同类型元素的可变长度的序列。
- 它是基于数组类型做的一层封装。
- 它非常灵活,支持自动扩容。
- 切片是一个引用类型,它的内部结构包含地址、长度和容量。
- 声明切片类型的基本语法如下:
1 | // var name []T |
1 | package main |
- 切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。
- 切片唯一合法的比较操作是和 nil 比较。 一个 nil 值的切片并没有底层数组,一个 nil 值的切片的长
度和容量都是 0。 - 但是我们不能说一个长度和容量都是 0 的切片一定是 nil
- 例如下面的
2、切片的本质
- 切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切
片的容量(cap)。 - 举个例子,现在有一个数组 a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片 s1 := a[:5],相应示意图如下。
{width=”6.232062554680665in” height=”6.558185695538057in”}
- 切片 s2 := a[3:6],相应示意图如下
3、切片的长度和容量
- 切片拥有自己的长度和容量,我们可以通过使用内置的 len()函数求长度,使用内置的 cap()函数求
切片的容量。 - 切片的长度就是它所包含的元素个数。
- 切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
- 切片 s 的长度和容量可通过表达式 len(s) 和 cap(s) 来获取。
1 | import "fmt" |
8.2 切片循环
切片的循环遍历和数组的循环遍历是一样的
1、基本遍历
1 | package main |
2、k,v遍历
1 | package main |
8.3 append()
Go 语言的内建函数 append()可以为切片动态添加元素,每个切片会指向一个底层数组这个数组的容量够用就添加新增元素。
当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行”扩容”,此时该切片指向的底层数组就会更换。
“扩容”操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收 append 函数的返回值。
1、append添加
1 | package main |
2、append追加多个
1 | package main |
3、切片中删除元素
Go 语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素
1 | package main |
4、切片合并
1 | package main |
九、Map 字典
9.1 map介绍
map 是一种无序的基于 key-value 的数据结构,Go 语言中的 map 是引用类型,必须初始化才能使用。
Go 语言中 map 的定义语法如下:
map[KeyType]ValueType
其中
KeyType:表示键的类型。
ValueType:表示键对应的值的类型。
map 类型的变量默认初始值为 nil,需要使用 make()函数来分配内存。
其中 cap 表示 map 的容量,该参数虽然不是必须的。
- 注意:获取 map 的容量不能使用 cap, cap 返回的是数组切片分配的空间大小, 根本不能用于map。
- 要获取 map 的容量,可以用 len 函数。
9.2 定义map
1 | package main |
9.3 map基本使用
1、判断某个键是否存在
1 | package main |
2、delete()函数
- 使用 delete()内建函数从 map 中删除一组键值对,delete()函数的格式如下:delete(map 对象, key)
- 其中,
- map 对象:表示要删除键值对的 map 对象
- key:表示要删除的键值对的键
1 | package main |
9.4 map遍历
1、遍历key和value
1 | package main |
2、只遍历Key
注意: 遍历 map 时的元素顺序与添加键值对的顺序无关
1 | package main |
十、指针
10.1 关于指针
要搞明白 Go 语言中的指针需要先知道 3 个概念:指针地址
、指针类型
、指针取值
指针地址(&a)指针取值(*&a)
指针类型(&a) —> *int 改变数据传指针
变量的本质是给存储数据的内存地址起了一个好记的别名。
- 比如我们定义了一个变量 a := 10 ,这个时候可以直接通过 a 这个变量来读取内存中保存的 10 这个值。
- 在计算机底层 a 这个变量其实对应了一个内存地址。
- 指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的
内存地址。 - Go 语言中的指针操作非常简单,我们只需要记住两个符号:&(取地址)和 *(根据地址取值)
1 | package main |
{width=”6.383173665791776in” height=”6.266369203849519in”}
10.2 &取变量地址
1、&符号取地址操作
1 | package main |
2、b := &a 的图示
{width=”6.179955161854768in” height=”2.376104549431321in”}
10.3 new 和 make
1、执行报错
- 执行下面的代码会引发 panic,为什么呢?
- 在 Go 语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否
则我们的值就没办法存储。 - 而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。
- 要分配内存,就引出来今天的 new 和 make。
- Go 语言中 new 和 make 是内建的两个函数,主要用来分配内存。
1 | package main |
2、make和new比较
- new 和 make 是两个内置函数,主要用来创建并分配类型的内存。
- make和new区别
make
关键字的作用是创建于 slice、map 和 channel 等内置的数据结构new
的作用是为类型申请一片内存空间,并返回指向这片内存的指针
1 | package main |
3、new函数
一:系统默认的数据类型,分配空间
1 | package main |
二:自定义类型使用 new 函数来分配空间
1 | package main |
4、make函数
- make 也是用于内存分配的,但是和 new 不同,它只用于 chan、map 以及 slice 的内存创建
- 而且它返回的类型就是这三个类型本身,而不是他们的指针类型
- 因为这三种类型就是引用类型,所以就没有必要返回他们的指针了
1 | package main |
- 当我们为slice分配内存的时候,应当尽量预估到slice可能的最大长度
- 通过给make传第三个参数的方式来给slice预留好内存空间
- 这样可以避免二次分配内存带来的开销,大大提高程序的性能。