e := errors.New("原始错误e")
w := fmt.Errorf("Wrap了一个错误%w", e)
C <-chan Time 用法
type Timer struct {
C <-chan Time
r runtimeTimer
}
C <-chan Time 表示C 是一个chan 类型,且chan 是只可读的,channel 中的类型是Time类型
将函数作为入参(回调函数),能带来便利。如日志处理,为了统一处理,将信息均通过指定函数去记录日志,且是否记录日志还有开关
func Log(title string, getMsg func() string) {
//如果开启日志记录,则记录日志
if true {
fmt.Println(title, ":", getMsg())
}
}
//---------调用--------------
count := 0
msg := func() string {
count++
return "您没有即使提醒我,已触犯法律"
}
Log("error", msg)
Log("warring", msg)
Log("info", msg)
fmt.Println(count)
package main
import "fmt"
func main() {
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received ", i1, " from c1\n")
case c2 <- i2:
fmt.Printf("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
fmt.Printf("received ", i3, " from c3\n")
} else {
fmt.Printf("c3 is closed\n")
}
default:
fmt.Printf("no communication\n")
}
}
interface即interface{}可以看作任意类型, 即C中的void *
v interface{}
v.(string) //类型转换
v.(net.Conn).Close()
解决:interface conversion: interface {} is float64, not int
w := video["width"]
h := video["height"]
if w != nil && h != nil {
width := int(w.(float64))
height := int(h.(float64))
logger.Infof("width:%d, height:%d", width, height)
if width != 0 && height != 0 {
output.Dimensions = strconv.Itoa(width) + "x" + strconv.Itoa(height)
}
}
4. go适合做什么
- 服务端开发
- 分布式系统,微服务
- 网络编程
- 区块链开发
- 内存KV数据库,例如boltDB、levelDB
- 云平台
1.1.5. 学习Go语言的前景
目前Go语言已经⼴泛应用于人工智能、云计算开发、容器虚拟化、⼤数据开发、数据分析及科学计算、运维开发、爬虫开发、游戏开发等领域。
Go语言简单易学,天生支持并发,完美契合当下高并发的互联网生态。Go语言的岗位需求持续高涨,目前的Go程序员数量少,待遇好。
抓住趋势,要学会做一个领跑者而不是跟随者。
国内Go语言的需求潜力巨大,目前无论是国内大厂还是新兴互联网公司基本上都会有Go语言的岗位需求。
Go语言GOPATH详解(Go语言工作目录)在Windows上安装Go语言开发包
Goland入门指南(使用Goland创建并运行项目) //my_json "baseline_client/json" 旧写法
my_json "github.com/angeek/bclient/json"
1. 通道channel
ch <- v // 发送值v到Channel ch中
v := <-ch // 从Channel ch中接收数据,并将数据赋值给v
chan的分类
分为带缓存和不带缓存这2类,尤其需要关注带缓存的用法,防止掉坑里。
不带缓存
make(chan 数据类型)
带缓存
make(chan 数据类型,长度)
例如定义一个带缓存的chan: ch := make(chan int,2)
这里我们定义个缓存长度为2的chan,当我们已经往chan中写入了2个数据,当再次写入第三个数据的时候就会发送阻塞,直到其他人从该chan中读取了数据,那么才可以再次写入数据,带缓存的chan类似于一个队列,当队列满的时候是无法写入数据的。
3. chan的关闭
if runtime.GOOS == "windows" {
path = string(path[0 : strings.LastIndex(path, "\\")])
} else {
path = string(path[0 : strings.LastIndex(path, "/")])
}
chan十分类似我们Python中的队列Queue不是吗?
其实,缓冲信道是先进先出的,我们可以把缓冲信道看作为一个线程安全的队列:
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
for v := range ch {
fmt.Println(v)
if len(ch) <= 0 { // 如果现有数据量为0,跳出循环
break
}
}
close(ch)
被关闭的信道会禁止数据流入, 是只读的。我们仍然可以从关闭的信道中取出数据,但是不能再写入数据了。
无缓冲的信道是一批数据一个一个的「流进流出」
缓冲信道则是一个一个存储,然后一起流出去
2. 切片
切片简介
数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型Slices切片(“动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。切片中有两个概念:一是len长度,二是cap容量,长度是指已经被赋过值的最大下标+1,可通过内置函数len()获得。容量是指切片目前可容纳的最多元素个数,可通过内置函数cap()获得。切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。
append函数将x追加到切片s的末尾,并且在必要的时候增加容量。
a := make([]int, 1)
// a == []int{0}
a = append(a, 1, 2, 3)
// a == []int{0, 1, 2, 3}
s := arr[startIndex:endIndex]
将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片
var counters []IOCountersStat
counters = append(counters, c)
3. make
返回指针数组
fun a()([]*url.URL, error){
u := new(URL)
return []*url.URL{u}, nil
}
务必记得 make 仅适用于 map,slice 和 channel,并且返回的不是指针。应当用 new 获得特定的指针。
var p *[] int = new ([] int ) //← 分配 slice 结构内存;很少使用
var v [] int = make ([] int , 100) //← v 指向一个新分配的有 100 个整数的数组
//==========================
var p *[] int = new ([] int ) //← 不必要的复杂例子
*p = make ([] int , 100, 100)
//==========================
v := make ([] int , 100) //← 更常见
make 只能为 slice、map或 channel 类型分配内存并初始化,同时返回一个有初始值的 slice、map 或 channel 类型引用,不是指针。具体如何使用 make 呢,官网中 https://golang.org/ref/spec#Making_slices_maps_and_channels 有介绍
对于make slice而言,有两个概念需要搞清楚:长度跟容量。
容量表示底层数组的大小,长度是你可以使用的大小。
容量的用处在哪?在与当你用 appen d扩展长度时,如果新的长度小于容量,不会更换底层数组,否则,go 会新申请一个底层数组,拷贝这边的值过去,把原来的数组丢掉。也就是说,容量的用途是:在数据拷贝和内存申请的消耗与内存占用之间提供一个权衡。
而长度,则是为了帮助你限制切片可用成员的数量,提供边界查询的。所以用 make 申请好空间后,需要注意不要越界【越 len 】
切片增长不能超出其容量。增长超出切片容量将会导致运行时异常,就像切片或数组的索引超出范围引起异常一样。同样,不能使用小于零的索引去访问切片之前的元素。
4. string
upper := strings.ToUpper(strData) //将小写变大写
_, err = conn.WriteToUDP([]byte(upper), rAddr) //将string转为切片[]byte
既然string就是一系列字节,而[]byte也可以表达一系列字节,那么实际运用中应当如何取舍?
- string可以直接比较,而[]byte不可以,所以[]byte不可以当map的key值。
- 因为无法修改string中的某个字符,需要粒度小到操作一个字符时,用[]byte。
- string值不可为nil,所以如果你想要通过返回nil表达额外的含义,就用[]byte。
- []byte切片这么灵活,想要用切片的特性就用[]byte。
- 需要大量字符串处理的时候用[]byte,性能好很多。
最后脱离场景谈性能都是耍流氓,需要根据实际场景来抉择。
参考链接
Golang也支持面向对象编程。但与以前学过传统的面向对象编程语言有区别。
1)Golang没有类class,Go语言的结构体struct和类class有相似的特性。
2)Golang中不存在继承,方法重载,构造函数,析构函数,隐藏和this指针。
3)Golang有继承,封装,多态的特性,但是实现方法与传统OOP语言不同。
数组
动态数组应该这样写
array := make([]int, length)
出现线程安全问题,可以使用sync包或者goroutine和channel来解决这个问题。
switch case
go的switch case默认有break。如果不需要break,可以加上fallthrough
反射:可以在运行时动态获取变量的相关信息
Import (“reflect”)
package main
import "fmt"
type Callback func (x, y int) int
//提供一个接口,让外部去实现
func test(x, y int, callback Callback) int {
return callback(x, y)
}
func add(x, y int) int {
return x + y
}
func main() {
x, y := 6, 7
z := test(x, y, add)
fmt.Printf("z = %d",z)
}
Golang接口----flag.Parse()
flag.Value接口的用法,我们看到的很多代码如果使用了flag包那么它的主函数中总会有flag.Parse(),这个函数的主要是把用户传递的命令行参数解析为对应变量的值。可能会有些人比较疑惑,这句话什么意思我们来看一下代码就好,但在看代码之前我们先来了解一下命令行传参的格式: