关于go的疑问
这必然是一路风景
1、go的时间time.sleep()其中的参数默认单位不是秒而是微秒 go中有专门的时间值time.Second/Minute/Hour...
2、channel的 make(chan int, arg)第二个参数定义channel buffer,默认是unbuffer,单线程使用的时候必须要定义,否则会被阻塞(unbuffer意思是只要我收到了,我就会阻塞在这个地方,等人来取,单线程就会直接报错了编不过,例子如下)https://www.jianshu.com/p/f12e1766c19f
func Ping(ping chan<- int, s int) {
ping <- s
}
func Pong(ping <-chan int, pong chan<- int) {
msg := <-ping
pong <- msg
}
func ChannelDirect() {
pi := make(chan int) // 会报错当ping <- s的时候
po := make(chan int)
Ping(pi, 1)
Pong(pi, po)
fmt.Println(<-po)
}
3、fmt.printf("%T", arg) 判断arg的类型
4、channel synchronization 其实就是通过在goroutine里面放置channel的方式,保证goroutine中的代码运行完毕
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
<-done // 这里阻塞直到goroutine运行完
}
5、goroutines leaking 永远不要在不知道它将如何停止的情况下启动一个goroutine,因为是并发的,所以会一直占用内存,其中一种情况就是channel没有设置buffer,则默认为阻塞的,如果channel阻塞且正好在goroutines中,则会造成此goroutine永远释放不了
6、数组和切片的区别:1.定义的时候数组必须定长度或者[...]让机器判定长度,切片则不用。多维数组只有第一层能使用[...]
7、数组传递的是值,是值类型,map和slice传递的是指针
8、切片是对底层数组的进一层封装,所以切片是由数组切来的时候,并不是复制了一份,而是直接的引用,改变数组的值会同时更改切片的值,切片的赋值也有相同效果,赋值的结果是复制了引用,底层数组是同一个
9、当切片append元素时,如果append后长度未超出cap长度,则依然适用上面的规则,如果append后超出了,则会复制一个新的底层数组重新指向,此时再更改上面的数组就不再影响slice
func ArraySlice() {
new_array := [3]int{1, 3, 5}
new_slice := new_array[:2]
fmt.Println(new_slice)
new_slice = append(new_slice, 9, 10)
new_array[1] = 7
fmt.Println(new_slice)
fmt.Println(cap(new_slice))
}
stdout-->
[1 3]
[1 3 9 10]
6 // 原来是3
9、切片没有删除方法,可以append(slice[:index], slice[index+1:]...),删除掉index位置的元素就是从index+1开始append到index位置其实,注意第二个后面的三个点是go的语法糖,必须要加的,不然会直接append一个切片上去,其实就类似python的*args的*
10、切片是否为空必须要用len是否为0来判断,不能弄nil
11、new与make的区别:
-
二者都是用来做内存分配的。
-
make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
-
而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
-
new和&在用于结构体时没什么区别,
a := &simple_struct{} a := new(simple_struct) 输出的都是*simple_struct
但是对于基本类型有区别,例如不可能出现这种语法 a := &int() 只能是a:=new(int)
12、map是没有cap的,所以make(map, 111)第二个参数是没用的(目前暂时认为,不确定),map也是一个指向底层数据的指针,是引用类型
13、go的goroutine并发不是越多越好,因为协程是go的内部处理调度,绑定的是固定几个cpu的内核线程,所以当goroutine到达一定数量,cpu被一直满载占用,此时再创建协程运算效率就并不会有什么明显提升了,而go中协程的调度负荷反而会越来越重,有可能还会造成性能的下降(个人理解)
14、for index, stu := range stus {},这个时候index和stu是固定的内存空间,range的作用把stus解析后赋值给前面两个变量,前面两个变量的地址是不变的,所以在这里面尽量不要有关于前面两个变量的指针操作
15、go语言指针的使用需要声明后用new分配内存空间,struct的属性占用连续的内存空间,排列方法是属性内存和结构体内存的对齐,可以从这个方面优化大体积结构体
16、go语言的方法接收者用指针比较常见,struct是值类型,如果接收者是值类型则修改只在局部函数中生效,方法可以对任意本地类型生效,例如type MyInt int,那么给MyInt加一个方法完全可以
什么时候应该使用指针类型接收者
需要修改接收者中的值
接收者是拷贝代价比较大的大对象
保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
补充:接收者如果是值类型,那么当对象是指针类型是可以直接使用的,因为go加了语法糖自动*obj取值了,反之接收者如果是指针类型,不能使用值类型对象。
17、go的继承通过结构体的嵌套实现,被嵌套的结构体是父类,结构体变量大写开头为公开,小写为私有(一定要注意,json.Marshal就不会读取私有的,如果是tag 小写是可以的例如`json:"name"`)
18、defer是延迟语句,执行在函数return值的时候(没有return其实就是默认return nil猜测,因为defer是可以正常执行的,但是我用goland无法获取无返回值的值会直接报错), defer延迟了语句执行,但是没有延迟传参操作,如果defer之后更改了defer函数的入参值,defer时更改是不会生效的,因为当传参时是进行了copy操作,{补充:函数传参是值拷贝形式}
func Test(A int ) {
d := &A
fmt.Printf("%p\n", &A)
defer func(a int) {
fmt.Printf("%p\n", &a)
fmt.Println(a)
}(A)
*d = 12
fmt.Println(A)
fmt.Println("in Test")
}
0xc00000a200
12
in Test
0xc00000a230 // 和上面指针已经不一样了,所以是复制了一份
11
19、go tool compile -S XXX.go 可以把go编译到汇编不生成binary
20、go语言错误处理机制panic相当于python的raise,recover相当于try/except,不过recover必须搭配defer使用并且位置在panic之前,因为panic是直接抛出异常如果在后面就直接报错了无法捕获(写到最开头不就完了)
21、interface{}虽然好用,最好少用
22、函数返回值中所用名字,此函数中不能再次初始化定义
func test() A string {
A = "hello"
}
此时不能用 A:=
23、判定类型时可以使用a.(类型),同时也是类型的强转,返回两个值,第一个值是转化后数据,第二个值是类型判定true还是false,或者用switch可以写成a.(type),只输出true或false,之后用case判定输出类型
24、go语言写入文件语句
w1, err = os.OpenFile(".\\logtest.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644),最后一个是文件的permission,类比linux 777 755,第二个参数可以点进去看源码,对应写文件模式
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
而w1.WriteString(msg.(string))输出两个值,第一个值是写入文件的字节数,第二个是err
25、如果方法带参数,接口也必须带参数
26、reflect.ValueOf(x).Elem()返回x指针值,作用是什么?这个reflect要单独开一章讲
27、unicode.IsLetter、unicode.IsDigit、unicode.IsSpace判断字符类型,传参int32一般需要转化一下
28、strings.ToLower strings.ToUpper转化大小写
29、go数组是值类型,可以通过赋值来copy,而切片是引用类型的,需要用copy方法,一维切片可以用浅copy,多维切片需要深copy,因为内部切片也是引用,浅copy复制的是引用不能完全复制。具体方法参照此连接