GO语言编程
1、认识GO
基于编译、垃圾收集和并发的编程语言,多处理器系统
==注意事项:==
1、.go作为文件拓展名
2、main()是主程序入口,不支持任何返回值和参数传入
3、代码区分大小写
4、一行一个语句
5、定义的变量或导入的包没用使用,代码会报错
2、基础语法
2.1关键字与标识符
25个关键字
break 中断循环;switch……case…… 选择结构,和switch、select一结合使用
chan 定义通道;const 定义常量;continue 跳过本轮循环,进入下一轮循环
default 设置默认值,和switch、select一结合使用;defer 延迟执行语句
else……if……;for;import;func;range;return;struct;type;
fallthrough,在case语句中加入fallthrough,匹配成功仍强制匹配其他语句;
go 启动并发执行;goto 跳转语句;interface 定义接口;map 定义集合;package 定义包
var 定义变量
标识符
字符、下划线和数字组成,但==第一个字符不能是数字==
1、标识符简短有意义;2、提高代码可读性;3、==myName;MyName;my_name==
_ 空白标识符 匿名占位符
预声明标识符
bool; byte; complex64 complex128复数类型; error异常错误类型; float32 float64;
int8 16 32 64; rune== int32; string; uint, uintptr 无符号整数; true;false; ==iota 常量计数器==;
append 在切片或数组中追加一个或多个元素; cap获取切片或数组的容量; close 关闭通道;
complex 为复数赋值; copy 复制切片; delete 删除集合某个元素; imag 获取复数虚部;
len; make 内存分配(chan、map和切片); new 创建指针变量; panic 非常严重的错误;
print 异常信息输出; println多了换行的print; real 取复数实部; recover
2.2变量与常量
var name type
var name type = value
var(
name type
name type = value
)
var name1,name2 type
name := value
==var定义变量,const定义常量==
没有赋值的常量与上一个常量值相同
2.3运算符的使用
+ - * / % ++ --
位运算:<<按位左移 >>按位右移 &按位与 ^按位异或 |按位或
逻辑运算:&&与 ||或 !非
&获取变量内存地址 *指针变量
//单行注释
/*多行注释
*/
3、数据类型
==不同类型的浮点数不能直接计算==
复数由俩个浮点数组成,一个表实,一个表虚
格式化
格式化%T 输出变量的数据类型
%v 以数据类型的格式输出
%+v 在%v基础上,为每个值添加对应的字段名
%#v 以Go语言的语法格式输出
%% 一个百分号
%b 二进制格式输出
%o 八进制格式输出
%x 十六进制格式输出
%X 十六进制格式输出,字母大写
%U Unicode格式输出
%f 浮点数格式输出
%p 十六进制格式表示,指针输出
%c 字符格式输出
%q 字符串格式带双引号输出
%t 布尔格式输出
%d 十进制格式输出
%s 字符串格式输出
Go语言字符有两种类型:uint8和rune
uint8也称byte型,代表一个ASCⅡ码的字符
rune代表一个UTF-8字符,当处理中文等复合字符时使用,等价于int32;
==不设置类型,默认为rune==
字符串操作
字符串:数字、字母、下划线
常用操作:==转义和格式化、拼接、获取长度、遍历、字符位置与截取、字符串分割、字符串替换==
| 转义符 | 说明 |
|---|---|
| \t | 制表符、排版用 |
| \n | 换行符 |
| \\ | 一个\ |
| \" | 一个" |
| \r | 回车 |
==反引号`==
字符串赋值必须用双引号或反引号表示。
字符串拼接
5种拼接方式:==+、fmt.Sprintf() 、strings.Join() 、strings.Builder 、bytes.Buffer
func main() {
n := "hello world"
m := "I am Tom"
// 使用"+"拼接字符串
j := n + "," + m
fmt.Println(j)
// 使用fmt.Sprintf()拼接字符串
k := fmt.Sprintf("%s,%s", n, m)
fmt.Println(k)
// 使用strings.Join()拼接字符串
g := strings.Join([]string{n, m}, ",")
fmt.Println(g)
// 使用builder.WriteString()连接字符串
var builder strings.Builder
builder.WriteString(n)
builder.WriteString(",")
builder.WriteString(m)
fmt.Println(builder.String())
// 使用buffer.WriteString()连接字符串
var buffer bytes.Buffer
buffer.WriteString(n)
buffer.WriteString(",")
buffer.WriteString(m)
fmt.Println(buffer.String())
}
len([]rune())//获取含有多字节字符字符串的长度
[]rune()//将字符串转化为切片,切片元素为rune类型
字符位置与截取
m:=strings.Index(n,"world")//获取子字符串world最开始的位置
l:=strings.LastIndex(n,"world")//获取子字符串world最末端的位置
k:=n[m:]//截取m位置往后的字符串
p:=n[m:m+3]//截取m位置往后3位
字符串分割、替换
strings.Split(s,sep)//sep是分割的子字符串或者字符,不会出现在结果里
strings.Replace(s,old,new,n)//n代表替换的次数,-1是全部替换
整型、浮点数和字符串转换
s:=strconv.Itoa(100)//整型转换为字符串,返回值是字符串
i,_:=strconv.Atoi("100")//字符串转换成整型,返回值是整型和异常信息
i,err := strconv.ParseInt(s,base,bitSize)
//base是字符串的进制数,值为2、4、8、10、16
//bitSize是整型类型,err是转换失败的异常信息,转换成功是空值(nil)
strconv.FormatFloat()和strconv.ParseFloat()
4、流程控制
==break+标签名 越级终止==
5、指针
Go语言指针分为两类:类型指针和切片指针:
类型指针允许对数据进行修改,直接使用指针传递数据,无须复制数据,但==类型指针不能进行偏移的计算==
切片指针是切片类型的指针,包含起始元素的指针、元素数量和容量
==指针赋值&,指针取值*==
var name *type //name是指针名字,type是指针指向的数据的数据类型
//还可以用new()来定义指针,会设置默认值
==所有变量先定义后使用==
var name int =200
var ptr *int
ptr = &name
name1 = *ptr
//*是从指针指向的内存地址里面获取值;
//指针的值是指向内容的内存地址;
==切片可以理解为动态数组==,可根据元素数量自动调整切片长度。
//切片指针的定义
var name []*type
name := []*type{}
6、内置容器
数组
数组长度固定,很少使用
var name [number]type
切片
三种定义方式:只定义、定义并赋值、使用make()函数定义
var name []type
name:=[]type{value1,value2}
name:=make([]type,len)//会赋默认值
ss:=apppend(slice,elems)//新增元素
ss:=slice[startIndex,endIndex]//截取元素,包含开始和结束,下标从0开始
i :=coppy(slice1,slice2)//i代表复制元素的数量,将2复制到1
容量和长度
集合
映射(map),无序键值对(key-value),通过key快速检索value
var name = map[keytype]valuetype{}//在{}里赋值
列表
var name list.List
name:=List.New()
//列表只有新增,加入和删除元素 操作
name.PushBack()//列表末位新增元素
name.PushFront()//列表首位新增元素
name.InsertAfter(添加内容,element)//元素后添加元素
name.InsertBefore()//元素前添加元素
name.PushBackList()//列表后添加列表
name.PushFrontList()//列表前添加列表
name.Remove()//删除元素
//遍历列表
name.Front()/*首个元素*/ Back()/*最后一个元素*/
name.Next()/*下个元素*/ Prev()/*上个元素*/
MoveToBack()/*移到最后*/ MoveToFront()/*移到第一位*/
7、函数
函数:关键字func、函数名、参数列表、返回值数据类型、函数体和返回值
func name (parameter)(returnType){
代码块
return value1,value2...
}
==引用外部变量的函数叫闭包==
用户登录验证可以通过闭包实现
8、结构体
Go语言没有继承和多态,但可以通过匿名字段实现继承,通过接口实现多态
type name struct{
field1 dataType
field2 dataType
}
type person struct{
name string
age int
}
//实例化结构体
var p1 person
p1.name=""
p1.age=22
p:=person{name:"",age:22}
P3:=new(person)
p3.name=""
p3.age=22
p4:= &person{}
p4.name=""
p4.age=22
结构体的指针接受者和值接收者:
1、值接收者是通过数据拷贝方式传递给方法
2、若需修改结构体成员原有的数据,则只能 使用指针接收者。由于值接收者通过数据拷贝方式传递,因此在方法中修改结构体成员值不会改变结构体成员的原有值;
3、无论采用哪一种,结构体方法的调用方式都不会改变。
9、接口
interface,数据类型,==一组方法的集合==。接口的组合、嵌套和鸭子类型(Duck Typing)等实现了代码复用、解耦和模块化等特性,而接口方法是动态分派、反射的基础功能。
type interface_name interface{
method1(parameter)[returnType]
method2(parameter)[returnType]
methodN(parameter)[returnType]
}
==统一管理不同类型的相同行为==
10、反射
计算机程序运行时可以访问、检测和修改它本身状态或行为的一种能力。
==反射就是程序在运行时能够观察并修改自己的行为==
- 处理未知类型:比如JSON解析库、ORM框架(数据库结果映射到结构体)。
- 编写通用工具:比如测试框架中自动调用测试方法。
- 极特殊情况:普通业务代码几乎用不到
11、并发编程
Goroutine
==M、G、P:==
M代表一个线程,G所有任务都是在M上执行的;
G代表一个Goroutine对象,每次调用的时候会创建一个G对象,主要为M提供运行环境和程序调度。go function启动G。
P代表一个处理器,运行每一个M都必须绑定一个P。
go func(parameter)
并发操作中,有返回值的函数会被忽略返回值,如果需要从并发中返回函数,只能使用通道实现,将需要返回的数据写入通道,主函数或其他并发函数都能从通道读取数据,从而实现数据共享。
通道
var name chan type
name:=make(chan type, num)//num是存放数据的数量上限,不设置num就是无缓冲通道
//构建通道
ch:=make(chan string)
//往通道写入数据
ch <- "hello"
//从通道获取数据
s:=<- ch
同一时刻只能有一个Goroutine进入通道进行写入和读取数据,通道遵循FIFO原则
==无缓冲通道,读写操作必须是一读一写;不能只读不写或只写不读==
cap()//可以获取通道的容量大小
close()//关闭通道
select是Go语言的控制结构语句,==仅适用于通道==
与case、default搭配,case必须是一个通信操作
//sync同步等待
sync.WaitGroup//通过计数器实现等待
//sync加锁机制
sync.Mutex//互斥锁
sync.RWMutex//读写互斥锁
12、语法特性
//异常处理关键字 defer panic recover
==只要某行代码使用了defer都会被最后执行==
1、defer具有延时执行的功能,在一个函数中,执行顺序在函数返回之前;
2、recover()捕捉异常,必须与defer搭配使用,否则程序无法正常捕捉异常;
3、如果panic()没有搭配recover(),程序会提示异常而终止运行,如果俩者在同一个函数中国,程序出现异常仍能继续往下执行。
深浅拷贝
==区别在于变量是否公用一个内存地址==
深拷贝是内存地址不同,浅拷贝内存地址相同
==值类型==:整型、布尔型、浮点型、字符串、数组和结构体,变量赋值都是深拷贝;
引用类型:指针、切片、集合、通道和接口,变量赋值都是浅拷贝。
类型别名与自定义
//类型别名
type name = Type
//类型定义
type name Type
type meString = String//等于把string改个名字
type myString String//新定义一个字符串的数据类型
new和make
func new(Type) *Type
//函数参数Type用于创建变量的数据类型
//返回值*Type以指针形式表示,说明创建的变量以指针表示,
//指针存储的数据为0,空字符串,false,空切片,空集合
==make()==仅允许设置==切片、集合以及通道==的数据类型
泛型
func name[p,r](parameter p)r{
parameter
return returnType
}
//name是函数名 parameter是函数参数
//p是为参数设置数据类型,如int|string
//r是为返回值设置数据类型,如int|string
func sum[K string,V int|float64](m map[K]V) V{
var s V
for _, V := range m{
s+=v
}
return s
}
//函数的参数是集合m,集合的key的类型是K,value的类型是V
//函数返回值的类型是V
13、包的应用和管理
1、==fmt包==实现格式化的标准输入和输出
2、==o包==提供原始I/O操作,定义4个基本接口Reader、Writer、Closer、Seeker,用于表示二进制流的读、写、关闭和寻址操作。
3、==bufio包==通过对io包的封装提供了数据缓冲功能
4、==sort包==提供排序功能,提供插入排序insertionSort、归并排序symMerge、堆排序heapSort和快速排序quickSort4种排序算法
5、==strconv包== 基本数据类型转换
6、==os包==操作系统访问功能,包括文件操作、进程管理、信号和用户账号等功能
7、==encoding/json包==将某些数据转换为JSON数据
JavaScript Object Notation
{
"name": "小明",
"age": 18,
"isStudent": true,
"hobbies": ["篮球", "音乐"],
"address": {
"city": "上海",
"street": "中山路"
}
}
10、==tml/template包==为web开发提供模板语法
11、==net/http包==提供HTTP服务,包括HTTP请求、响应和URL解析,以及HTTP客户端和服务端。少量代码就可以实现HTTP服务器或网络爬虫。
12、==strings包==提供字符串操作,字符串合并、查找、分割、比较、拓展名检查、索引、大小写等
13、==bytes包== 适用于字节类型的切片数据
14、==refect包== 反射功能
15、==log包== 实现日志功能,提供三个日志输出接口:Print、Fatal和Panic
16、==imag包== 提供图片处理功能
17、==time包== 提供时间和日期功能
18、==container/list包== 提供列表功能
19、==testing包== 提供自动化单元测试功能
20、==regexp包== 提供正则表达式
21、==math包== 提供基础数学常量和运算函数
import(
"fmt"
"math"
)//一次导入多个包
import(
"math/rand"//导入math里面的rand包
crand "crypto/rand"//导入crypto里面的rand包,并改名为crand
)
import(
."math/rand"//实心点. 无包名调用,可以直接调用rand里面的函数
)
初始化函数init()
初始化函数会在主函数前执行,用于设置包、初始化变量等
init()不能声明和创建变量,只能对变量执行赋值操作
init()不能设置参数和返回值
多个init()在执行的时候是无序执行的
包的自定义与使用
包以文件夹形式表示,文件夹名等于包名,自定义包是==创建一个新的文件夹==,文件夹中的所有go文件都隶属于这个包
包管理工具go mod
go env -w GO111MODULE=on
15、时间处理
时间戳、结构体Time、字符串格式化,三者可以相互转换
==时间戳==:北京时间1970年1月1日8时0分0秒到现在的总秒数
time.Now.Unix()//秒级时间戳
time.Now.UnixNano()//纳秒级时间戳 时间戳都是整型表示
now:=time.Now()//结构体实例化对象
t1 := now.Format//字符串格式化,结构体Time实例化对象转化为字符串
//字符串格式化模板说Go出生的时间 "2006-01-02 15:04:05.000 Mon Jan"
Sub() 计算时间差值 Add() /AddDate() 计算时间增量
Equal() Before() After() 对比两个时间信息
==time.sleep() 延时==