go学习
0.初始化项目(go mod init)
//实际项目开发中我们首先要在我们项目目录中用 go mod 命令生成一个 go.mod 文件管理我们项目的依赖。
//比如我们的 golang 项目文件要放在了 itying 这个文件夹,这个时候我们需要在 itying 文件夹
//里面使用 go mod 命令生成一个 go.mod 文件
//go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。
//go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。
//go.mod 提供了module【语句指定包的名字(路径)】, require【语句指定的依赖项模块】、replace【语句可以替换依赖项模块】和exclude【语句可以忽略依赖项模块】 四个命令
go mod命令
download:下载依赖包
graph:打印依赖图
edit:编辑go.mod
init:在当前目录初始化mod
tidy:拉去缺少的模块或移除不用的模块
vendor:将依赖复制到vendor下
why:解释依赖
verify:验证依赖
//如果想在一个包中引用另外一个包里的标识符(如变量、常量、类型、函数等)时,该标识符必须是对外可见的(public)。
//在 Go 语言中只需要将标识符的首字母大写就可以让标识符对外可见了。
//例如:
var age=18//私有变量
var Name ="sdas"//共有变量
func F1(){//共有方法
fmt.Println(0)
}
init函数
//在Go语言程序执行时导入包语句会自动触发包内部 init()函数的调用。
//需要注意的是:init()函数没有参数也没有返回值。
//init()函数在程序运行时自动被调用执行,不能在代码中主动调用它。
//init执行顺序
//Go 语言包会从 main 包开始检查其导入的所有包,每个包中又可能导入了其他的包。
//Go 编译器由此构建出一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包的代码。
//在运行时,被最后导入的包会最先初始化并调用其 init()函数, 如下图示
第三方包 第三方包需要下载常用网址https://pkg.go.dev/
method1: go get 包名(github.com/gocelery/gocelery)全局
method2 go mod download 全局
依赖包会自动下载到$GOPATH/pkg/mod,多个项目可以共享缓存的 mod
注意使用 go mod download 的时候首先需要在你的项目里面引入第三方包
method3 go mod vendor 本地
go mod vendor 将依赖复制到当前项目的 vendor 下 (本项目)
1、Strcconv 基本类型转换
atoi 字符串到int
ltoa int到字符串
ParseBool, ParseFloat, ParseInt和ParseUint将字符串转换为值:
FormatBool, FormatFloat, FormatInt和FormatUint将值转换为字符串:
2、Stings
strings.Split 分割
strings.Join 拼接
strings.Contains(s,c) 判断s是否包含c true
strings.HasPrefix(s,c))//判断是否以c开头
strings.HasSuffix(s,c))//判断是否以c结尾
strings.Index(s,"q")) //字符串索引位置 头
strings.LastIndex(s,"q")) //尾
3.sprintf转str
转换需要主要格式:int=%d,float=%f,bool=%t,byte=%c
4.sor t 排序
升序
分别用 sort.Ints() 、sort.Float64s() 和 sort.Strings() 函数
降序
使 用 sort.Reverse(slice)
sort.Sort(sort.Reverse(sort.IntSlice(intList)))
5.字符串遍历
//第一种 常规操作
func main(){
str := "abc你好"
for i:=0;i<len(str);i++{
fmt.Printf("%v=%c ",str[i],str[i])//97=a 98=b 99=c 228=ä 189=½ 160= 229=å 165=¥ 189=½
}
fmt.Println()
}
//第二种 获取键值
func main(){
str := "abc你好"
for _,i := range str{
fmt.Printf("%v=%c ",i,i)//97=a 98=b 99=c 20320=你 22909=好
}
}
6.数组定义
var name [length]type
var a [3]int
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yo0PtKXY-1672714264751)(D:\Users\Desktop\新云计划\学习\go学习笔记\images\数组定义.jpg)]
7.map定义(类似字典)
items := map[int]string{1: "A", 2: "B", 3: "C"}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EhANwHz8-1672714264752)(D:\Users\Desktop\新云计划\学习\go学习笔记\images\映射map定义.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9CAl24eW-1672714264754)(D:\Users\Desktop\新云计划\学习\go学习笔记\images\map深度操作.jpg)]
8.切片定义
a := []int{1, 2, 3, 4, 5, 6, 7}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-owA0NRyo-167271426475
5)(D:\Users\Desktop\新云计划\学习\go学习笔记\images\切片定义.jpg)]
9.Slice(切片)
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)
var name []type 声明数组 空是nil
声明变量后
bool -> false
numbers -> 0
string-> ""
//其它都是nil
pointers -> nil
slices -> nil
maps -> nil
channels -> nil
functions -> nil
interfaces -> nil
1
内置函数make()构造切片 s := make([]int,2,5)
2
切片添加元素:内置函数append() 切片添加切片s = append(s,c...) 记得加...
3
删除切片 逻辑删除 添加当前切片除删除值以外的值
s := []int{1,2,3,4,5}//删除3
s = append(s[:2],s[3:]...)
4
切片应用问题 使用make创建新切片 修改新数组原数组不改变
10.map 是 key-value 数据结构
使用必须用make初始化
var a map[string]string
a = make(map[string]string)
11.Go中指针概念:&:取地址,*根据地址取值
12.make和new
//make 用于Slice,map,channel等内置数据结构 它只用于 chan、map 以及 slice 的内存创建
//new 用于为类型申请一片内存空间,并返回指向这片内存的指针
13.struct 结构体
Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。
Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性
结构体定义
方法一:实例化法
type my struct {
name string
a int
}
方法二:new实例化法
方法四:键值对初始化法
14.方法
方法的定义格
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
15.泛型 interface(接口类型)
~符号,这也是泛型支持中新增加的一种表达方式~T波浪号,用于标识近似约束
// 泛型定义,T为参数,any是参数约束
func GenericFunc[T any](args T) {
// ...
}
type my interface {
f1()
f2()
}
type stu struct {
name string
}
func (p stu) f1() {
fmt.Println(p.name,"123")
}
func (p stu) f2() {
fmt.Println(p.name,"456")
}
16.if
func main() {
if s := 50; s < 50{
fmt.Println("aaa")
}else if s>50 {
fmt.Println("bbb")
}else {
fmt.Println("ccc")
}
}
17.for循环
i++类似python中range循环个数 range类似python'枚举'有key,value
func main() {
for i :=0;i<10;i++{
fmt.Println(i)//1--9
}
}
//外部定义
func main() {
i := 0
for i<10{
fmt.Println(i)
i++
}
}
//for range
func main() {
s := "asdasd"
for k,v := range s{
fmt.Printf("%v,%c",k,v)
}
}
18.switch
func main() {
s := 80
switch s {
case 90,100:
fmt.Println("A")
case 70,80:
fmt.Println("B")
case 50,60:
fmt.Println("C")
default:
fmt.Println("D")
}
} //B
//fallthrough 语法可以执行满足条件的 case 的下一个 case,是为了兼容 C 语言中的 case 设计
func main() {
s := 80
switch {
case s==100:
fmt.Println("A")
fallthrough
case s==80:
fmt.Println("B")
fallthrough
case s==60:
fmt.Println("C")
default:
fmt.Println("D")
}
}//B C
label 跳转
func TestGoto(t *testing.T) {
fmt.Println(1)
goto three //跳转
fmt.Println(2) // 这行将会被跳过
three:
fmt.Println(3)
} //顺序执行 1执行完直接跳到3执行
19.defer 延迟关闭文件 类似close
在Go中,defer语句会推迟函数的运行,常用于关闭文件描述符、数据库连接、解锁资源等操作。多个defer存在时,会逆序运行,先运行最后一个,最后运行第一个。这种操作可以让我们在打开资源时就增加defer关闭函数,比写在最后清晰且不易忘记。
// 逆序释放
// 1 2 3 4 -4 -3 -2 -1
for i := 1; i <= 4; i++ {
defer fmt.Println("deferred", -i)
fmt.Println("regular", i)
}
// 关闭文件,打开资源后就可以直接使用defer推迟关闭
fp, err := os.Create("1.txt")
if err != nil {
return
}
defer fp.Close()
20.捕获异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HbnSZgQB-1672714264757)(D:\Users\Desktop\新云计划\学习\go学习笔记\images\异常捕获1.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DhcNOLDN-1672714264758)(D:\Users\Desktop\新云计划\学习\go学习笔记\images\异常捕获2.jpg)]
21.pamic
一般情况下我们会将错误返回,然后判断错误是否存在并作出合理的处置,例如使用if err!= nil,但如果碰到无法恢复的错误不想让程序继续允许,我们就可以使用panic关键词来创建一个运行时错误,该错误将直接调用当前的defer后停止程序。
但我们应该尽量避免使用panic的方式来处理程序。
// 不会触发,panic只触发当前goroutine的defer
defer fmt.Println("defer 1")
go func() {
// 触发调用后退出
defer fmt.Println("defer 2")
panic("exit")
}()
time.Sleep(1 * time.Second)
22.recover
当有panic被调用时,程序会崩溃后停止,如果我们想在停止前重新获得控制权,来做一些处理操作,则可以使用recover函数。recover()只有在defer中调用才会生效。
defer fmt.Println("defer 1")
defer func() {
if err := recover(); err != nil {
// recover exit
// defer 1
fmt.Println("recover", err)
}
}()
panic("exit")
23.reflect 反射的三大定律。
反射可以从接口值中获得反射对象。如:reflect.TypeOf() / reflect.ValueOf()
反射可以从反射对象中获得接口值。如:y := v.Interface().(float64)
要修改反射对象,其值必须可设置。如:值类型的变量不可设置
// 无参调用
reflect.ValueOf(t).MethodByName(name).Call(nil)
// 有参调用
value := []reflect.Value{a, b}
reflect.ValueOf(t).MethodByName(name).Call(value)
// 接收返回值
ret := reflect.ValueOf(t).MethodByName(name).Call(nil)
// 类型判断
t := reflect.TypeOf(a)
switch t.Kind(){
case reflect.Int:
// ..
case reflect.String:
// ..
}
24.chan
在 go 关键字后面加一个函数,就可以创建一个线程,函数可以为已经写好的函数,也可以是匿名函数
func main() {
fmt.Println("main start")
go func() {
fmt.Println("goroutine")
}()
fmt.Println("main end")
}
// 声明不带缓冲的通道
ch1 := make(chan string)
// 声明带10个缓冲的通道
ch2 := make(chan string, 10)
// 声明只读通道
ch3 := make(<-chan string)
// 声明只写通道
ch4 := make(chan<- string)
写入
ch1 := make(chan string, 10)
ch1 <- "a"
读取
val, ok := <- ch1
// 或
val := <- ch1
关闭
close(chan)
注意:
close 以后不能再写入,写入会出现 panic
重复 close 会出现 panic
只读的 chan 不能 close
close 以后还可以读取数据
25.gin
// 获取 Get 参数
name := c.Query("name")
price := c.DefaultQuery("price", "100")
// 获取 Post 参数
name := c.PostForm("name")
price := c.DefaultPostForm("price", "100")
// 获取 Get 所有参数
reqGet = c.Request.URL.Query()
reqget['name'] 获取的是数组
reqget.Get("name") 获取是字符串
//获取 Post 所有参数
ReqPost = c.Request.PostForm
//返回参数 gin.H 自带的 H源码中是一个map
c.JSON(http.StatusOK, gin.H{
"message": "ok",
"username1": a,
"username2": b,
"address": "ccccc",
})
go build .\cmd\app\main.go
.\main.exe server --config .\config\app.ini
ncloud一些返回
返回的code码
const (
Ok = 0
Unauthorized = 401
DecryptFailed = 1001
ActionNotExist = 1002
ActionNotSupport = 1003
MissingParameters = 1004
ValidateFailed = 1005
)
var codesText = map[Code]string{
Ok: "ok",
Unauthorized: "Unauthorized",
DecryptFailed: "DecryptFailed",
ActionNotExist: "ActionNotExist",
ActionNotSupport: "ActionNotSupport",
MissingParameters: "MissingParameters",
ValidateFailed: "ValidateFailed",
}
接收参数类型
type formParams struct {
// r={"action":"ImageQuery", "sync": true, "params":{}}
params string
// s=7e43450a-83ac-4730-8a42-50e403c0b142
sign string
}
返回参数方式
func responseOk(ctx *gin.Context, result any) {
r := task.NewTaskResponse(codes.Ok, result)
response(ctx, r)
}
func responseError(ctx *gin.Context, c codes.Code) {
r := task.NewTaskResponse(c, nil)
response(ctx, r)
}
type TaskResponse struct {
Code codes.Code
Message string
Result any
}
解决一些问题
1. go: github.com/bytedance/sonic@v1.4.0: Get "https://proxy.golang.org/github.com/bytedance/sonic/@v/v1.4.0.mod": dial tcp 17
解决 go env -w GOPROXY=https://goproxy.cn
指针自己总结
1. 结构体下接收者指向了内存地址 实例化结构体必须指向结构体 否则会报错 反之 接受者没有指向内存地址 实例化会报错
2. 结构体的方法 可以直接调用方法 也可以指向结构体
同样的事情也发生在相反的方向。
接受一个值作为参数的函数必须接受一个指定类型的值:
var v Vertex
fmt.Println(AbsFunc(v)) // OK
fmt.Println(AbsFunc(&v)) // 编译错误!
而以值为接收者的方法被调用时,接收者既能为值又能为指针:
var v Vertex
fmt.Println(v.Abs()) // OK
p := &v
fmt.Println(p.Abs()) // OK
这种情况下,方法调用 p.Abs() 会被解释为 (*p).Abs()。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func AbsFunc(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
fmt.Println(AbsFunc(v))
p := &Vertex{4, 3}
fmt.Println(p.Abs())
fmt.Println(AbsFunc(*p))
}
4. 函数中传递结构体 有*必须传递指向结构体的变量(&)