大家好,我是明哥。自学Golang的那段时间,我写了详细的学习笔记,放在了我的个人微信上公众号《Go编程时光》。我也是Go语言的初学者,所以我写的应该更适合刚入门的人。如果你刚开始学习Go语言,不妨关注一下,一起学习成长。我的在线博客:http://golang.iswbm.com我的Github:github.com/iswbm/GolangCodingTime1。单行导入vs.多行导入在Go语言中,一个包可以包含多个.go文件(这些文件必须在同一个文件夹中),只要这些.go文件的头部都声明同一个包使用包关键字。导入包有两种方式:单行导入import"fmt"import"sync"多行导入import("fmt""sync")可以看到,Go语言导入的包必须用双引号括起来,在这里投诉。2.使用别名在某些场景下,我们可能需要对导入的包进行重命名。例如,当我们导入两个包名相同的包时,就会发生冲突。在这种情况下,为其中一个包定义一个别名import("crypto/rand"mrand"math/rand"//将名称替换为mrand以避免冲突)我们导入了一个名称很长的包。为了避免后面写这么长的包名列表,我们可以定义aliasimporthw"helloworldtestmodule"来防止导入的包名和局部变量冲突,比如path,一个很常用的变量名,和导入的标准包冲突.importpathpkg"path"3、使用点操作如果我们在程序中经常使用一个工具包,比如fmt,那么每次使用它的打印功能进行打印,都需要使用包名+方法名。对于这种高频包,可以在导入的时候定义为“自己人”(方法是用一个.)。如果是自己人,不管对方,它的方法就是我们的方法。从此以后,我们不再需要添加fmt来打印了。进口。"fmt"funcmain(){Println("hello,world")}但是这种用法有一定的隐患,就是导入的包中可能有函数,会和我们自己的函数冲突。4.包初始化每个包都允许有一个init函数。导入包时,会执行包的init函数,做一些初始化工作。init函数的执行有两点需要注意。init函数在包引用链中先于main函数执行,包的初始化是深度优先的。比如有这样一个包引用关系:main→A→B→C,那么初始化顺序就是C.init→B.init→A.init→main5。包的匿名导入当我们导入一个包的时候,如果这个包没有被使用,编译的时候就会报错。但有些情况下,我们导入一个包,只想执行包中的init函数来运行一些初始化任务。这个时候怎么办?可以使用匿名导入,用法如下,其中下划线为空白标识,不可访问//注册一个PNG解码器import_"image/png"由于导入时会执行init函数,所以包还是会在编译成可执行文件时被编译。6.导入的是路径还是包?我们在使用import导入testmodule/foo的时候,初学者经常会问,这个foo是一个包,还是只是包所在目录的名字?import"testmodule/foo"为了得出这个结论,专门做了个实验(请看第七点的代码示例),最后的结论是:导入的时候,是按照目录导入的。导入一个目录后,该目录下的所有包都可以使用。出于习惯,包名和目录名通常设置为相同,这样会给你一种导入包的错觉。7、相对导入和绝对导入据我了解,在Go1.10之前,好像不支持相对导入,Go1.10之后才有。绝对导入:从$GOPATH/src或$GOROOT或$GOPATH/pkg/mod目录中搜索包并导入相对导入:从当前目录中搜索包并开始导入。就像下面的import("./module1""../module2""../../module3""../module4/module5")我们分别举个例子。1.使用绝对导入有如下目录结构(确保当前目录在GOPATH下)其中main.go就是这样一个包mainimport("app/utilset"//这是一个绝对路径导入)funcmain(){utils.PrintHello()}和在main.go同级目录下,还有一个文件夹utilset。为了让大家理解“第6点:import导入的是路径而不是包”,我在utilset目录下定义了一个hello.go文件。这个go文件定义属于包utils。packageutilsimport"fmt"funcPrintHello(){fmt.Println("你好,我在utilset目录下的utils包中")}运行结果如下2.使用相对导入还是上面的代码,改成绝对导入后torelativeimport设置回GOPATH路径(请对比上面使用绝对路径的GOPATH),再次运行总结一下。使用相对导入,有两点需要注意,项目不要放在$GOPATH/src下,否则会报错(比如我修改当前项目目录为GOPATH后会报错运行时)GoModules不支持相对导入。启用GO111MODULE后,将无法使用相对导入。最后不得不说:使用相对导入的方式,项目的可读性会大大降低,开发者也不会习惯于理清整个引用关系。所以一般推荐使用绝对引用。如果使用绝对引用,就又要谈优先级了。8.包导入路径的优先级上一节介绍了三种不同的包依赖管理方案。不同的管理模式可能有不同的存放包的路径。有些Packages可以放在GOPATH下,有些包可以放在vendor下,有些包是built-in包放在GOROOT下。那么问题来了,如果这三个不同的路径下都存在一个包名相同但是版本不同的包,那么我们在导入的时候,应该选择导入哪个呢?这就需要我们明白,Golang中包搜索路径的优先级是什么?这时候就需要区分使用哪种模式进行包管理。如果我们在导入包时使用govendor,它会:先从项目根目录的vendor目录开始搜索,最后从$GOROOT/src目录开始搜索,然后再从$GOPATH/src目录开始搜索。如果找不到,就会报错。为了验证这个过程,我在创建时创建了vendor目录后,开启了vendor模式。我在main.go中随机导入了一个包pkg。由于这个包是我随机指定的,当然是找不到的。Find如果没有达到会报错,Golang会在错误信息中打印查找过程。从这个信息可以看出Golang的包搜索的优先级。如果你使用gomodules,如果你导入的包有域名,会先在$GOPATH/pkg/mod下查找。如果找不到,请到网站上搜索。而如果你导入的包没有域名(比如“fmt”),则只会在$GOROOT中查找。还有一点很重要,当你的项目下有vendor目录的时候,不管你的包有没有域名,你只会在vendor目录下找。通常vendor目录是由gomodvendor命令生成的,该命令会将所有项目依赖打包到你项目目录下的verdor文件夹中。扩展阅读如何使用gomodule导入本地包系列指南01.搭建开发环境(Goland&VSCode)02.学习变量创建的五种方法03.数据类型详解:整数和浮点数04.详解数据类型:byte、rune和string05。数据类型详解:array和slice06。数据类型详解:字典和布尔类型07.数据类型详解:pointer08。面向对象编程:结构和继承09.一篇了解Go10中函数的文章。Go语言流程控制:if-else条件语句11.Go语言流程控制:switch-case选择语句12.Go语言流程控制:for循环语句13.Go语言流程控制:goto无条件跳转14.Go语言流程控制:defer延迟调用15.面向对象编程:接口和多态16.关键词:make和new的区别?17.一篇理解Go18中语句块和作用域的文章。学习Go协程:goroutine19。学习Go协程:channels/channels20详解。通道死锁的几个经典错误案例详解21。学习Go协程:WaitGroup22。学习Go协程:互斥锁和读写锁23。Go中的异常处理:panic和recover24。超详细解读GoModules的前世今生和入门使用25。Go语言包导入的八个必学知识Point26.如何将自己写的模块开源给别人使用?27.说说Go语言中的类型断言?28.这五点带你看懂Go语言中select的使用
