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

保留了指针, 弱化的指针

引入包的概念, 一个文件都要归属于一个包

垃圾回收机制, 内存自动回收

天然并发(重要)

  1. 从语言层面支持并发, 实现简单
  2. goroutine, 轻量级线程, 可实现大并发处理, 高效利用多核心
  3. 基于CPS并发模型实现
  4. 吸收了管道通信机制, 形成Go语言特有的管道channel, 实现不同goroutine之间相互通信
  5. 函数可以多返回值
  6. 新的创新, 比如切片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(参数列表) 返回值列表
  ...
}

定义方法, 不需要实现

接口的所有方法都没有方法体

不需要显式实现

所有类型都实现了空接口

接口在一定程度上实现代码解耦

面向对象编程-多态

类型断言