首页
统计
留言
Search
1
PHP中使用反射
995 阅读
2
phpstorm配置SFTP
937 阅读
3
Go语言——结构体
792 阅读
4
PhpStorm 使用 AI 代码生成工具 Codeium
778 阅读
5
关于PHP的垃圾回收机制
762 阅读
后端
PHP
Go
数据库
其他
前端
其他技术
生活杂谈
登录
Search
标签搜索
Laravel
Mysql
RPC
Redis
Liunx
PHP
CSS
ES
算法
开发工具
断点续传
反射
phpstorm
工具
防盗链
CURL
设计模式
面试
Nginx
搜索引擎
quhe.net
首页
栏目
后端
PHP
Go
数据库
其他
前端
其他技术
生活杂谈
页面
统计
留言
搜索到
6
篇与
Go
的结果
2021-11-12
Golang方法集的理解
先看一段代码// notifier 是一个定义了notify行为的接口 type notifier interface { notify() } // user 使用指针语义实现了notifier接口 type user struct { name string email string } // receiver为指针语义 func (u *user) notify() { fmt.Printf("Sending User Email To %s<%s>\n", u.name, u.email) } // receiver为值语义 func (u user) showUserInfo() { fmt.Printf("Name:%s\n Email:<%s>\n", u.name, u.email) } func main() { u := user{"Han", "han@email.com"} // 发生编译错误!!!!!!!!!!! sendNotification(u) } // sendNotification函数接收一个实现notifier接口的变量 func sendNotification(n notifier) { n.notify() } 代码在25行发生了编译错误// ./prog.go:32:19: cannot use u (variable of type user) as type notifier in argument to sendNotification: // user does not implement notifier (notify method has pointer receiver) 为什么会发生编译错误?1. 什么是方法集类型的 方法集 确定了该类型的 操作数 所可以 调用 的方法。每一个类型都有一个(可能为空的)方法集与之关联类型 T (值语义)的方法集,包含全部receiver为 T 的方法类型 *T (指针语义)的方法集,包含全部receiver为 T + *T 的方法注:当类型调用自己声明的方法时,不需要考虑receiver是值语义还是指针语义,可以调用全部方法。因为在调用时,编译器自动做了转换。例如:// 使用值语义声明变量u u := user{} // notify方法的receiver为指针语义 u.notify() // 编译器自动转换为 (&u).notify() // 使用指针语义声明变量u2 u2 := &user{} // showUserInfo方法的receiver为值语义 u2.showUserInfo() // 编译器自动转换为 (*u2).showUserInfo() 2. 什么是接口go语言接口是一个或多个方法签名的集合。任何类型的方法集中只要拥有该接口 对应的全部方法签名(指有相同名称、参数列表 (不包括参数名) 以及返回值列表) ,就表示该类型实现了该接口,无须在该类型上显式声明实现了哪个接口。解决理解了上面两个概念后,回到文章开头的代码中:func main() { // 使用值语义声明的user u := user{"Han", "han@email.com"} // 此时 user 类型变量的方法集中仅包含: // 值语义receiver的 showUserInfo() 方法 sendNotification(u) // 发生编译错误!!!!!!!!!!! // user类型的变量 u 没有实现 notifier 接口 // 因为拥有指针语义receiver的 notify 方法不属于 u 变量的方法集 } 修改后:func main() { // 依然使用值语义声明的user u := user{"Han", "han@email.com"} // 对u取指针,此时传递给 sendNotification 方法的参数是 *user 类型 sendNotification(&u) // 此时 *user 类型变量的方法集中包含了: // 拥有值语义receiver的 showUserInfo() 方法 // 拥有指针语义receiver的 notify() 方法 // 实现了 notifier 接口,编译器不报错 } {callout color="#f0ad4e"}可以得到以下结论:{/callout}若以值语义 T 作为receiver实现接口,不管是T类型的值,还是T类型的指针,都实现了该接口若以指针语义 *T 作为receiver实现接口,只有T类型的指针实现了该接口
2021年11月12日
103 阅读
0 评论
3 点赞
2021-11-12
Golang的panic与recover
一、panicpanic的作用就是抛出一条错误信息,从它的参数类型可以看到它可以抛出任意类型的错误信息。在函数执行过程中的某处调用了panic,则立即抛出一个错误信息,同时函数的正常执行流程终止,但是该函数中panic之前定义的defer语句将被依次执行。之后该goroutine立即停止执行。二、recoverrecover()用于将panic的信息捕捉。recover必须定义在panic之前的defer语句中。在这种情况下,当panic被触发时,该goroutine不会简单的终止,而是会执行在它之前定义的defer语句。建议每起一个goroutine,都defer统一捕获下异常谨记用defer统一捕获异常只对 当前的goroutine 有效,goroutine的异常并不会向上传递给main主函数例子package main import "fmt" import "math" func foo(a int) { defer fmt.Println("foo退出来了") defer func() { if r := recover(); r != nil { fmt.Printf("捕获到的错误:%s\n", r) } }() if a < 0 { panic("必须输入大于0的数") } fmt.Println("该数的方根为:", math.Sqrt(float64(a))) }
2021年11月12日
27 阅读
0 评论
0 点赞
2021-08-09
go build、go install、go get的区别
1.作用go build:用于 测试编译包有无错误 ,在项目目录下 生成可执行文件 ( 有main包 )。go install:主要用来生成库和工具。一是编译包文件(无main包),将编译后的包文件放到 pkg 目录下($GOPATH/pkg)。二是编译生成可执行文件(有main包),将可执行文件放到 bin 目录($GOPATH/bin)。go get:相当于 git clone + go install2. 相同点都能生成可执行文件3. 不同点go build 不能生成包文件(.a), go install 可以生成包文件(.a,但是在mod模式下只会在编译中间缓存生成,不会生成到pkg目录下)go build 生成可执行文件在 当前目录下 , go install 生成可执行文件在 bin目录 下( $GOPATH/bin )
2021年08月09日
79 阅读
0 评论
1 点赞
2021-06-09
Go的三种包管理方式(gopath,govendor,gomodule)
前言go语言的发展极其迅速,短短几年便已经历了高速变迁和进化,可能仅仅是几年前的项目,在现在的开发者眼中看起来都像是“上古时代”的产物。其中go的包管理方式就是其中一种,因为各种历史遗留原因导致目前go module、go path和go vendor三种包管理方式在各种项目中都有混杂使用,这篇文章主要来介绍下三种方式的区别和优缺点,现在的go项目该选择哪一种。先说结论,以目前的情况来说,更推荐选择go module。go path和go vendor都是在发展的过程中因为不适应需求逐渐被更好的包管理方式替代,所以也不能说目前的go module就一定是最佳选择,只能说是目前的最佳实践。两个命令go install xxx(安装xxx第三方二进制可执行文件 )go get xxx(下载xxx第三方依赖包并安装)简单说go install类似go build, 将生成的可执行文件放到【$GOPATH/bin】目录中。go get是下载并安装,即git clone+go install,将clone的源码放到【$GOPATH/src】中。需注意,go get从go 1.17开始官方不建议(deprecated)用于安装二进制可执行文件,并且从go 1.18开始不再支持。选项-d未来将成为默认参数,仅执行下载,不进行安装,这么做是为了更符合get的语义。所以安装二进制可执行文件推荐使用go install参见Deprecation of 'go get' for installing executables两个路径GOROOT:go的安装目录,类似java的jdk,存放一些内置的开发包和工具。GOPATH:go指定的工作空间,用于保存go项目的代码和第三方依赖包。可以通过go env查看go相关的环境变量三个包管理工具**go path【不推荐使用】go path是最早的依赖包管理方式(Go1.0),启用GOPATH模式(注意区分GOPATH模式和GOPATH路径,这是两个语境),要求将所有工程代码要求放在GOPATH/src目录下。在工程经过go build、go install或 go get等指令后,会将拉取的第三方xxx依赖包放在GOPATH/src目录下,产生的二进制可执行文件放在GOPATH/bin 目录下,生成的中间缓存文件会被保存在 GOPATH/pkg下。go path有什么问题呢?GOPATH模式下没有版本控制的概念,在执行 go get 的时候,获取的永远是最新的依赖包,下载到GOPATH/src,如果你有两个工程依赖一个包的v1和v2版本,则会发生冲突,因为 GOPATH 模式下两个工程内依赖的导入路径都是一样的,因此两个工程获取的都是v2版本。所以这个模式已经不推荐使用了。govendor【不推荐使用】在Go 1.5版本之后,Go 提供了 GO15VENDOREXPERIMENT 环境变量(Go 1.6版本默认开启该环境变量)和 Go vendor包管理工具,用于将 go build 时的应用路径搜索调整成为当前工程路径/vendor目录的方式,有效的解决了不同工程使用自己独立的依赖包问题。编译 Go 代码会优先从工程目录下的vendor目录先寻找依赖包,如果没有找到,然后GOPATH 中查找,都没找到最后在 GOROOT中查找。优势:因为将第三方依赖完全和工程整合,完全本地化,使得项目构建速度快,且可以工作在无法连接外网的CI/CD流程中。问题:放弃了依赖重用,使得冗余度上升。同一个依赖包如果不同工程想重用,都必须各自复制一份在自己的vendor目录下。而且有趣的是,在go vendor库的官方README文件已经推荐改用go module了。go module从Go1.11开始,官方推出Go module作为包管理工具,且从Go1.13开始为默认选择启用。GOMODULE模式下所有依赖的包存放在GOPATH/pkg/mod目录下,所有第三方二进制可执行文件放在GOPATH/bin目录下,且工程项目可以放在GOPATH路径之外,但要求项目中需要有go.mod文件(该文件通过go mod init命令初始化可以生成)。GOMODULE模式和GOPATH模式一样都指定了依赖包存放的位置,而不是如vendor模式放入工程内,区别在于GOMODULE的go.mod文件中记录了所依赖包的具体版本,既实现了工程之间重用依赖包,且解决了GOPATH模式下不同工程无法依赖同一个包的不同版本的问题。使用GO MODULE模式,需要先开启配置。在Go1.13之后,可以通过如下命令设置GO MODULE启用状态。export GO111MODULE=on GO111MODULE有三个可选值: on、off 和auto ,go 1.16之前默认auto,go 1.16之后默认onGO111MODULE=off 无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包。GO111MODULE=on 模块支持,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖。GO111MODULE=auto 在 $GOPATH/src 外面且根目录有 go.mod 文件时,开启模块支持。关于GO111MODULE有兴趣的可以参考这篇文章 【Why is GO111MODULE everywhere, and everything about Go Modules (updated with Go 1.17)】go mod命令go help mod查看相关帮助命令(govendor是第三方可执行文件,但是go mod命令是go自带的,不用额外go install)命令功能go mod init初始化当前文件夹,创建go.mod文件,事实上,如果你的环境中GO111MODULE=on,使用类似goland的工具创建工程会自动生成go.modgo mod tidy包整理(多的删去、少的拉取),使用之前自然是import了需要的库了go mod download下载依赖到本地(默认为 GOPATH/pkg/mod 目录)go mod vendor将依赖包复制到工程文件的vendor目录下在GO111MODULE=on且项目中包含go.mod文件时,执行go get xxx 或 go install xxx下载的包或者二进制可执行文件将放入GOPATH/pkg/mod目录和GOPATH/bin目录下。总结import后面的是目录包名和目录名没有关系,但是包名最好等于目录名同一个目录下只能有一种包名。import 后面的目录,如果不加./,直接xxx/xxx这种就是从goroot的src下面找下找,或者从gopath/pkg/mod(go mod模式时)、GOPATH/src(gopath)、或当前项目(gomod模式时其实就是以gomod里定义的当前项目名开头)。如果加了./(只能在非go.mod模式下使用),就是从当前文件目录查找go.mod 里的module就是当前项目的包名(目录名)Go mod模式下,第三方包在gopath/pkg/mod ;gopath模式下,第三方包在gopath/src下。
2021年06月09日
77 阅读
0 评论
1 点赞
2020-05-13
Go语言——结构体
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存,因此必须在定义结构体并实例化后才能使用结构体的字段。实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。Go语言可以通过多种方式实例化结构体,根据实际需要可以选用不同的写法。基本的实例化形式结构体本身是一种类型,可以像整型、字符串等类型一样,以 var 的方式声明结构体即可完成实例化。基本实例化格式如下:var ins T其中,T 为结构体类型,ins 为结构体的实例。用结构体表示的点结构(Point)的实例化过程请参见下面的代码: type Point struct { X int Y int } var p Point p.X = 10 p.Y = 20 type Point struct { X int Y int } var p Point p.X = 10 p.Y = 20在例子中,使用.来访问结构体的成员变量,如 p.X 和 p.Y 等,结构体成员变量的赋值方法与普通变量一致。创建指针类型的结构体Go语言中,还可以使用 new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。使用 new 的格式如下:ins := new(T)其中:T 为类型,可以是结构体、整型、字符串等。ins:T 类型被实例化后保存到 ins 变量中,ins 的类型为 *T,属于指针。Go语言让我们可以像访问普通结构体一样使用.来访问结构体指针的成员。下面的例子定义了一个玩家(Player)的结构,玩家拥有名字、生命值和魔法值,实例化玩家(Player)结构体后,可对成员进行赋值,代码如下: type Player struct{ Name string HealthPoint int MagicPoint int } tank := new(Player) tank.Name = "Canon" tank.HealthPoint = 300 type Player struct{ Name string HealthPoint int MagicPoint int } tank := new(Player) tank.Name = "Canon" tank.HealthPoint = 300经过 new 实例化的结构体实例在成员赋值上与基本实例化的写法一致。Go语言和 C/C++在 C/C++ 语言中,使用 new 实例化类型后,访问其成员变量时必须使用->操作符。在Go语言中,访问结构体指针的成员变量时可以继续使用.,这是因为Go语言为了方便开发者访问结构体指针的成员变量,使用了语法糖(Syntactic sugar)技术,将 ins.Name 形式转换为 (*ins).Name。取结构体的地址实例化在Go语言中,对结构体进行&取地址操作时,视为对该类型进行一次 new 的实例化操作,取地址格式如下:ins := &T{}其中:T 表示结构体类型。ins 为结构体的实例,类型为 *T,是指针类型。下面使用结构体定义一个命令行指令(Command),指令中包含名称、变量关联和注释等,对 Command 进行指针地址的实例化,并完成赋值过程,代码如下: type Command struct { Name string // 指令名称 Var *int // 指令绑定的变量 Comment string // 指令的注释 } var version int = 1 cmd := &Command{} cmd.Name = "version" cmd.Var = &version cmd.Comment = "show version" type Command struct { Name string // 指令名称 Var *int // 指令绑定的变量 Comment string // 指令的注释 } var version int = 1 cmd := &Command{} cmd.Name = "version" cmd.Var = &version cmd.Comment = "show version"代码说明如下:第 1 行,定义 Command 结构体,表示命令行指令第 3 行,命令绑定的变量,使用整型指针绑定一个指针,指令的值可以与绑定的值随时保持同步。第 7 行,命令绑定的目标整型变量:版本号。第 9 行,对结构体取地址实例化。第 10~12 行,初始化成员字段。取地址实例化是最广泛的一种结构体实例化方式,可以使用函数封装上面的初始化过程,代码如下: func newCommand(name string, varref *int, comment string) *Command { return &Command{ Name: name, Var: varref, Comment: comment, } } cmd = newCommand( "version", &version, "show version", )
2020年05月13日
792 阅读
0 评论
31 点赞
1
2