SpecialPrint特殊的输出结果
循环range问题
-
解答
这样的写法初学者经常会遇到的,很危险! 与Java的foreach一样,都是使用副本的方式。所以m[stu.Name]=&stu实际上一致指向同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝
package main
import "fmt"
type student struct {
Name string
Age int
}
func main() {
m := make(map[string]*student)
stus := []student{
{Name: "zhou", Age: 24},
{Name: "li", Age: 23},
{Name: "wang", Age: 22},
}
// 错误写法
for _, stu := range stus {
m[stu.Name] = &stu
}
for _, stu := range stus {
stu.Age = stu.Age + 10
}
for k, v := range m {
println(k, "=>", v.Age, v.Name)
}
fmt.Println("================")
//正确
for i := 0; i < len(stus); i++ {
m[stus[i].Name] = &stus[i]
}
for k, v := range m {
println(k, "=>", v.Name, v.Age)
}
}
# 输出
zhou => 22 wang
li => 22 wang
wang => 22 wang
================
zhou => zhou 24
li => li 23
wang => wang 22
defer执行顺序
-
解答
defer 是后进先出。 协程遇到panic时,遍历本协程的defer链表,并执行defer。在执行defer过程中,遇到recover则停止panic,返回recover处继续往下执行。如果没有遇到recover,遍历完本协程的defer链表后,向stderr抛出panic信息。从执行顺序上来看,实际上是按照先进后出的顺序执行defer
package main
import (
"fmt"
"log"
)
func main() {
defer_call()
}
func defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
# 输出
打印后
打印中
打印前
panic: 触发异常
goroutine 1 [running]:
main.defer_call()
D:/gohere/go-newbase/test_go/for/one/main.go:16 +0x85
main.main()
D:/gohere/go-newbase/test_go/for/one/main.go:8 +0x27
exit status 2
defer输出进阶版
package main
import (
"fmt"
)
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("1", a, calc("10", a, b))
a = 0
defer calc("2", a, calc("20", a, b))
b = 1
}
# 输出
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4
go执行的随机性和闭包
-
解答
其中A:输出完全随机,取决于goroutine执行时i的值是多少;
而B:一定输出为0~9,但顺序不定。第一个go func中i是外部for的一个变量,地址不变化,但是值都在改变。
第二个go func中i是函数参数,与外部for中的i完全是两个变量。
尾部(i)将发生值拷贝,go func内部指向值拷贝地址。所以在使用goroutine在处理闭包的时候,避免发生类似第一个go func中的问题。
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
runtime.GOMAXPROCS(1)
wg := sync.WaitGroup{}
wg.Add(20)
for i := 0; i < 10; i++ {
go func() {
fmt.Println("A: ", i)
wg.Done()
}()
}
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println("B: ", i)
wg.Done()
}(i)
}
wg.Wait()
}
# 输出
B: 9
A: 10
A: 10
A: 10
A: 10
A: 10
A: 10
A: 10
A: 10
A: 10
A: 10
B: 0
B: 1
B: 2
B: 3
B: 4
B: 5
B: 6
B: 7
B: 8
go组合继承
-
解答
这是Golang的组合模式,可以实现OOP的继承。 被组合的类型People所包含的方法虽然升级成了外部类型Teacher这个组合类型的方法(一定要是匿名字段),但它们的方法(ShowA())调用时接受者并没有发生变化。 此时People类型并不知道自己会被什么类型组合,当然也就无法调用方法时去使用未知的组合者Teacher类型的功能。
package main
import "fmt"
type People struct{}
func (p *People) ShowA() {
fmt.Println("showA-People")
p.ShowB()
}
func (p *People) ShowB() {
fmt.Println("showB-People")
}
type Teacher struct {
People
}
func (t *Teacher) ShowB() {
fmt.Println("teacher showB")
}
func main() {
t := Teacher{}
t.ShowA()
}
# 输出
showA-People
showB-People
select随机性
-
解答
select会随机选择一个可用通用做收发操作。 单个chan如果无缓冲时,将会阻塞。但结合 select可以在多个chan间等待执行。有三点原则: select 中只要有一个case能return,则立刻执行。 当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。 如果没有一个case能return则可以执行”default”块。
package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(1)
int_chan := make(chan int, 1)
string_chan := make(chan string, 1)
int_chan <- 1
string_chan <- "hello"
select {
case value := <-int_chan:
fmt.Println("int:", value)
case value := <-string_chan:
fmt.Println("string:", value)
}
}
# 输出
都有可能
make默认值和append(这个很奇怪)
package main
import (
"fmt"
)
func main() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
}
# 输出
[0 0 0 0 0 1 2 3]
interface内部结构
-
解答
空接口是
var i interface{}
,这个不是
package main
import (
"fmt"
)
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func live() People {
var stu *Student
return stu
}
func main() {
if live() == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}
# 输出
BBBBBBB