Golang开山篇
Golang的学习方向
区块链研发工程师,
Go服务器端/游戏软件工程师,
Golang分布式/云计算软件工程师
Golang的应用领域
区块链的应用开发
分布式账本技术: 去中心化, 公开透明
后台服务器应用
云计算/云服务后台应用
学习方法介绍
先整体框架, 再细节
工科
做中学
适当囫囵吞枣
讲课方式说明
通俗易懂
兼顾技术细节
Golang的概述
什么是程序
Go语言诞生的小故事
Go语言的核心开发团队-三个大牛
肯.汤姆森
罗布.派克
Google创造go语言的原因
计算机硬件更新频繁, 目前主流语言不能利用多核CPU
缺乏一个足够简单高效的编程语言
c/c++编译速度慢, 内存泄漏等困扰
Golang的发展历程
2007年三大创始人开始设计
2009年11月10日, 开源
2015年8月19日, 1.5发布, 移除最后残余的C代码
…
Golang的语言特点
静态编译语言的安全和性能, 动态语言开发维护的高效率, 结合
Go = C + Python
保留了指针, 弱化的指针
引入包的概念, 一个文件都要归属于一个包
垃圾回收机制, 内存自动回收
天然并发(重要)
- 从语言层面支持并发, 实现简单
- goroutine, 轻量级线程, 可实现大并发处理, 高效利用多核心
- 基于CPS并发模型实现
- 吸收了管道通信机制, 形成Go语言特有的管道channel, 实现不同goroutine之间相互通信
- 函数可以多返回值
- 新的创新, 比如切片slice, 延时执行defer
Golang开发工具介绍
Vscode, Goland
Win搭建开发环境
Linux搭建开发环境
Mac下搭建开发环境
Go语言快速开发入门
需求
编写hello.go文件, hello world
目录结构
找到gopath目录, 在src下建github.com目录, 下面是你的github用户名, 在下面hello是项目名, main.go是代码
% go env
GOPATH="/Users/admin/go"
% tree
.
├── bin
├── pkg
│ └── mod
└── src
└── github.com
└── w2216
└── hello
└── main.go
% pwd
/Users/admin/go/src/github.com/w2216/hello
go build main.go
编译, 编译后的文件可以直接执行
或 go run main.go
执行
Go语言的转义字符
\t
制表符, 排版
\n
换行符
\
转义字符
\r
一个回车
Go开发常见的问题和解决办法
查文档, or 百度
注释
行注释
块注释
规范代码风格
格式化代码方法
goland: Ctrl + Alt + L
Vscode: Shift + Alt + F
gofmt -wmain.go
Golang官方编程指南
https://golang.org Packages
Golang标准库API文档
https://golang.org Packages
Dos常用命令
Golang变量
为什么需要变量
一个程序就是一个世界
变量是程序的基本组成单位
变量的介绍
声明变量
变量赋值
使用变量
变量使用注意事项
声明后不赋值, 只用默认值
根据值的类型, 类型推导
省略var
, 用:=
多变量声明:
var( // 都可以用
n1=100
n2=200
)
n1, name := 100, "aaa" // 函数内可以用
统一作用域内不能重复定义
变量 = 变量名 + 值 + 数据类型
默认值: int 0, string "", bool false, float 0
变量的声明, 初始化和赋值
var 变量名 数据类型
+号的使用
数值类型做加法运算
字符串做拼接
数据类型的基本介绍
数据类型
基本数据类型
数值型(整数, 浮点), 字符型, 布尔型
复杂数据类型
指针, 数组, 结构体, 管道, 切片, 接口, map
整数类型
Int8 1字节
Int16 2字节
Int32 4字节
int64 8字节
类型: int, uint, rune(int32), byte(uint8)
使用细节
有符号和无符号, int 和 uint 和系统有关
golang默认整数是int类型
查看某个变量的字节大小和数据类型:
var a = 10
fmt.Printf("%T, %d \n", a, unsafe.Sizeof(a))
% go run main.go
int, 8
bit: 计算机中最小的存储单位, byte: 计算机中基本存储单元.
1byte = 8bit
小数类型/浮点型
单精度 4字节 float32
双精度 8字节 float64
浮点数 = 符号位 + 指数位 + 尾数位
浮点数都是有符号的
尾数部分可能丢失, 造成精度丢失
使用细节:
不受OS影响
默认声明为float64
两种表示形式: 5.12
.512
5.12e2
==> e2是10的二次方, e-2是/10的2次方
推荐使用float64
字符类型
一般使用byte存, 存储单个字符
字符串就是一串固定长度的字符连接起来的字符序列. 而Go的字符串是单个字节连接起来的
使用细节:
单引号括起来
可以使用'\n'
表示换行符, (特殊字符型常量)
UTF-8编码
字符的本质是一个整数, 直接输出时, 是对应的UTF-8编码的码值
可以直接赋值一个数字, 输出时使用%c
, 回输出字符
var b = 98
fmt.Printf("%c\n", b)
% go run main.go
b
可以进行运算
10 + 'a'
字符本质:
存储: 字符—>对应码值—>二进制—>存储
读取: 二进制—>码值—>字符—>读取
布尔类型
true false
占一个字节
用于逻辑运算:
if for
string类型
使用细节:
字符串一旦赋值, 就不能修改了(用下标修改), 字符串不可变
表示形式:
双引号, 识别转义字符
反引号, 原样输出
字符串拼接: +
基本数据类型默认值
整型 0, 浮点型 0, 字符串 “”, 布尔 false
基本数据类型相互转换
Go语言需要显示转换, 不能自动转换
基本语法
表达式 T(v)
将v转换为T类型
var i int = 100
var n float32 = float32(i) // 把 int 转成 float32
大转小可能会溢出
基本数据类型和string类型转换
fmt.sprintf("%参数", 表达式)
fmt.sprintf("%d", num) // %f %t %c
strconv包的函数
strconv.FormatInt(int64(num2), 10) // FormatBool FormatFloat FormatUint
string类型转基本类型
ParseBool ParseFloat ParseInt ParseUint
注意事项:
要确保string类型能够转成有效的数据, 不然会转成该类型的默认值, 如:把"hello"=>int类型, 会变成0
指针
变量的地址 &, 获取变量的地址 &num
指针类型, 指针变量存的是一个地址, 这个地址指向的空间才是值
var ptr *int = &num
获取指针类型所指向的值, *ptr
使用细节:
值类型, 都有对应的指针类型, 形式为 *数据类型
值类型包括: int系列, float系列, bool, string, 数组, 结构体
值类型和引用类型
值类型包括: int系列, float系列, bool, string, 数组, 结构体
引用类型: 指针, slice切片, map, 管道chan, interface
值类型特点:变量直接存储值, 内存通常在栈中分配
引用类型特点: 变量存储一个地址, 内存通常在堆中分配
标识符的命名规范
区分大小写
不能有空格
下划线为空标识符
注意系统保留关键字 25个
驼峰命名法
首字母大写可以被别的包访问, 小写不可以
系统保留关键字
break, func, go, defer… 25个
系统预定义标识符
int, bool, nil… 36个
运算符
运算符的基本介绍
运算符是一种特殊的符号, 表示数据的运算/赋值/比较等
算术运算符
赋值运算符
比较运算符/关系运算符
逻辑运算符
位运算符
其他运算符
算术运算符
正号+,负号-,+,-,*,/,%,++,–,字符串相加+
注意事项:
整数间除法, 只保留整数部分, 舍弃小数部分
取模: a%b = a-a/b*b
++, – 独立使用
关系运算符
==,!=,<,>,<=,>=
返回bool类型
逻辑运算符
&&,||,!
与或非
赋值运算符
=,+=,-+,*=,/=,%=
«=,»=,&=,^=,|=
特点:
从右往左
位运算符
&,|,^,«,»
其他运算符
&返回变量地址,*指针变量
特别说明,
不支持三元运算符
运算符优先级
键盘输入语句
fmt.Scanln(), fmt.Scanf()
进制
二进制, 八进制0, 10进制, 16进制0X
位运算
原码,反码,补码
6句话:
二进制的最高位位符号位: 0正数,1负数
正数的原码,反码补码都一样
负数的反码=原码符号位不变, 其他位取反
负数的补码=它的反码+1
计算机运算时, 都是以补码运算的
程序流程控制
顺序控制
分支控制
循环控制
顺序控制
从上到下
If分支控制
单分支
双分支
多分支
switch分支控制
不需要加break, 简洁性
穿透: fallthrough
for循环控制
for
for-range
While/do…while
不存在, 没有这个语法
多重循环控制
打印金字塔, 99乘法表
跳转控制语句break
可以通过标签, 指定终止哪一层
跳转控制语句continue
可以通过标签, 指定终止哪一层
跳转控制语句go-to
不主张使用, 造成程序混乱
goto label1
label1:
跳转控制语句return
函数/包/错误处理
函数基本概念
为完成某一功能的程序指令的集合
自定义函数, 系统函数
函数基本语法
func 函数名 (形参列表) (返回值列表) {
执行语句
return 返回值列表
}
包的基本概念
go的每个文件都属于一个包
包的三大作用
区分相同码值的函数/变量等标识符
当程序文件很多时, 便于管理项目
控制函数/变量等访问的范围, 即作用域
包的相关说明
package 包名
import “包的路径”
包可以取别名
函数的调用机制
函数的递归调用
斐波那契数列
函数使用细节
基本数据类型都是值传递, 即值拷贝
go函数不支持重载
函数也是一种数据类型, 可以作为形参
go支持自定义数据类型
type myInt int
支持对函数返回值命名
_标识符, 忽略返回值
支持可变参数, args是切片, 通过args[index]访问
init函数
每一个文件都能包含一个init函数, 在main函数之前调用
细节:
全局变量定义>init函数>main函数
匿名函数
没有名字的函数
直接调用
把匿名函数赋值给一个变量, 可以通过改变量来调用匿名函数
闭包
一个函数和其相关的引用环境组合的一个整体
函数的defer
在函数执行玩毕后, 及时释放资源, defer延时机制
关闭数据库链接,关闭文件, 释放锁等
defer入栈, 先入后出
函数参数传递方式
值传递
引用传递
变量的作用域
局部变量, 作用域仅限于函数内部
全局变量, 整个包都有效, 如果首字母大写, 整个程序都有效
如果变量是在一个代码块, 作用域就在这个代码块中, for/if
字符串常用的系统函数
字符串长度, 按字节, len(str)
字符串遍历, r:=[]rune(str)
字符串转整数: strconv.Atoi(“12”) // Itoa
字符串转byte: []byte(“hello”) // string([]byte{97,98,99})
Strings.Contains(“hello world”, “llo”) //true
Strings.Count
…
时间和日期函数
time包
time.Time类型, 用于表示时间
now := time.Now()
now.Year()
...
格式化时间
2006-01-02 15:04:05
时间常量
time.Sleep(time.Second)
内置函数
len: 长度 string,array,slice,map,channel
new:分配内存 int,float,struct 返回的是指针
make:分配内存 分配引用类型 channel,map,slice
错误处理
Defer,panic,recover
使用defer+recover处理错误
自定义错误:
Errors.New(“错误说明”)
panic内置函数, 接受一个interface{}的值, 可以接受error类型的变量
数组和切片
数组是值类型
for遍历
for-range遍历
注意事项:
长度固定, 不能动态变化
数组中的值可以是如何数据类型, 包括值类型和引用类型, 但不能混用
没有赋值, 即零值
数组下标越界会panic
长度是数组类型的一部分
切片slice
切片是引用类型
长度可以变化, 可以理解为动态变化的数组
for遍历
for-range遍历
注意事项:
append内置函数, 可以动态追加切片
copy(s2, s1) 拷贝切片
string和slice
string底层是一个byte数组
string是不可变的, 不能通过str[‘0’] = ‘a’ 来改修改字符串
如果要修改字符串, 先转成byte/rune, 修改, 再转回来
排序和查找
排序的基本介绍
内部排序
交换排序法, 选择排序法, 插入排序法
外部排序
合并排序法, 直接合并排序法
冒泡排序
查找
顺序查找
二分查找
二维数组
map
map的基本介绍
map是key-value数据结构
map的声明
var 变量名 map[keytype]valuetype
key通常为int/string
value通常为数字,string,map,struct
无序
delete(map, “key”) // 删除
val, ok:=city[“no1”] // 查找
for-range
map切片
切片的数据类型如果是map, 就叫map切片
map排序
先将key排序, 将结果输出即可
map使用细节
引用类型
map能自动扩容
value通常为struct类型
面向对象编程(上)
结构体
type 结构体名称 struct {
field1 type
field2 type
}
指针, slice, map的零值都是nil, 因为还没有分配空间
结构体是值类型
创建结构体变量和访问结构体字段
var person Person
var person Person = Person{}
var person *Person = new (Person) // 返回结构体指针
var person *Person = &Person{} // 返回结构体指针
结构体使用注意事项和细节
结构体的所有字段在内存中是连续的
结构体是用户单独定义的类型, 和其他类型进行转换时需要有完全相同的字段
结构体进行type 重新定义, 相当于取别名, go认为是新的数据类型, 但是相互之间可以强转
struct的每个字段上可以写一个tag, 该tag通过反射机制获取, 常用于序列化和反序列化
方法
作用在指定的数据额类型上的, 和指定的数据类型进行绑定
因此, 自定义类型, 都可以有方法, 而不仅仅是struct
方法的声明和调用
func (a A) test(参数列表) (返回值列表){
fmt....
}
细节说明:
test方法和A类型绑定
test方法只能通过A类型的变量来调用, 而不能直接调用, 也不能使用其他类型变量来调用
a 这个名称, 不固定, 随意制定
方法的调用和传参机制原理
细节:
结构体是值拷贝类型
修改结构体变量的值, 通过指针方法处理
如果类型实现了String()方法, 会默认调用
面向对象编程应用实例
声明结构体, 确定结构体名
编写结构体字段
编写结构体方法
工厂模式
golang中没有构造函数, 通常使用工厂模式来解决问题
面向对象编程(下)
面向对象编程思想-抽象
面向对象三大特性-封装
面向对象三大特性-继承
提高代码复用性, 扩展性, 维护性
结构体和匿名结构体, 就近访问原则
一个struct嵌套一个有名结构体, 组合
多重继承: 一个struct嵌套多个匿名结构体
接口
type 接口名 interface{
方法1(参数列表) 返回值列表
方法2(参数列表) 返回值列表
...
}
定义方法, 不需要实现
接口的所有方法都没有方法体
不需要显式实现
所有类型都实现了空接口
接口在一定程度上实现代码解耦