go基础知识_持续更新
2022-06-08 22:09:55 2 举报
AI智能生成
登录查看完整内容
涵盖 go 基础知识
作者其他创作
大纲/内容
初始化生成 go.mod文件
init
go mod 命令go mod
go命令行会使用modules,而一点也不会去GOPATH目录下查找
GO111MODULE=on
当前目录在GOPATH/src之外且该目录包含go.mod文件
当前文件在包含go.mod文件的目录下面
默认值,go命令行将会根据当前目录来决定是否启用module功能
GO111MODULE=auto
go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找
GO111MODULE=off
GO111MODULE
go mod 配置
记录 dependency tree
go.sum 文件
go.mod
go mod 文件go.mod
go mod
检查可以升级的package
go list -m -u all
升级后会将新的依赖版本更新到go.mod *
go get -u need-upgrade-package
升级所有依赖
go get -u
升级到最新的修订版本
go get -u=patch
将会升级到指定的版本号version
go get package@version
替换无法直接获取的package
modules 可以通过在 go.mod 文件中使用 replace 指令替换成github上对应的库
replace ( golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a)
replace
列出当前模块及其所有依赖项
下载依赖
go mod download
导入依赖
go mod vendor
相关配置命令
https://www.jianshu.com/p/760c97ff644c
module versioning 版本控制
GOPATH
GOROOT
环境变量
代码开发必须在go path src目录下
依赖手动管理
依赖包没有版本可言
go path
解决了包依赖,一个配置文件就管理
依赖包全都下载到项目vendor下,每个项目都把有一份
govendor
模块是相关Go包的集合
modules是源代码交换和版本控制的单元
go命令直接支持使用modules,包括记录和解析对其他模块的依赖性
modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件
golang 1.11 新加的特性
配置
go mod 解决方式
类库依赖
所有源文件的第一行非注释语句
不能包含空白字符
每个目录中只能定义一个package
包所在的目录中可能有一些文件名是以_test.go为后缀的Go源文件以_test为后缀包名的测试外部扩展包都由go test命令独立编译,普通包和测试的外部扩展包是相互独立的
包的名字并不包含版本号后缀,而是yaml
一些依赖版本号的管理工具会在导入路径后追加版本号信息,例如“gopkg.in/yaml.v2”
限制条件
$GOROOT/src/
$GOPATH/pkg/mod/
当前路径
go.mod模块名声明中的相对路径
包路径
包的查找
import (\"fmt\"; \"math\")import ( \"fmt\" \"math\")
用圆括号组合导入包路径
import ( \"a/b/c\" c1 \"x/y/c\" // 将导入的包c定义别名为 c1 格式化 \"fmt\" // 将导入的包fmt定义别名为 格式化 m \"math\" // 将导入的包math定义别名为 m)
import ( \"crypto/rand\" mrand \"math/rand\" // alternative name mrand avoids conflict)
想同时导入两个有着名字相同的包,例如math/rand包和crypto/rand包,那么导入声明必须至少为一个同名包指定一个新的包名以避免冲突
可以定义别名
// 引用普通名称的导入包c.hello()// 引用定义别名的包格式化.Println(m.Pi)
引用包名是导入包路径的最后一个目录中定义的唯一包的名称定义的包名与目录同名时,直接引用即可
// 源文件路径: proj/my-util/util.go// 定义包名: utilpackage util
// 导入util包路径import \"proj/my-util\"// 引用util包util.doSomething()
定义的包名与所在目录名称不同时,导入包路径仍为目录所在路径,引用包名为定义的包名称
// 类似C中的include 或Java中的import staticimport . \"fmt\"// 然后像使用本包元素一样使用fmt包中可见的元素,不需要通过包名引用Println(\"no need package name\")
静态导入,在导入的包路径之前增加一个小数点 .
// 如果当前go源文件中未引用过log包,将会导致编译错误import \"log\" // 错误import . \"log\" // 静态导入未使用同样报错// 在包名前面增加下划线表示导入包但是不直接使用它,被导入的包中的init函数会在导入的时候执行import _ \"github.com/go-sql-driver/mysql\"
导入包但不直接使用该包,在导入的包路径之前增加一个下划线 _被导入的包中的init函数会在导入的时候执行
包的导入规则
Unicode包含的大写字母
名称首字符为Unicode包含的大写字母的元素是被导出的,对外部包是可见的
首字为非大写字母的元素只对本包可见(同包跨源文件可以访问,子包不能访问)
internal包及其子包中的导出元素只能被与internal同父包的其他包访问
internal包(内部包)
规范导入包路径
导入包后,定义/声明的常量、变量、类型,作用域是当前包范围,可以被当前包中所有函数调用
span style=\
包 package
格式化 main.go 文件的源码输出到终端
gofmt main.go
格式化 main.go 文件并回写到原文件
gofmt -w main.go
格式化 Tasks 目录下的所有文件并且将格式化内容保存到对应源文件
gofmt -w Tasks
gofmt
会从$GOROOT目录下的标准库(library)和$GOPATH/src 目录下的所有项目中的代码注释中生成文档
godoc --http=:8080
net/http 包的文档可以通过 localhost:8080/pkg/net/http 查看
web
godoc -v 可以查看 godoc启动扫描详情
godoc 可以直接查看 go的标准库文档
go doc net/http
在终端输出 net/http 的文档
命令行
godoc
golang 内置 testing 支持
以_test.go为后缀名的源文件在执行go build时不会被构建成包的一部分
go test 包名或者cd 某个包目录下 && go test
运行 gotest 的时候执行 file_test.go 里面的用例
是以Test为函数名前缀的函数,用于测试程序的一些逻辑行为是否正确
go test命令会调用这些测试函数并报告测试结果是PASS或FAIL
测试函数
以Benchmark为函数名前缀的函数,它们用于衡量一些函数的性能
go test命令会多次运行基准测试函数以计算一个平均的执行时间
基准测试(benchmark)函数
以Example为函数名前缀的函数,提供一个由编译器保证正确性的示例文档
示例函数
*_test.go文件中,有三种类型的函数
有3个测试函数包含不同的测试文件,这3个函数分别是:TestFrenchPalindromeTestCanalPalindromeTestChinaPalindrome
上面这个正则的函数名是TestFrenchPalindrome 和TestCanalPalindrome
go test -run=”French|Canal”
相关命令
go test
执行 go build构建应用程序,编译器会把所有 *.go 除了*_test.go 进行编译
想给编译的文件指定名称,使用 go build -o tasks
```shenv GOOS=darwin GOARCH=386 go build -o tasks.appenv GOOS=windows GOARCH=386 go build -o tasks.exeenv GOOS=linux GOARCH=amd64 go build -o tasks```
- 构建时间(整个应用+依赖库,编译为一个二进制文件)- 交叉编译(跨平台)
Go的编译不是以go文件为最小单位的,而是以包为最小单位的
go build只在编译程序对应的目标操作系统是Linux或Mac OS X时才编译这个文件
// +build linux darwin
该构建注释则表示不编译这个文件
将一个特别的构建注释参数放在包声明语句之前可以提供更多的构建过程控制
限制于约束
在任何目录执行
go会在环境变量找到GOPATH/src,然后再拼接common_crawler
寻找依赖包的时候,go会在 GOPATH/src 和 GOROOT/src下找依赖包
go build common_crawler 过程
go build
编译包文件(无main包),将编译后的包文件放到 pkg 目录下($GOPATH/pkg)
编译生成可执行文件(有main包),将可执行文件放到 bin 目录($GOPATH/bin)
加上 -i 参数都会安装依赖包到pkg并且编译二进制执行文件
go install
在命令行直接运行应用的入口文件或者直接执行某个文件
会自动编译(编译后文件在临时目录,运行结束会自动删除)
go run
go 内置的 package 管理工具,可以直接从 github 等代码管理站点获取包
go get
_obj/_test/_testmain.gotest.out
build.out*.[568ao]DIR(.exe)IDR . test(.exe)MAINFILE(*.exe)
go clean
-diff : 不将修正后的内容写入文件,而只打印修正前后的内容的对比信息到标准输出
-r : 只对目标源码文件做有限的修正操作
-force : 使用此标记后,即使源码文件中的代码已经与Go语言的最新版本相匹配了,也会强行执行指定的修正操作
参数
是命令go tool fix的简单封装
对旧程序调用的代码更换为对新程序调用的代码、把旧的语法更换为新的语法,等等
会把指定代码包的所有Go语言源码文件中的旧版本代码修正为新版本的代码
代码包的所有Go语言源码文件不包括其子代码包中的文件
说明这里所说的版本即Go语言的版本
go fix
Go语言程序查看和诊断工具
依次列出 PID,PPID,程序名称,编译使用的 Go 版本号,程序路径
https://www.cnblogs.com/snowInPluto/p/7785651.html
gops 还能进行程序的诊断
gops
go version 获取 go 安装包版本go env 获取 golang 环境变量go list 当前目录下的包
其他
go tools
配置环境
go get 链接
go mod tidy
https://goproxy.io/zh/
在Linux 或 macOS下# 启用 Go Modules 功能export GO111MODULE=on# 配置 GOPROXY 环境变量export GOPROXY=https://goproxy.io在windows下# 启用 Go Modules 功能$env:GO111MODULE=\"on\"# 配置 GOPROXY 环境变量$env:GOPROXY=\"https://goproxy.io\"
代理
第三方类库下载
它是谷歌chrome官方无头框架puppeteer的python版本 https://miyakogi.github.io/pyppeteer/reference.html
Pyppeteer
它是一个Node库,可以使用Node.js来实现控制Chrome或Chromiumhttp://www.puppeteerjs.com/
Puppeeer
是由Go语言编写,支持Chrome DevTools Protocol的一个库,不需要依赖其他的外界服务(如Selenium和PhantomJS) https://github.com/chromedp/chromedp
Chromedp
相关语言
协议 CDP
chromedp.NewContext() 初始化chromedp的上下文,后续这个页面都使用这个上下文进行操作chromedp.Run() 运行一个chrome的一系列操作chromedp.Navigate() 将浏览器导航到某个页面chromedp.WaitVisible() 等候某个元素可见,再继续执行。chromedp.Click() 模拟鼠标点击某个元素chromedp.Value() 获取某个元素的value值chromedp.ActionFunc() 再当前页面执行某些自定义函数chromedp.Text() 读取某个元素的text值chromedp.Evaluate() 执行某个js,相当于控制台输入jsnetwork.SetExtraHTTPHeaders() 截取请求,额外增加header头chromedp.SendKeys() 模拟键盘操作,输入字符chromedp.Nodes() 根据xpath获取某些元素,并存储进入数组chromedp.NewRemoteAllocatorchromedp.OuterHTML() 获取元素的outer htmlchromedp.Screenshot() 根据某个元素截图page.CaptureScreenshot() 截取整个页面的元素chromedp.Submit() 提交某个表单chromedp.WaitNotPresent() 等候某个元素不存在,比如“正在搜索。。。”chromedp.Tasks{} 一系列Action组成的任务
Chromedp使用
https://github.com/chromedp/chromedp
TOML 的目标是成为一个极简的配置文件格式
TOML 被设计成可以无歧义地被映射为哈希表,从而被多种语言解析
https://github.com/BurntSushi/toml
github.com/BurntSushi/toml
golang的测试框架
assert虽然也标记为case失败,但case不会退出,而是继续往下执行
assert package
require的函数会直接导致case结束
require package
package
常用的stretchr/testify框架函数
github.com/stretchr/testify
https://pkg.go.dev/github.com/Shopify/sarama
doc
github.com/Shopify/sarama
deprecated
This repository has been archived by the owner. It is now read-only.
github.com/bsm/sarama-cluster
kafka官网推荐的golang package
github.com/confluentinc/confluent-kafka-go
支持 RestFull 风格 API
支持 GET,POST,PUT,PATCH,DELETE,OPTIONS 等 http 方法
支持文件上传,分组路由,Multipart/Urlencoded FORM
支持 JsonP,参数处理等等功能
一个轻量级的 WEB 框架
https://gin-gonic.com/docs/
github.com/gin-gonic/gin
Go support for Protocol Buffers
github.com/golang/protobuf
Gorilla WebSocket is a Go implementation of the WebSocket protocol.
github.com/gorilla/websocket
go implementation of strftime
github.com/jehiah/go-strftime
A simple fake clock for Go
github.com/jonboulle/clockwork
Provide an io.Writer that periodically rotates log files from within the application. Port of File::RotateLogs from Perl to Go
github.com/lestrrat-go/file-rotatelogs
Fast strftime for Go
github.com/lestrrat-go/strftime
基于Go语言的BDD(Behavior Driven Development)测试框架,一般用于Go服务的集成测试
github.com/onsi/ginkgo
Ginkgo's Preferred Matcher Library
·
xid is a globally unique id generator thought for the web
github.com/rs/xid
github.com/sirupsen/logrus
第三方类库
有符号整数 int8 int16 int32 int64 int
无符号整数类型
uintptr
float32 、 float64
浮点数
数字类型
var b boolb = 1 // 编译错误b = bool (1) // 编译错误
bool 类型
一个部分是指针,指向了底层的一个字节数组
一个部分是字节数组的长度
一个引用类型
解释型
非解释型
分类
相关字符类型
string
complex64 complex128
复数
基础类型
常量可以是字符、字符串、布尔或数值类型的值,数值常量是高精度的值
const a = 0 // a intconst ( b = 2.3 // b float64 c = true // c bool)
根据常量值自动推导类型
const ( a = 3 // a = 3 b // b = 3 c // c = 3 d = len(\"asdf\
常量组内定义时复用表达式常量组内定义的常量只有名称时,其值会根据上一次最后出现的常量表达式计算相同的类型与值
const a int = iota // a = 0const b int = iota // b = 0const c byte = iota // c = 0const d uint64 = iota // d = 0
iota的枚举值可以赋值给数值兼容类型每个常量单独声明时,iota不会自动递增
const ( a uint8 = iota // a = 0 b int16 = iota // b = 1 c rune = iota // c = 2 d float64 = iota // d = 3 e uintptr = iota // e = 4)
const ( a = \"A\" b = 'B' c = iota // c = 2 d = \"D\" e = iota // e = 4)
即使iota不是在常量组内第一个开始引用,也会按组内常量数量递增
const ( a = iota // a int32 = 0 b // b int32 = 1 c // c int32 = 2)
枚举的常量都为同一类型时,可以使用简单序列格式(组内复用表达式)
const ( a byte = iota // a uint8 = 0 b // b uint8 = 1 c // c uint8 = 2 d rune = iota // d int32 = 3 e // e int32 = 4 f // f int32 = 5)
枚举序列中的未指定类型的常量会跟随序列前面最后一次出现类型定义的类型
const ( a = iota // a int32 = 0 b // b int32 = 1 c // c int32 = 2)const ( e = iota // e int32 = 0 (iota重新初始化并自增) f // f int32 = 1)
iota自增值只在一个常量定义组合中有效,跳出常量组合定义后iota初始值归0
定制iota序列初始值与步进值 (通过组合内复用表达式实现)
自动递增枚举常量 iota
常量
通过取地址操作符&获取指向值/引用对象的指针
内置函数new(T)分配了一个零初始化的 T 值,并返回指向它的指针
使用*读取/修改指针指向的值
结构体字段/方法可以通过结构体指针来访问,通过指针间接的访问是透明的
fmt.Println(s.A)fmt.Println((*s).A)
指针使用点号来访问结构体字段
在指针引用多层对象时,指针是针对引用表达式的最后一位元素
跨层指针元素的使用
指针 Pointer
&
|
^
位清除 &^
<<
>>
位运算
逻辑运算符 == 、!= 、 > 、 >= 、 <=
算术运算符 + 、-、* 、 / 、% 、++ 、--
运算符优先级
运算符
进制
基础语法-类型
数组是值类型默认零值不是nil,传递参数时会进行复制
使用...自动计算数组的长度
var a = [3]int{2:3}var b = [...]string{2:\"c\
初始化指定索引的数组元素,未指定初始化的元素保持默认零值
数组 Array
引用类型,所以未初始化时的默认零值是nil,长度与容量都是
有 3 个域的结构体:指向相关数组的指针,切片长度以及切片容量
内存结构
声明格式: var slice1 []type(不需要说明长度)初始化格式:var slice1 []type = arr1[start:end]注:slice1是由数组 arr1 从 start 索引到 end-1 索引之间的元素构成的子集
内置函数make初始化slice,第一参数是slice类型,第二参数是长度,第三参数是容量(省略时与长度相同)
初始化、声明
复制
追加
追加/修改元素
sl = sl[0:len(sl)+1]
切片在达到容量上限后可以扩容
切片移动
http://www.zbpblog.com/blog-221.html
基于一个切片创建一个切片
重组
对比数组
var digitRegexp = regexp.MustCompile(\"[0-9]+\
举个例子:实现将一个文件加载到内存,然后搜索其中所有的数字并返回一个切片的函数FindDigits()下面代码虽然可以顺利运行,但返回的 []byte 指向的底层是整个文件的数据。只要该返回的切片不被释放,垃圾回收器就不能释放整个文件所占用的内存。因此,导致因为一点点有用的数据却占用了整个文件的内存
切片的内存回收
切片 Slice
传递时会复制值,其默认零值
结构体是值类型
// 声明变量a为空的匿名结构体类型var a struct{}// 声明变量b为包含一个字段的匿名结构体类型var b struct{ x int }// 声明变量c为包含两个字段的匿名结构体类型var c struct { u int v bool}
匿名结构体声明时省略了type关键字,并且没有名称
匿名结构体
声明、定义
通过结构体字段的值作为列表来新分配一个结构体
var s S = S{B: \"1\
使用 Name: 语法
结构体初始化
结构体 Struct
map是引用类型
var m map[int]intm[0] = 0 // × runtime error: assignment to entry in nil mapfmt.Printf(\"type: %T\\
var map6 map[string]intfmt.Println(map6 == nil) // truemap6[\"num1\"] = 1 // 报错,不能为一个没有指向哈希表的map变量进行元素赋值fmt.Println(map6)// var声明后,正确的定义方式是var map6 map[string]intmap6 = map[string]int{} // 先给map6赋值一个空的哈希表,底层创建出一个哈希表,此时才可以为map6添加元素。或者直接在赋值这里创一个有元素的哈希表map6[\"num1\"] = 1fmt.Println(map6)
未初始化的map值为 nil 长度为0,并且不能赋值元素
map2 := map[string]string { \"name\" : \"zbp\
直接赋值初始化map
m[0] = 3 // 修改m中key为0的值为3m[4] = 8 // 添加到m中key为4值为8a := n[\"a\
读取、添加、修改、删除元素
声明初始化使用
字典/映射 Map
将切片或者结构体转化为json字符串
json.Marshal函数
返回格式化后的json字符串,该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进
json.MarshalIndent函数
第一参传入一个byte类型的内容
第二参传入一个指定的结构体类型的指针用于接收格式化为struct后的json数据
可以将json字符串转为指定的结构体类型
只返回错误信息
json.Unmarshal函数
基础类库
不能在要json化的结构体中使用首字母小写的成员
json
基础语法-复合类型
添加档案分类
档案分类编辑
Text和HTML模板
encoding/xml
logrus包和seelog包
archive/zip包
type Logger struct { mu sync.Mutex // ensures atomic writes; protects the following fields prefix string // prefix on each line to identify the logger (but see Lmsgprefix) flag int // properties out io.Writer // destination for output buf []byte // for accumulating text to write}
Logger类型
Print系列(Print|Printf|Println)
Fatal系列(Fatal|Fatalf|Fatalln)
Panic系列(Panic|Panicf|Panicln)
预定义的“标准”logger
创建新的Logger
log
io.Reader
Write方法会先把内容写入到缓冲区中,不会直接刷到磁盘,只有在所有Write都结束之后才会写入到磁盘。如果希望能够每次写入后都进行刷盘的话,可以每次调用完Write后调用f.Sync()方法。所以如果是复制一个大文件例如几G的视频的时候,要注意要手动调用Sync方法刷盘,否则内存可能会被撑爆。
io.Writer
io
ioutil
F开头的函数可以传入io.Writer和io.Reader指定从哪个可读或可写对象中读取或输出
f结尾的函数可以用占位符格式化如 Fprintf Sprintf Printf Fscanf Sscanf Scanf
S开头的函数不打印而是只返回给变量
ln结尾的函数会在打印时换行
Fscanln与Fscan的区别在于,前者会把换行当作结束符空格当作分隔符,后者会把换行和空格都当作分隔符
fmt
bufio
path和filepath包
IO 及文件操作
type File struct { *file // os specific}type file struct { pfd poll.FD name string dirinfo *dirInfo // nil unless directory being read appendMode bool // whether file is opened for appending}
文件(非目录的文件)用文件对象os.*File来表示
type FileInfo interface { Name() string // base name of the file Size() int64 // length in bytes for regular files; system-dependent for others Mode() FileMode // file mode bits ModTime() time.Time // modification time IsDir() bool // abbreviation for Mode().IsDir() Sys() interface{} // underlying data source (can return nil)}
os.FileInfo
os
文件系统操作
container
标准类库
func name(parameter-list) (result-list) { body}
声明格式
使用关键字func声明函数,函数可以没有参数或接受多个参数
可变参数只能声明为函数的最后一个参数
当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略
可变数量的参数: 在函数参数类型之前使用...声明该参数
函数可以返回任意数量的返回值
会默认返回 r1 和 r2, 这种返回方式叫做 bare return,bare return 可以减少代码的重复,但是使得代码可读性降低
函数返回结果参数,可以像变量那样命名和使用
返回值
参数和返回值在函数调用的时候就会自动声明
函数声明 Declare
形式参数列表和返回值列表中的变量类型一一对应
函数的签名
传值
传指针引用传递
Go语言没有默认参数
函数传参
执行到return语句之后,但真正返回一个值之前的时候
执行到return
发生panic异常的时候
在f所在函数释放之前执行
用于记录函数的执行情况和信息
使用defer来查看或者改变函数的返回值
defer 函数
func append
func cap(v Type) int
func cap
func close(c chan<- Type)
func close
func complex
func imag(c ComplexType) FloatType
func imag
func copy
m := map[int]string{ 0: \"A\
func delete
func len(v Type) int
func len
func make
func new(Type) *Type
func new
func panic(v interface{})
panic函数
func println(args ...Type)
func println
func real(c ComplexType) FloatType
func real
func recover() interface{}
Recover函数
内建(内置)函数
是用于程序执行前做包的初始化工作的函数
init函数的声明没有参数和返回值
一个package或go源文件可以包含零个或多个init函数
init函数被自动调用,在main函数之前执行,不能在其他函数中调用,显式调用会报错该函数未定义
所有init函数都会被自动调用,调用顺序
初始化函数 init
主函数 main
匿名函数(函数闭包 Closure)
函数 function
方法不区分静态方法和实例方法
Go 方法是作用在接收者(receiver)上的一个函数;
类似于oop编程中的self或者this,但是go中的接收器名词可以自定义,不一定非得定义为this或者self,一般会定义为类名的小写或者首字母
面向对象编程
方法是和类型相关联而不是和某一个对象相关联
为某一个类型定义一个方法,那么使用该类型声明的变量就都可以调用这个方法,但是不能专门为一个变量定义方法
结构体中的成员当成是对象的成员属性
为这个新类型定义的方法看成是类方法
定义一个以结构体类型为底层类型的新类型来定义一个类
方法的接收者receiver是类型的值时,编译器会隐式的生成一个同名方法,其接收者receiver为该类型的指针,反过来却不会
特性
方法值
方法表达式
一个新类型只要有一个方法使用指针作为接收器,那么这个新类型的其他所有方法也要用指针而不是变量值作为接收器
go 设计原则
定义方法的一般格式如下:func (recv receiver_type) methodName(parameter_list)(return_value_list) { ... }
接收器
匿名字段为值类型时:值的方法会传递给结构体的值,指针的方法会传递给结构体的指针
匿名字段为指针类型时:指针的方法会传递给值和指针
匿名字段为接口类型时:方法会传递给值和指针
继承
方法 Method
type Namer interface { Method1(param_list) return_type Method2(param_list) return_type ...}
接口定义格式
接口嵌套接口
*ByteCouter和io.Writer是同一类型,和io.ReadWriter不是
A.一个类必须实现这个接口的所有方法才算实现了这个接口
B.实现方法的时候的接收器才是和这个接口是同一类型
C.当一个类型除了实现了一个接口的所有方法之外还有其他方法的话,当把这个类型声明为接口类型的话,这个类型的其他方法会被隐藏而无法使用
D. 空接口 interface{} 可以被任何类型的方法实现,但是由于空接口没有指定任何方法,所以实现了空接口的类型不能调用它自己原有的所有方法
接口实现的条件
两个接口值相等
接口值
空接口
函数签名
接口 Interface
x.(T),T是要断言的类型,x是接口值
语法
断言只能用于接口值变量不能用于普通的变量,否则会报错。
我们可以用1个或者2个变量接收断言的返回结果
关键点
判断对象(这个对象必须是一个接口值)是否有某一个或某多个方法
用途
类型断言
函数、方法、接口
一个程序有多个goroutine在运行,其中任何一个goroutine发生panic异常都会终止所有的goroutine,然后程序结束
由Go运行时环境管理的轻量级线程
使用关键字go调用一个函数/方法,启动一个新的协程goroutine
协程 Goroutine
定义
一种引用类型,它对应着底层的一种数据结构
是引用类型,使用make函数来初始化。未初始化的channel零值是nil,且不能用于发送和接收值
var c0 chan int // 可用来发送和接收int类型的值var c1 chan<- int // 可用来发送int类型的值var c2 <-chan int // 可用来接收int类型的值
<-ch // 这样也可以,相当于弹出一个值但不赋给任何变量
操作符<-用于指定channel的方向,发送或接收如果未指定方向,则为双向channel
channel的关闭一般都是在发送消息的goroutine,而不是在接收消息的goroutine
特点
带缓冲的channel在缓冲区已满时发送方会阻塞,直到接收方从channel中取出值
带缓存
无缓冲的channe中有值时发送方会阻塞,直到接收方从channel中取出值
不带缓存
导致panic异常
关闭的
发送
如果goroutine 1从一个带缓存的channel(channel有消息的话)接收消息,则不会阻塞;如果channel为空就会阻塞
接收方在channel中无值会一直阻塞
如果channel中还有消息,则可以不阻塞的接收到这些消息
如果channel中没有消息,则会不阻塞的不停的接收到相应类型的零值,不会引发panic
关闭的(对带不带缓存的channel都相同)
接收
一个没有任何case的select语句写作select{},会永远地等待下去
运行到select时,所有的case的channel都阻塞,则运行default的代码块
运行到select时,所有的case的channel都阻塞,而且没有default这一项,则select会阻塞直到有一个channel可读或可写
运行到select时,有多个channel都处于可接收或可发送的状态,select会随机选择一个channel执行
select
传递指针更高效
传递信号使用 空struct
超时检测
结合 Timer 限制频率
for range
设计模式
channel 的设计模式
channel的使用
Channels
基于共享内存(使用互斥锁的例子)
临界区
原理
互斥锁
并发 Concurrency
Kind()
Field(x)
reflect.TypeOf(x)
Elem
Field
Type
Interface
NumFiled
Method
reflect.ValueOf(x)
基于反射的代码是比较脆弱的。对于每一个会导致编译器报告类型错误的问题,在反射中都有与之相对应的误用问题,不同的是编译器会在构建时马上报告错误,而反射则是在真正运行到的时候才会抛出panic异常,可能是写完代码很久之后了,而且程序也可能运行了很长的时间
即使对应类型提供了相同文档,但是反射的操作不能做静态类型检查,而且大量反射的代码通常难以理解。总是需要小心翼翼地为每个导出的类型和其它接受interface{}或reflect.Value类型参数的函数维护说明文档
于反射的代码通常比正常的代码运行速度慢一到两个数量级
慎用反射
反射 reflect
Go是采用语法解析器自动在每行末尾增加分号
for循环使用分号把初始化、条件和遍历元素分开
if/switch的条件判断带有初始化语句时使用分号分开初始化语句与判断语句
在一行中有多条语句时,需要增加分号
需要手工增加分号:
控制语句(if,for,switch,select)、函数、方法 的左大括号不能单独放在一行, 语法解析器会在大括号之前自动插入一个分号,导致编译错误
分号/括号 ; {
if (i < 0 || i > 10) { println(i)}if i < 0 { println(i)} else if i > 5 && i <= 10 { println(i)} else { println(i)}
if语句 小括号 ( )是可选的,而大括号 { } 是必须的
if i := 0; (i < 1) { // 编译通过. println(i)}if i := 0; i < 0 { // 使用gofmt格式化代码会自动移除代码中不必要的小括号( ) println(i)} else if i == 0 { println(i)} else { println(i)}
可以在条件之前执行一个简单的语句,由这个语句定义的变量的作用域仅在 if / else if / else 范围之内
if语句作用域范围内定义的变量会覆盖外部同名变量
// 如果传入x类型为int,则可以直接获取其值 a := x.(int) println(a) // 如果传入x类型不是byte,则会产生恐慌panic b := x.(byte) println(b)
if判断语句类型断言
条件语句 if
switch x {case 0: println(\"single const\
switch存在分支选择对象时,case分支支持单个常量、常量列表
switch x *= 2; x {case 4: { println(\"single const\
分支选择对象之前可以有一个简单语句,case语句的大括号可以省略
switch x /= 3; {case x == 8: println(\"expression\")case x >= 9: println(\"expression\")default: println(\"default\")}
switch只有一个简单语句,没有分支选择对象时,case分支支持逻辑表达式语句
switch {case x == 10: println(\"expression\")case x >= 11: println(\"expression\")default: println(\"default\")}
switch没有简单语句,没有分支选择对象时,case分支支持逻辑表达式语句
switch类型分支,只能在switch语句中使用的.(type)获取对象的类型
使用fallthrough强制向下继续执行后面的case代码
在类型分支中不允许使用fallthrough语句
switch中每个case分支默认带有break效果,一个分支执行后就跳出switch,不会自动向下执行其他case
分支选择 switch
Go只有一种循环结构:for 循环
可以让前置(初始化)、中间(条件)、后置(迭代)语句为空,或者全为空
for语句中小括号 ( )是可选的,而大括号 { } 是必须的
Go的for each循环for range
循环的继续、中断、跳转
for range只支持遍历数组、数组指针、slice、string、map、channel类型
循环语句 for
任意某个通信可以进行,它就执行,其他被忽略
如果任意一个通讯都可以进一步处理,则从中随机选择一个,执行对应的语句
否则在没有默认分支(default case)时,select语句则会阻塞,直到其中一个通讯完成
Go 不会重新对 channel 或值进行求值
select 每个 case 都必须是一个通信要么是发送要么是接收
Select用于当前goroutine从一组可能的通讯中选择一个进一步处理
func main() { for { select { case <-time.Tick(time.Second): println(\"Tick\") case <-time.After(5 * time.Second): println(\"Finish\") default: println(\"default\") time.Sleep(5e8) } }}
select只会执行一次case分支的逻辑,与for组合使用实现多次遍历分支
通道选择 select
package mainfunc main() { defer print(0) defer print(1) defer print(2) defer print(3) defer print(4) for i := 5; i <= 9; i++ { defer print(i) } // 输出:9876543210}
defer语句调用函数,将调用的函数加入defer栈,栈中函数在defer所在的主函数返回时执行,执行顺序是先进后出/后进先出
package mainfunc main() { println(f()) // 返回: 15}func f() (i int) { defer func() { i *= 5 }() return 3}
defer在函数返回后执行,可以修改函数返回值
mu.Lock()defer mu.Unlock()
释放锁
ch <- \"hello\"defer close(ch)
关闭channel
关闭IO流
关闭数据库连接
defer用于释放资源
defer用于恐慌的截获
延迟执行 defer
goto用于在一个函数内部运行跳转到指定标签的代码处,不能跳转到其他函数中定义的标签
package mainfunc main() { i := 0loop: i++ if i < 5 { goto loop } println(i)}
goto模拟循环
跳转语句 goto
// 向一个未初始化的channel中写入数据会永久阻塞(chan int)(nil) <- 0// 从一个未初始化的channel中读取数据会永久阻塞<-(chan struct{})(nil)for range (chan struct{})(nil){}// select无任何选择分支会永久阻塞select{}
永久阻塞语句
阻塞语句
语句 Statement
封装
用组合实现;内嵌包含想要的行为(字段和方法)的类型;
数据和方法是一种松耦合的正交关系,可以不混在一起定义,数据和方法是独立的令具有更大的灵活性。
多重继承可以通过一个类型内嵌多个类型( 接口方法继承要内嵌类型的指针)来实现
都是必须提供一个指定方法集的实现,但更加灵活通用;
不需要显式地声明类型是否满足某个接口,任何提供了接口方法实现代码的类型都隐式地实现了该接口,而不用显式地声明
多态
面向对象设计
变量的命名规则遵循骆驼命名法,即首个单词小写,每个新单词的首字母大写,例如:numShips 和 startDate。
但如果你的全局变量希望能够被外部包所使用,则需要将首个单词的首字母也大写
对于布尔值而言,建议以 is 或者 Is 开头的 isSorted、isFinished、isVisible
变量命名
当某个函数需要被外部包调用的时候才使用大写字母开头,并遵循 Pascal 命名法;
否则就遵循骆驼命名法,即第一个单词的首字母小写,其余单词的首字母大写。
返回某个对象的函数或方法的名称一般都是使用名词,没有 Get... 之类的字符。
修改某个对象,则使用 SetName,有必须要的话可以使用大小写混合的方式,如 MixedCaps 或 mixedCaps,而不是使用下划线来分割多个名称。
函数、方法命名
接口的名字由方法名加 [e]r 后缀组成,例如 Printer、Reader、Writer、Logger、Converter 等等。
还有一些不常用的方式(当后缀 er 不合适时),比如 Recoverable,此时接口名以 able 结尾,或者以 I 开头(像Java 中那样)。
接口类型
错误类型以 “Error” 结尾错误变量以 “err” 或 “Err” 开头。
错误类型
标准库中有许多包含Must 前缀的函数,像 regexp.MustComplie 和 template.Must;即当正则表达式或模板中转入的转换字符串导致错误时,这些函数会 panic
panic
测试
命名规范
它会被用在 fmt.Printf() 中生成默认的输出:等同于使用格式化描述符 %v 产生的输出
广泛使用一个自定义类型时,最好为它定义 String() 方法
Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法
1)在包内部,总是应该从 panic 中 recover:不允许显式的超出包范围的 panic ()
2)向包的调用者返回错误值(而不是 panic)
Go 库的原则是在包的内部使用了 panic,在它的对外接口(API)中也必须用 recover 处理成返回显式的错误
编程规范
调试范例
使用tips
go 基础知识
收藏
收藏
0 条评论
回复 删除
下一页