博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
go语言错题集(坑)【三】
阅读量:4099 次
发布时间:2019-05-25

本文共 6058 字,大约阅读时间需要 20 分钟。

系列相关:

目录

 


不要对Go并发函数的执行时机做任何假设

请看下列的列子:

12345678910111213141516
import (	"fmt"	"runtime"	"time")func main(){	names := []string{"lily", "yoyo", "cersei", "rose", "annei"}	for _, name := range names{		go func(){			fmt.Println(name)		}()	}	runtime.GOMAXPROCS(1)	runtime.Gosched()}

请问输出什么?

答案:

12345
anneianneianneianneiannei

为什么呢?是不是有点诧异?

输出的都是“annei”,而“annei”又是“names”的最后一个元素,那么也就是说程序打印出了最后一个元素的值,而name对于匿名函数来讲又是一个外部的值。因此,我们可以做一个推断:虽然每次循环都启用了一个协程,但是这些协程都是引用了外部的变量,当协程创建完毕,再执行打印动作的时候,name的值已经不知道变为啥了,因为主函数协程也在跑,大家并行,但是在此由于names数组长度太小,当协程创建完毕后,主函数循环早已结束,所以,打印出来的都是遍历的names最后的那一个元素“annei”。
如何证实以上的推断呢?
其实很简单,每次循环结束后,停顿一段时间,等待协程打印当前的name便可。

1234567891011121314151617
import (	"fmt"	"runtime"	"time")func main(){	names := []string{"lily", "yoyo", "cersei", "rose", "annei"}	for _, name := range names{		go func(){			fmt.Println(name)		}()		time.Sleep(time.Second)	}	runtime.GOMAXPROCS(1)	runtime.Gosched()}

打印结果:

12345
lilyyoyocerseiroseannei

以上我们得出一个结论,不要对“go函数”的执行时机做任何的假设,除非你确实能做出让这种假设成为绝对事实的保证。

假设T类型的方法上接收器既有T类型的,又有*T指针类型的,那么就不可以在不能寻址的T值上调用*T接收器的方法

请看代码,试问能正常编译通过吗?

1234567891011121314151617181920
import (	"fmt")type Lili struct{	Name string}func (Lili *Lili) fmtPointer(){	fmt.Println("poniter")}func (Lili Lili) fmtReference(){	fmt.Println("reference")}func main(){	li := Lili{}	li.fmtPointer()}

答案:

1
能正常编译通过,并输出"poniter"

感觉有点诧异,请接着看以下的代码,试问能编译通过?

12345678910111213141516171819
import (	"fmt")type Lili struct{	Name string}func (Lili *Lili) fmtPointer(){	fmt.Println("poniter")}func (Lili Lili) fmtReference(){	fmt.Println("reference")}func main(){	Lili{}.fmtPointer()}

答案:

123
不能编译通过。“cannot call pointer method on Lili literal”“cannot take the address of Lili literal”

是不是有点奇怪?这是为什么呢?其实在第一个代码示例中,main主函数中的“li”是一个变量,li的虽然是类型Lili,但是li是可以寻址的,&li的类型是*Lili,因此可以调用*Lili的方法。

一个包含nil指针的接口不是nil接口

请看下列代码,试问返回什么

123456789101112131415161718192021
import (	"bytes"	"fmt"	"io")const debug = truefunc main(){	var buf *bytes.Buffer	if debug{		buf = new(bytes.Buffer)	}	f(buf)}func f(out io.Writer){	if out != nil{		fmt.Println("surprise!")	}}

答案是输出:surprise。

ok,让我们吧debug开关关掉,及debug的值变为false。那么输出什么呢?是不是什么都不输出?

123456789101112131415161718192021
import (	"bytes"	"fmt"	"io")const debug = falsefunc main(){	var buf *bytes.Buffer	if debug{		buf = new(bytes.Buffer)	}	f(buf)}func f(out io.Writer){	if out != nil{		fmt.Println("surprise!")	}}

答案是:依然输出surprise。

这是为什么呢?

这就牵扯到一个概念了,是关于接口值的。概念上讲一个接口的值分为两部分:一部分是类型,一部分是类型对应的值,他们分别叫:动态类型和动态值。类型系统是针对编译型语言的,类型是编译期的概念,因此类型不是一个值。
在上述代码中,给f函数的out参数赋了一个*bytes.Buffer的空指针,所以out的动态值是nil。然而它的动态类型是bytes.Buffer,意思是:“A non-nil interface containing a nil pointer”,所以“out!=nil”的结果依然是true。
但是,对于直接的``
bytes.Buffer``类型的判空不会出现此问题。

1234567891011
import (	"bytes"	"fmt")func main(){	var buf *bytes.Buffer	if buf == nil{		fmt.Println("right")	}}

还是输出: right

只有 接口指针 传入函数的接口参数时,才会出现以上的坑。
修改起来也很方便,把*bytes.Buffer改为io.Writer就好了。

123456789101112131415161718
import (	"bytes"	"fmt"	"io")const debug = falsefunc main(){	var buf  io.Writer //原来是var buf *bytes.Buffer	if debug{		buf = new(bytes.Buffer)	}	f(buf)}func f(out io.Writer){	if out != nil{		fmt.Println("surprise!")	}}

将map转化为json字符串的时候,json字符串中的顺序和map赋值顺序无关

请看下列代码,请问输出什么?若为json字符串,则json字符串中key的顺序是什么?

12345678910
func main() {	params := make(map[string]string)	params["id"] = "1"	params["id1"] = "3"	params["controller"] = "sections"	data, _ := json.Marshal(params)	fmt.Println(string(data))}

答案:输出{"controller":"sections","id":"1","id1":"3"}

利用Golang自带的json转换包转换,会将map中key的顺序改为字母顺序,而不是map的赋值顺序。map这个结构哪怕利用for range遍历的时候,其中的key也是无序的,可以理解为map就是个无序的结构,和php中的array要区分开来

Json反序列化数字到interface{}类型的值中,默认解析为float64类型

请看以下程序,程序想要输出json数据中整型id加上3的值,请问程序会报错吗?

123456789101112
func main(){	jsonStr := `{"id":1058,"name":"RyuGou"}`	var jsonData map[string]interface{}	json.Unmarshal([]byte(jsonStr), &jsonData)	sum :=  jsonData["id"].(int) + 3	fmt.Println(sum)}``` 答案是会报错,输出结果为:

panic: interface conversion: interface {} is float64, not int

1
使用 Golang 解析 JSON  格式数据时,若以 interface{} 接收数据,则会按照下列规则进行解析:

bool, for JSON booleans

float64, for JSON numbers

string, for JSON strings

[]interface{}, for JSON arrays

map[string]interface{}, for JSON objects

nil for JSON null

1234567891011
应该改为:```gofunc main(){	jsonStr := `{"id":1058,"name":"RyuGou"}`	var jsonData map[string]interface{}	json.Unmarshal([]byte(jsonStr), &jsonData)	sum :=  int(jsonData["id"].(float64)) + 3	fmt.Println(sum)}

即使在有多个变量、且有的变量存在有的变量不存在、且这些变量共同赋值的情况下,也不可以使用:=来给全局变量赋值

:=往往是用来声明局部变量的,在多个变量赋值且有的值存在的情况下,:=也可以用来赋值使用,例如:

12
msgStr := "hello wolrd"msgStr, err := "hello", errors.New("xxx")//err并不存在

但是,假如全局变量也使用类似的方式赋值,就会出现问题,请看下列代码,试问能编译通过吗?

123456789101112131415
var varTest stringfunc test(){	varTest, err := function()	fmt.Println(err.Error())}func function()(string, error){	return "hello world", errors.New("error")}func main(){	test()}

答案是:通不过。输出:

1
varTest declared and not used

但是如果改成如下代码,就可以通过:

12345678910111213141516
var varTest stringfunc test(){	err := errors.New("error")	varTest, err = function()	fmt.Println(err.Error())}func function()(string, error){	return "hello world", errors.New("error")}func main(){	test()}

输出:

1
error

这是什么原因呢?

答案其实很简单,在test方法中,如果使用varTest, err := function()这种方式的话,相当于在函数中又定义了一个和全局变量varTest名字相同的局部变量,而这个局部变量又没有使用,所以会编译不通过。

*interface 是一个指向interface的指针类型,而不是interface类型

请问以下代码,能编译通过吗?

123456789101112131415161718192021222324252627
import (	"fmt")type Father interface {	Hello()}type Child struct {	Name string}func (s Child)Hello()  {}func main(){	var buf  Child	buf = Child{}	f(&buf)}func f(out *Father){	if out != nil{		fmt.Println("surprise!")	}}

答案是:不能编译通过。输出:

1
*Father is pointer to interface, not interface

注意了:接口类型的变量可以被赋值为实现接口的结构体的实例,但是并不能代表接口的指针可以被赋值为实现接口的结构体的指针实例。即:

1
var buf Father = Child{}

是对的,但是

1
var buf *Father = new(Child)

却是不对的。应该改为:

12
var buf Father = Child{}var pointer *Father = &buf

要想让问题最开始的代码编译通过要将以上代码修改为:

123456789101112131415161718192021222324252627
import (	"fmt")type Father interface {	Hello()}type Child struct {	Name string}func (s Child)Hello()  {}func main(){	var buf  Father	buf = Child{}	f(&buf)}func f(out *Father){	if out != nil{		fmt.Println("surprise!")	}}

 

转载地址:http://rzwsi.baihongyu.com/

你可能感兴趣的文章
maven 安装第三方jar包到本地仓库&安装第三方jar包到私服
查看>>
文件下载工具包DownLoadUtils
查看>>
经典基础编程50题
查看>>
dubbox简介
查看>>
spring boot 聚合工程 报错repackage failed: Unable to find main class -> [Help 1] 解决方法:
查看>>
Oracle Start With的用法
查看>>
1.oracle中的exists 和not exists 用法:
查看>>
python opencv 霍夫变换
查看>>
python OpenCV 模版匹配
查看>>
OPenCV 图像透视变换矫正
查看>>
python-OpenCV图像轮廓边缘检测
查看>>
python-OpenCV几何变换
查看>>
CSRT跟踪算法的使用
查看>>
python-OpenCV-鼠标交互
查看>>
java-抽象与接口来输出电脑的显卡
查看>>
python-OpenCV-答题卡识别
查看>>
python-OpenCV信用卡数字识别
查看>>
Java程序员面试必备的一些流程图
查看>>
使用Redis实现延时任务
查看>>
日志排查问题困难?分布式日志链路跟踪来帮你
查看>>