内容简介:#小引之前学习过goland,但一直没有总结过。最近自己一直在使用goland语言,回头又看了一遍GO圣经和其他资料,做了这个总结。
#小引
之前学习过goland,但一直没有总结过。最近自己一直在使用goland语言,回头又看了一遍 GO 圣经和其他资料,做了这个总结。
GOLAND学习
一、动词(verb ) golang术语在格式化打印时用
二、变量生命周期
声明:var a = "" or var a string 简短声明: a:=1 这种声明适用在函数内部 包级声明的变量生命周期:和整个程序运行周期一致。 局部声明的变量生命周期:动态的,从创建一个新变量的声明开始,到该变量不再被引用, 然后变量的存储空间可能被回收。
编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,不是用var或new声明变量的方式决定。
var global *int func f() { var x int x=1 global = &x //x指针赋值给全局变量,所以x从f函数中逃逸。此变量是堆变量 } func g() { y := new(int) *y = 1 //y虽然是new创建的,但是g函数调用结束后,无法再访问到y。 此变量是栈变量。 }
如果将指向短生命周期对象的指针保存到具有长生命周期的对象中,特别是保存到全局变量时,会阻止 对短生命周期对象的垃圾回收(从而可能影响程序的性能)
三、变量可赋值性
显式的赋值形式 var a int a = 1 程序中还有很多地方会发生隐式的赋值行为: 函数调用会隐式地将调 用参数的值赋值给函数的参数变量 如果用struct作参数,struct属性较多时使用指针。 一个返回语句将隐式地将返回操作的值赋值给结果变量 复合类型的字面量隐式赋值。 例如: medals := []string{"gold", "silver", "bronze"} 编绎器隐式地对slice的每个元素进行赋值操作 medals[0] = "gold" medals[1] = "silver" medals[2] = "bronze"
四、iota 常量生成器
常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用 每行都写一遍初始化表达式。在一个const声明语句中,在第一个声明的常量所在的行,iota将 会被置为0,然后在每一个有常量声明的行加一。 const ( x = iota // x == 0 y = iota // y == 1 z = iota // z == 2 w // 这里隐式地说w = iota,因此w == 3。其实上面y和z可同样不用"= iota" ) const v = iota // 每遇到一个const关键字,iota就会重置,此时v == 0 const ( h, i, j = iota, iota, iota //h=0,i=0,j=0 iota在同一行值相同 )
五、数组
值类型。函数参数变量接收的是一个复制的副本,所以数据复杂时传递是低效的。
数组顺序初始化 q :=[3]int{1,2,3} q2 :=[...]int{1,2,3} fmt.Printf("type:%T\n",q) fmt.Printf("type:%T\n",q2) 执行结果: type:[3]int type:[3]int 指定一个索引和对应值列表的方式初始化 const ( USD = iota //美元 EUR //欧元 GBP //英镑 RMB //人民币 ) sypmbol := [...]string{USD:"$",EUR:"€",GBP:"£",RMB: "¥"} fmt.Printf("type:%T\n",sypmbol) fmt.Printf("money:%v,sypmbol:%v\n",RMB,sypmbol[RMB]) 执行结果: type:[4]string money:3,sypmbol:¥ 省略索引初始化 r := [...]int{2:-1} 打印r结果: [0 0 -1]
六、切片slice
> 切片由三部分组成:指针、长度、容量。 > 指针指向第一个slice元素对应的底层数组元素的地址 > 长度对应slice中元素的数目;长度不能超过容量 > 容量一般是从slice的开始位置到底层数据的结尾位置 > 多个slice之间可以共享底层的数据,并且引用的数组部分区间可能重叠 q3:= []int{1,2,3} fmt.Printf("type:%T\n",q3) 执行结果: type:[]int sx定义了生肖数组 sx[0]=“”, 所以是一个13长度的数组。 sx := [...]string{1: "鼠",2:"牛",3:"虎",4:"兔",5:"龙", 6:"蛇",7:"马",8:"羊",9:"猴",10: "鸡",11:"狗",12:"猪",} [4:7] 是半开半闭区间,代表下标4到6,从下标4到数组结尾13一共9的长度,所以容量是9. sx1 := sx[4:7] sx2 := sx[6:9] fmt.Printf("sx:%v,len:%v,cap:%v\n",sx,len(sx),cap(sx)) fmt.Printf("sx1:%v, len:%v, cap:%v \n",sx1,len(sx1),cap(sx1)) fmt.Printf("sx2:%v, len:%v, cap:%v \n",sx2,len(sx2),cap(sx2)) 执行结果: sx:[ 鼠 牛 虎 兔 龙 蛇 马 羊 猴 鸡 狗 猪],len:13,cap:13 sx1:[兔 龙 蛇], len:3, cap:9 sx1:[蛇 马 羊], len:3, cap:7
找了一张图,内容不一样,原理相同 。
下面例子可以看出操作切片虽然超出了len,但是因为没有超出cap所以没有报错,并且自动从底层数组中取值。 sx3 := sx2[:5] 输出 sx3结果: sx3:[蛇 马 羊 猴 鸡] sx4:=sx2[:10] 由于操作超出cap,直接panic slice bounds out of range
结构体struct
嵌入:
第一种写法: type A struct { a string } type B struct { b string } type C struct { A A B B } c := C{ A:A{"a"}, B:B{"b"}, } fmt.Println(c.A.a) fmt.Println(c.a) //编绎时报错 第二种写法:匿名成员,可以直接使用匿名成员的成员及实现方法。 type C struct { A B } 可以直接.访问匿名成员中的成员 fmt.Println(c.A.a) fmt.Println(c.a)
函数 function
实参通过值的方式传递,因此函数的形参是实参的拷贝。对形参进行修改不会影响实参。但是,如果实 参包括引用类型,如指针,slice(切片)、map、function、channel等类型,实参可能会由于函数的简介 引用被修改。
一、错误处理策略
1、最常用的方式是传播错误,return err 2、偶然性的,或由不可预知的问题导致的。一个明智的选择是重新尝试失败的操作。在重试时,我们需要限制重试的时间间隔或重试的次数,防止无限制的重试.
func WaitForServer(url string) error{ const timeout = 1 * time.Minute deadline := time.Now().Add(timeout) for tries := 0 ;time.Now().Before(deadline);tries++ { _,err := http.Head(url) if err == nil { return nil } fmt.Println("server not responding;retrying....") time.Sleep(time.Second << uint(tries)) } return fmt.Errorf("server %s failed to respond after %s",url,timeout) }
3、输出错误信息并结束程序 这种策略只应在main中执行。对库函数而言,应仅向上传播错误。除非该错误意味着程序内部 包含不一致性,即遇到了bug,才能在库函数中结束程序。
if err := WaitForServer(url); err != nil { log.Fatalf("Site is down: %v\n", err) }
4、输出错误信息 5、忽略
二、错误处理
Go语言引入了一个关于错误处理的标准模式,即error接口
type error interface { Error() string } //自定义错误类型 type MyError struct { Msg string Time time.Time Err error } func (m *MyError) Error() string { return m.Time.String()+ m.Msg + m.Err.Error() }
defer
当defer语句被执行 时,跟在defer后面的函数会被延迟执行.多条defer按照先进后出原则执行。
用法: 一、关闭资源:打开、关闭、连接、断开连接、加锁、释放锁 二、计时
func bigSlowOperation(){ defer trace("bigSlowOperation")() time.Sleep(10*time.Second) } func trace(msg string) func() { star := time.Now() log.Printf("enter %s",msg) return func() { log.Printf("exit %s (%s)",msg,time.Since(star)) } } 调用bigSlowOperation函数,输出: 2018/12/17 15:11:34 enter bigSlowOperation 2018/12/17 15:11:44 exit bigSlowOperation (10.003011214s)
执行顺序后进前出,即使defer后执行函数panic,也不影响其他defer执行
func test(i int) { fmt.Println(100/i) } defer fmt.Println("1") defer fmt.Println("2") defer fmt.Println("3") defer test(0) defer fmt.Println("5")
输出:
panic recover
panic后的defer不会执行 recover只接收最近的panic
defer func() { p:=recover() fmt.Println(p) }() defer fmt.Println("1") defer fmt.Println("2") defer fmt.Println("3") defer test(0) defer fmt.Println("5") panic("my panic") 输出结果: 5 3 2 1 runtime error: integer divide by zero
三、匿名函数,闭包
匿名函数由一个不带函数名的函数声明和函数体组成,匿名函数可以直接赋值给一个变量或者直接执行。
//闭包的实现确保只要闭包还被使用,那么被闭包引用的变量会一直存在 func squares() func() int { var x int fmt.Println("x:",x) return func() int { j := 0 x++ j++ return x * j } } f := squares() //f变量指向一个闭包函数。闭包内变量j被隔离,可保证其安全性。 fmt.Println(f()) fmt.Println(f()) fmt.Println(f()) 执行结果: x: 0 1 2 3
方法
在声明一个method的receiver该是指针还是非指针类型时,你需要考虑两方面: 第一方面是这 个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝; 第二方面是如果你用指 针类型作为receiver,那么你一定要注意,这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝。
接口 interface
Go语言中接口类型的独特之处在于它是满足隐式实现的。 也就是说,我们没有必要对于给定的具体类型定义所有满足的接口类型。
接口内嵌 type ReadWriter interface { Reader Writer } 实现接口: 一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口 type C interface { Aprint() string Bprint() string Cprint() string } type AS struct { } func (as *AS) Aprint() string { return "A" } func (as *AS) Bprint() string { return "B" } func (as *AS) Cprint() string { return "C" } var c C c = new(AS)
接口赋值 一、将对象实例赋值给接口 首先,该对象实例必须实现接口要求的所有方法。例如上面的例子中:
var as *AS var c C c =as
二、将一个接口赋值给别一个接口 只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等同的,可以相互赋值。 或者接口A的方法列表是接口B的方法列表的子集, 那么接口B可以赋值给接口A,反过来不成立。
type Integer int type LessAdder interface { Less(b Integer) bool Add(b Integer) } func (a Integer) Less(b Integer) bool { return a < b } func (a *Integer) Add(b Integer) { *a += b } type LessAdder2 interface { Less(b Integer) bool } //Integer实现了LessAdder接口所有方法 //LessAdder2是LessAdder接口子集 var l1 LessAdder = new(Integer) var l2 LessAdder2 = l1
接口查询 查询某个对象实例是否实现了某个接口。
var l3 LessAdder2 aa, ok := l3.(LessAdder)
七、goruntine 协程
语法 :在函数或方法调用前加关键字 go
main函数在单独的goruntine中运行,叫main goruntine.
例子:
//每过一秒钟将当前时间返回给客户端 func handleConn(conn net.Conn) { defer conn.Close() for { _, err := io.WriteString(conn, time.Now().Format("15:04:05\n")) if err != nil { log.Info("write string to conn err.") return } time.Sleep(1 * time.Second) } } main方法启动,监听8888端口。等待客户端连接,连接成功执行handleConn listener, err := net.Listen("tcp", "localhost:8888") if err != nil { log.Fatal("listen address faild.") } for { conn, err := listener.Accept() if err != nil { log.Info("accept conn faild.") continue } handleConn(conn) }
//客户端,连接客户端 func main() { conn, err := net.Dial("tcp", "localhost:8888") if err != nil { log.Fatal(err) } defer conn.Close() mushCopy(os.Stdout, conn) } func mushCopy(dst io.Writer, src io.Reader) { if _, err := io.Copy(dst, src); err != nil { log.Fatal(err) } }
启动两个终端分别运行客户端代码,效果如下: 两个客户端同时只能有一个客户端可以打印时间。另一个等待中
将服务端代码进行修改:
go handleConn(conn)
之后两个客户端可以同时打印时间了。
八、channels
一个channels是一个 通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。
ch := make(chan int,cap) //cap大于零代表带缓存的通道 通道操作: channel <- value //发送value到channel <-channel //接收并将其丢弃 x := <-channel //从channel中接收数据,并赋值给x x, ok := <-channel //功能同上,同时检查通道是否已关闭或者是否为空 向已经关闭的channel发送数据会报panic异常。可以取数据,没有数据时取通道类型的零值。
8.1 无缓存的通道 这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。如果两个goroutine没有同时准备好,通道会导致先执行发送或接收操作的 goroutine 阻塞等待
下图中手进入通道模拟了上锁。也就是进入了阻塞。
8.2串联的通道---管道(pipeline) Channels也可以用于将多个goroutine链接在一起,一个Channels的输出作为下一个Channels的输入。这 种串联的Channels就是所谓的管道(pipeline)
8.3单方向channel ,只接受或者只发送
类型chan<- int表示一个只发送int的channel,只能发送不能接收。相反,类型<-chan int表示一个只接收int的channel,只能接收不能发送
任何双向的channel向单向channel变量赋值都将导致双向通道隐式转换为单向通道。
8.4带缓存的channel 这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。通道会阻塞发送和接收动作的条件也会不同。只有在通道中没有要接收的值时,接收动作才会阻塞。只有在通道没有可用缓冲区容纳被发送的值时,发送动作才会阻塞
func main() { respnose := make(chan string, 3) go func() { respnose <- request(time.Duration(1), "1") }() go func() { respnose <- request(time.Duration(2), "2") }() go func() { respnose <- request(time.Duration(3), "3") }() fmt.Println(<-respnose) } func request(d time.Duration, s string) string { time.Sleep(d * time.Second) return s }
如果使用无缓存,将导致两个慢的goroutine因为没有接收者而一直处于阻塞状态,这种情况叫goroutine泄露,是一个BUG,GC不会自动回收,所以使用时要考虑不再使用的goroutine可以正常退出。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Scratch少儿趣味编程
[ 日] 阿部和广 / 陶 旭 / 人民邮电出版社 / 2014-11 / 59.00元
Scratch 是麻省理工学院设计开发的一款编程工具,是适合少儿学习编程和交流的工具和平台,有中文版且完全免费。本书结合孩子们学习的语文、数学、科学、社会、音乐、体育等科目,手把手地教大家如何用Scratch 设计程序(如设计一个自动写作文的程序),配合各式卡通形象,通俗易懂,寓教于乐。麻省理工学院教授米切尔•瑞斯尼克作序推荐。 本书图文并茂,生动风趣,适合中小学生等初学者自学或在家长的帮助......一起来看看 《Scratch少儿趣味编程》 这本书的介绍吧!
RGB转16进制工具
RGB HEX 互转工具
Base64 编码/解码
Base64 编码/解码