Go语言编程笔记

101 阅读13分钟

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包==实现格式化的标准输入和输出 image-20250226150921179.png

image-20250226150945085.png 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() 延时==