func Test1(t *testing.T) {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
打印后
打印中
打印前
panic: 触发异常
defer 的执行顺序是先进后出,发生panic后,会先执行defer
func Test2(t *testing.T) {
slice := []int{0, 1, 2, 3}
m := make(map[int]*int)
for key, val := range slice {
m[key] = &val
}
for k, v := range m {
fmt.Printf("key: %d, value: %d \n", k, *v)
}
}
key: 0, value: 3
key: 1, value: 3
key: 2, value: 3
key: 3, value: 3
for range 循环的时候会创建每个元素的副本,而不是元素的引用, 所以 m[key] = &val 取的都是变量 val 的地址,所以最后 map 中的所有元素的值都是变量 val 的地址, 因为最后 val 被赋值为3,所有输出都是3.
func Test3(t *testing.T) {
i := make([]int, 5)
i = append(i, 1, 2, 3)
fmt.Println(i)
j := make([]int, 0)
j = append(j, 1, 2, 3, 4)
fmt.Println(j)
}
[0 0 0 0 0 1 2 3]
[1 2 3 4]
make如果输入值,会默认给其初始化默认值
func funcMui(x,y int)(sum int,error){
return x+y,nil
}
第二个返回值没有命名,在函数有多个返回值时,只要有一个返回值有命名, 其他的也必须命名。如果有多个返回值必须加上括号(); 如果只有一个返回值且命名也必须加上括号()。 这里的第一个返回值有命名 sum,第二个没有命名,所以错误。
new只初始化并返回指针,而make不仅仅要做初始化,还需要设置一些数组的长度、容量等
func main() {
list := new([]int)
// 编译错误
// new([]int) 之后的 list 是一个未设置长度的 *[]int 类型的指针
// 不能对未设置长度的指针执行 append 操作。
list = append(list, 1)
fmt.Println(list)
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
// 编译错误,s2需要展开
s1 = append(s1, s2)
fmt.Println(s1)
}
func Test7(t *testing.T) {
sn1 := struct {
age int
name string
}{age: 11, name: "qq"}
sn2 := struct {
age int
name string
}{age: 11, name: "qq"}
// true
if sn1 == sn2 {
fmt.Println("sn1 == sn2")
}
sm1 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
sm2 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
// 编译错误,含有map、slice类型的struct不能进行比较
if sm1 == sm2 {
fmt.Println("sm1 == sm2")
}
}
A. p.name
B. (&p).name
C. (*p).name
D. p->name
AC
A. str := 'abc' + '123'
B. str := "abc" + "123"
C. str := '123' + "abc"
D. fmt.Sprintf("abc%d", 123)
BD
golang单引号''中的内容表示单个字符(rune),反引号``中的内容表示不可转义的字符串
func Test10(t *testing.T) {
const (
x = iota
_
y
z = "pi"
k
p = iota
q
)
fmt.Println(x, y, z, k, p, q)
}
0 2 pi pi 5 6
A. var x = nil
B. var x interface{} = nil
C. var x string = nil
D. var x error = nil
BD
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-
ABC
写 chan 时,<- 右端必须要有值
func hello(num ...int) {
num[0] = 18
}
func Test13(t *testing.T) {
i := []int{5, 6, 7}
hello(i...)
fmt.Println(i[0])
}
A.18
B.5
C.Compilation error
A
可变参数是指针传递
func main() {
a := 5
b := 8.1
fmt.Println(a + b)
}
A.13.1
B.13
C.compilation error
C
整形与浮点形不能相加
func Test15(t *testing.T) {
a := [5]int{1, 2, 3, 4, 5}
s := a[3:4:4]
fmt.Println(s[0])
}
A.3
B.4
C.compilation error
B
a[3:4][0] = 4 切片的长度是1,容量是2
a[4:4][0] 报越界错误
a[3:4:4][0] = 4 切片的长度是1,容量是1,最后一个4表示切片容量的最大坐标(不含)
func Test16(t *testing.T) {
a := [2]int{5, 6}
b := [3]int{5, 6}
if a == b {
fmt.Println("equal")
} else {
fmt.Println("not equal")
}
}
A. compilation error
B. equal
C. not equal
A 编译错误
对于数组而言,一个数组是由数组中的值和数组的长度两部分组成的,如果两个数组长度不同,那么两个数组是属于 不同类型的,是不能进行比较的
A. array
B. slice
C. map
D. channel
A B D array 返回数组的元素个数; slice 返回 slice 的最大容量; channel 返回 channel 的容量;
func Test18(t *testing.T) {
var i interface{}
if i == nil {
fmt.Println("nil")
return
}
fmt.Println("not nil")
}
A. nil
B. not nil
C. compilation error
A
当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil。
func Test19(t *testing.T) {
s := make(map[string]int)
delete(s, "h")
fmt.Println(s["h"])
}
A. runtime panic
B. 0
C. compilation error
B
删除 map 不存在的键值对时,不会报错,相当于没有任何作用; 获取不存在的键值对时,返回值类型对应的零值,所以返回 0
可以使用if v, ok := s["h"]; ok {}的方式判断键值对是否存在
func Test20(t *testing.T) {
i := -5
j := +5
fmt.Printf("%+d %+d", i, j)
}
A. -5 +5
B. +5 +5
C. 0 0
A
%+d 是带符号输出
A. var str string
B. str := ""
C. str = ""
D. var str = ""
A D
B 只支持局部变量声明;C 是赋值,str 必须在这之前已经声明;
func f(i int) {
fmt.Println(i)
}
func Test22(t *testing.T) {
i := 5
defer f(i)
i = i + 10
}
5
f() 函数的参数在执行 defer 语句的时候会保存一份副本, 在实际调用 f() 函数时用,所以是 5.
func Test23(t *testing.T) {
str := "hello"
str[0] = 'x'
fmt.Println(str)
}
A. hello
B. xello
C. compilation error
C 编译错误
Go 语言中的字符串是只读的
func inc(p *int) int {
*p++
return *p
}
func Test24(t *testing.T) {
p := 1
inc(&p)
fmt.Println(p)
}
A. 1
B. 2
C. 3
B
func add(args ...int) int {
sum := 0
for _, arg := range args {
sum += arg
}
return sum
}
A. add(1, 2)
B. add(1, 3, 7)
C. add([]int{1, 2})
D. add([]int{1, 3, 7}…)
A B D
func Test26(t *testing.T) {
var s1 []int
var s2 = []int{}
if ___ == nil {
fmt.Println("yes nil")
} else {
fmt.Println("no nil")
}
}
A. s1
B. s2
C. s1、s2 都可以
A
nil 切片和空切片。nil 切片和 nil 相等,一般用来表示一个不存在的切片; 空切片和 nil 不相等,表示一个空的集合
func Test27(t *testing.T) {
i := 65
fmt.Println(string(i))
}
A. A
B. 65
C. compilation error
A
UTF-8 编码中,十进制数字 65 对应的符号是 A
func Test28(t *testing.T) {
s := [3]int{1, 2, 3}
a := s[:0]
b := s[:2]
c := s[1:2:cap(s)]
fmt.Println(cap(a))
fmt.Println(cap(b))
fmt.Println(cap(c))
}
3 3 2
操作符 [i:j:k],k 主要是用来限制切片的容量, 但是不能大于数组的长度 ,截取得到的切片长度和容量计算方法是 j-i、k-i
func increaseA() int {
var i int
defer func() {
i++
}()
return i
}
func increaseB() (r int) {
defer func() {
r++
}()
return r
}
func Test29(t *testing.T) {
fmt.Println(increaseA())
fmt.Println(increaseB())
}
0 1
func f1() (r int) {
defer func() {
r++
}()
return 0
}
func f2() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f3() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
func Test30(t *testing.T) {
fmt.Println(f1())
fmt.Println(f2())
fmt.Println(f3())
}
1 5 1
type Person struct {
age int
}
func Test31(t *testing.T) {
person := &Person{28}
// 1
defer fmt.Println(person.age)
// 2
defer func(p *Person) {
fmt.Println(p.age)
}(person)
// 3
defer func() {
fmt.Println(person.age)
}()
person.age = 29
}
29 29 28
1.person.age 此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28;
2.defer 缓存的是结构体 Person{28} 的地址,最终 Person{28} 的 age 被重新赋值为 29,所以 defer 语句最后执行的时候,依靠缓存的地址取出的 age 便是 29,即输出 29;
3.闭包引用,输出 29;
A. var a []int
B. a := []int{}
A
A 声明的是 nil 切片;B 声明的是长度和容量都为 0 的空切片。 A的声明不会分配内存,优先选择
type S struct {
}
func m(x interface{}) {
}
func g(x *interface{}) {
}
func Test33(t *testing.T) {
s := S{}
p := &s
m(s) //A
g(s) //B
m(p) //C
g(p) //D
}
B D 会编译错误
函数参数为 interface{} 时可以接收任何类型的参数,包括用户自定义类型等, 即使是接收指针类型也用 interface{},而不是使用 *interface{}。 永远不要使用一个指针指向一个接口类型,因为它已经是一个指针。
func Test34(t *testing.T) {
s1 := []int{1, 2, 3}
s2 := s1[1:]
s2[1] = 4
fmt.Println(s1)
s2 = append(s2, 5, 6, 7)
fmt.Println(s1)
}
[1 2 4]
[1 2 4]
golang 中切片底层的数据结构是数组。当使用 s1[1:] 获得切片 s2,和 s1 共享同一个底层数组 这会导致 s2[1] = 4 语句影响 s1。 而 append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1
func Test35(t *testing.T) {
if a := 1; false {
} else if b := 2; false {
} else {
println(a, b)
}
}
1 2
func Test36(t *testing.T) {
a := 1
b := 2
defer calc("A", a, calc("10", a, b))
a = 0
defer calc("B", a, calc("20", a, b))
b = 1
}
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
10 1 2 3
20 0 2 2
B 0 2 2
A 1 3 4
func Test37(t *testing.T) {
m := map[int]string{0: "zero", 1: "one"}
for k, v := range m {
fmt.Println(k, v)
}
}
0 zero
1 one
或者
1 one
0 zero
map输出是无序的
type People interface {
Speak(string) string
}
type Student struct{}
func (stu *Student) Speak(think string) {
fmt.Println(think)
}
func main() {
var peo People = Student{}
think := "speak"
fmt.Println(peo.Speak(think))
}
不能编译通过,因为是 *Student 实现了Speak,并不是 值类型的Student, 但是如果是 Student 类型实现了Speak方法,那么用 值类型的
Student{}
或是指针类型的&Student{}
都可以访问到该方法
const (
a = iota
b = iota
)
const (
name = "name"
c = iota
d = iota
)
func Test39(t *testing.T) {
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
}
0 1 1 2
iota 在 const 关键字出现时将被重置为0,const中每新增一行常量声明将使 iota 计数一次。
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func Test40(t *testing.T) {
var s *Student
if s == nil {
fmt.Println("s is nil")
} else {
fmt.Println("s is not nil")
}
var p People = s
if p == nil {
fmt.Println("p is nil")
} else {
fmt.Println("p is not nil")
}
}
s is nil
p is not nil
当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。上面的代码,给变量 p 赋值之后, p 的动态值是 nil,但是动态类型却是 *Student,是一个 nil 指针,所以相等条件不成立。
type Direction int
const (
North Direction = iota
East
South
West
)
func (d Direction) String() string {
return [...]string{"North", "East", "South", "West"}[d]
}
func Test41(t *testing.T) {
fmt.Println(South)
}
South
type Square struct {
x, y int
}
var m = map[string]Square{
"foo": Square{2, 3},
}
func Test42(t *testing.T) {
m["foo"].x = 1
fmt.Println(m["foo"].x)
}
编译失败, m["foo"].x = 4 报错
对于类似 X = Y的赋值操作,必须知道 X 的地址,才能够将 Y 的值赋给 X, 但 go 中的 map 的 value 本身是不可寻址的
有两种解决方法:
第一种:
square := m["foo"]
square.x = 1
m["foo"] = square
第二种:
var m = map[string]*Math{
"foo": &Math{2, 3},
}
m["foo"].x = 1
var p *int
func foo() (*int, error) {
var i int = 5
return &i, nil
}
func bar() {
//use p
fmt.Println(*p)
}
func Test43(t *testing.T) {
p, err := foo()
if err != nil {
fmt.Println(err)
return
}
bar()
fmt.Println(*p)
}
bar 函数会发生panic,空指针异常
因为 err 前面没有声明,所以 p, err := foo() 中的 p 是重新声明的局部变量,而不是我们在前面声明的全局变量 p
func Test44(t *testing.T) {
v := []int{1, 2, 3}
for i := range v {
v = append(v, i)
fmt.Println(v)
}
}
[1 2 3 0]
[1 2 3 0 1]
[1 2 3 0 1 2]
func Test45(t *testing.T) {
var m = [...]int{1, 2, 3}
for i, v := range m {
go func() {
fmt.Println(i, v)
}()
}
time.Sleep(time.Second * 1)
}
2 3
2 3
2 3
for range 使用短变量声明(:=)的形式迭代变量, 需要注意的是,变量 i、v 在每次循环体中都会被重用,而不是重新声明。
有两种解决方式
第一种(推荐)
for i, v := range m {
go func(i,v int) {
fmt.Println(i, v)
}(i,v)
}
第二种
for i, v := range m {
i := i // 这里的 := 会重新声明变量,而不是重用
v := v
go func() {
fmt.Println(i, v)
}()
}
func f46(n int) (r int) {
defer func() {
r += n
recover()
}()
var f func()
defer f()
f = func() {
r += 2
}
return n + 1
}
func Test46(t *testing.T) {
fmt.Println(f46(3))
}
7
func Test47(t *testing.T) {
var a = [5]int{1, 2, 3, 4, 5}
var r [5]int
for i, v := range a {
if i == 0 {
a[1] = 12
a[2] = 13
}
r[i] = v
}
fmt.Println("r = ", r)
fmt.Println("a = ", a)
}
r = [1 2 3 4 5]
a = [1 12 13 4 5]
func change(s ...int) {
s = append(s, 3)
}
func Test48(t *testing.T) {
slice := make([]int, 5, 5)
slice[0] = 1
slice[1] = 2
// 1
change(slice...)
fmt.Println(slice)
// 2
change(slice[0:2]...)
fmt.Println(slice)
}
[1 2 0 0 0]
[1 2 3 0 0]
1.change函数内部append时超出了s的容量,生成了新的底层数组的切片, 未对change函数外的切片产生影响。
2.change函数收到的切片长度小于容量,append没有重新生成底层数组, 直接修改了底层数组对应位置的值,影响到了change函数外的切片。
func Test49(t *testing.T) {
var m = map[string]int{
"A": 21,
"B": 22,
"C": 23,
}
counter := 0
for k, v := range m {
if counter == 0 {
delete(m, "A")
}
counter++
fmt.Println(k, v)
}
fmt.Println("counter is ", counter)
}
counter is 2 或者 counter is 3
for range map 是无序的,若先遍历到A则counter是3,否则是2
A. 协程和线程都可以实现程序的并发执行;
B. 线程比协程更轻量级;
C. 协程不存在死锁问题;
D. 通过 channel 来进行协程间的通信;
A D
A. 循环语句既支持 for 关键字,也支持 while 和 do-while;
B. 关键字 for 的基本使用方法与 C/C++ 中没有任何差异;
C. for 循环支持 continue 和 break 来控制循环,但是它提供了一个更高级的 break,可以选择中断哪一个循环;
D. for 循环不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量;
C D
func Test52(t *testing.T) {
i := 1
s := []string{"A", "B", "C"}
i, s[i-1] = 2, "Z"
fmt.Printf("s: %v \n", s)
}
s: [Z B C]
A. 条件表达式必须为常量或者整数;
B. 单个case中,可以出现多个结果选项;
C. 需要用break来明确退出一个case;
D. 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case;
B D
func Test54(t *testing.T) {
var a Integer = 1
var b Integer = 2
var i interface{} = &a
sum := i.(*Integer).Add(b)
fmt.Println(sum)
}
A.
type Integer int
func (a Integer) Add(b Integer) Integer {
return a + b
}
B.
type Integer int
func (a Integer) Add(b *Integer) Integer {
return a + *b
}
C.
type Integer int
func (a *Integer) Add(b Integer) Integer {
return *a + b
}
D.
type Integer int
func (a *Integer) Add(b *Integer) Integer {
return *a + *b
}
A C
A. b = true
B. b = 1
C. b = bool(1)
D. b = (1 == 2)
B C
A.
i := 1
i++
B.
i := 1
j = i++
C.
i := 1
++i
D.
i := 1
i--
A D
go 里面没有 ++i 和 --i
type Fragment interface {
Exec(transInfo *TransInfo) error
}
type GetPodAction struct {
}
func (g GetPodAction) Exec(transInfo *TransInfo) error {
...
return nil
}
A. var fragment Fragment = new(GetPodAction)
B. var fragment Fragment = GetPodAction
C. var fragment Fragment = &GetPodAction{}
D. var fragment Fragment = GetPodAction{}
A C D
A. s := make([]int)
B. s := make([]int, 0)
C. s := make([]int, 5, 10)
D. s := []int{1, 2, 3, 4, 5}
B C D
func Test59(t *testing.T) {
runtime.GOMAXPROCS(1)
intChan := make(chan int, 1)
stringChan := make(chan string, 1)
intChan <- 1
stringChan <- "hello"
select {
case value := <-intChan:
fmt.Println(value)
case value := <-stringChan:
panic(value)
}
}
不一定,当两个chan同时有值时,select 会随机选择一个可用通道做收发操作
A. 给一个 nil channel 发送数据,造成永远阻塞
B. 从一个 nil channel 接收数据,造成永远阻塞
C. 给一个已经关闭的 channel 发送数据,引起 panic
D. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值
A B C D
const i = 100
var j = 123
func main() {
fmt.Println(&j, j)
fmt.Println(&i, i)
}
Go语言中,常量无法寻址, 是不能进行取指针操作的
func Test62(t *testing.T) {
x := []string{"a", "b", "c"}
for v := range x {
fmt.Print(v)
}
}
012
range 一个返回值时,这个值是下标,两个值时,第一个是下标,第二个是值,当 x 为 map时,第一个是key,第二个是value
A. 无缓冲的channel是默认的缓冲为1的channel;
B. 无缓冲的channel和有缓冲的channel都是同步的;
C. 无缓冲的channel和有缓冲的channel都是非同步的;
D. 无缓冲的channel是同步的,而有缓冲的channel是非同步的;
D
func Foo(x interface{}) {
if x == nil {
fmt.Println("empty interface")
return
}
fmt.Println("non-empty interface")
}
func Test64(t *testing.T) {
var x *int = nil
Foo(x)
}
non-empty interface
接口除了有静态类型,还有动态类型和动态值, 当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。 这里的 x 的动态类型是 *int,所以 x 不为 nil
A. select机制用来处理异步IO问题;
B. select机制最大的一条限制就是每个case语句里必须是一个IO操作;
C. golang在语言级别支持select关键字;
D. select关键字的用法与switch语句非常类似,后面要带判断条件;
A B C
func Stop(stop <-chan bool) {
close(stop)
}
只可以接收数据的 channel 不可以被关闭
func Test67(t *testing.T) {
var x = []int{2: 2, 3, 0: 1}
fmt.Println(x)
}
[1 0 2 3]
func incr(p *int) int {
*p++
return *p
}
func Test68(t *testing.T) {
v := 1
incr(&v)
fmt.Println(v)
}
2
func Test69(t *testing.T) {
var a = []int{1, 2, 3, 4, 5}
var r = make([]int, 0)
for i, v := range a {
if i == 0 {
a = append(a, 6, 7)
}
r = append(r, v)
}
fmt.Println(r)
}
[1 2 3 4 5]
a 在 for range 过程中增加了两个元素 len 由 5 增加到 7,但 for range 时会使用 a 的副本 a' 参与循环,副本的 len 依旧是 5, 因此 for range 只会循环 5 次,也就只获取 a 对应的底层数组的前 5 个元素。
func main() {
var s []int
s = append(s,1)
var m map[string]int
m["one"] = 1
}
切片可以开箱即用,但 map 需要用 make函数 进行初始化之后才能赋值
func main() {
var fn1 = func() {}
var fn2 = func() {}
if fn1 != fn2 {
println("fn1 not equal fn2")
}
}
编译错误,func 只能与 nil 做比较
type T struct {
n int
}
func main() {
m := make(map[int]T)
m[0].n = 1
fmt.Println(m[0].n)
}
编译错误, map[key]struct 中 struct 是不可寻址的,所以无法直接赋值。
func main() {
m := make(map[int]T)
t := T{1}
m[0] = t
fmt.Println(m[0].n)
}
type X struct {}
func (x *X) test() {
println(x)
}
func main() {
var a *X
a.test()
X{}.test()
}
X{} 是不可寻址的,不能直接调用方法
func main() {
var a *X
a.test()
// 为其定义一个变量,让其可寻址
var x = X{}
x.test()
}
A. 向已关闭的通道发送数据会引发 panic;
B. 从已关闭的缓冲通道接收数据,返回已缓冲数据或者零值;
C. 无论接收还是接收,nil 通道都会阻塞;
D. close() 可以用于只接收通道;
E. 单向通道可以转换为双向通道;
F. 不能在单向通道上做逆向操作(例如:只发送通道用于接收);
A B C F
func Test75(t *testing.T) {
s := make([]int, 3, 9)
fmt.Println(len(s))
s2 := s[4:8]
fmt.Println(len(s2))
}
3 4
func Test76(t *testing.T) {
var x interface{}
var y interface{} = []int{3, 5}
_ = x == x
_ = x == y
_ = y == y
}
_ = y == y 会发生panic, 因为两个比较值的动态类型为同一个不可比较类型
func Test77(t *testing.T) {
x := make([]int, 2, 10)
_ = x[6:10]
_ = x[6:]
_ = x[2:]
}
_ = x[6:] 这一行会发生panic, 截取符号 [i:j], 如果 j 省略,默认是原切片或者数组的长度,x 的长度是 2,小于起始下标 6 ,所以 panic
type data struct {
sync.Mutex
}
func (d data) test(s string) {
d.Lock()
defer d.Unlock()
for i := 0; i < 5; i++ {
fmt.Println(s, i)
time.Sleep(time.Second)
}
}
func Test78(t *testing.T) {
var wg sync.WaitGroup
wg.Add(2)
var d data
go func() {
defer wg.Done()
d.test("read")
}()
go func() {
defer wg.Done()
d.test("write")
}()
wg.Wait()
}
锁失效。将 Mutex 作为匿名字段时,相关的方法必须使用指针接收者,否则会导致锁机制失效。
// 指针接收者
func (d *data) test(s string) {
d.Lock()
defer d.Unlock()
for i:=0;i<5 ;i++ {
fmt.Println(s,i)
time.Sleep(time.Second)
}
}
func Test79(t *testing.T) {
var k = 1
var s = []int{1, 2}
k, s[k] = 0, 3
fmt.Println(s[0] + s[1])
}
4
func Test80(t *testing.T) {
nil := 123
fmt.Println(nil)
var _ map[string]int = nil
}
var _ map[string]int = nil 会编译错误, 当前作用域中, 预定义的 nil 被覆盖,此时 nil 是 int 类型值,不能赋值给 map 类型。
func Test81(t *testing.T) {
var x int8 = -128
var y = x / -1
fmt.Println(y)
}
-128, 因为溢出 int8为 -128 ~ 127 之间
func Test82(t *testing.T) {
defer func() {
fmt.Println(recover())
}()
defer func() {
defer fmt.Println(recover())
panic(1)
}()
defer recover()
panic(2)
}
2 1, recover() 必须在 defer函数体内使用才有效,所以 defer recover() 是无效的
A. str := 'abc' + '123'
B. str := "abc" + "123"
C. str := '123' + "abc"
D. fmt.Sprintf("abc%d", 123)
B D 双引号用来表示字符串 string,其实质是一个 byte 类型的数组,单引号表示 rune 类型。
func main() {
runtime.GOMAXPROCS(1)
go func() {
for i:=0;i<10 ;i++ {
fmt.Println(i)
}
}()
for {}
}
for{} 独占 CPU 资源导致其他 Goroutine 饿死
func main() {
runtime.GOMAXPROCS(1)
go func() {
for i:=0;i<10 ;i++ {
fmt.Println(i)
}
}()
select {}
}
func main() {
f, err := os.Open("file")
defer f.Close()
if err != nil {
return
}
b, err := ioutil.ReadAll(f)
println(string(b))
}
应该先判断 err, 再用defer 关闭文件句柄
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
fmt.Println("1")
wg.Done()
wg.Add(1)
}()
wg.Wait()
}
协程里面,使用 wg.Add(1) 但是没有 wg.Done(),导致 panic()。
func printI(num ...int) {
num[0] = 18
}
func Test87(t *testing.T) {
i := []int{5, 6, 7}
printI(i...)
fmt.Println(i[0])
}
18, 可变参数是指针传递
func alwaysFalse() bool {
return false
}
func Test88(t *testing.T) {
switch alwaysFalse(); {
case true:
println(true)
case false:
println(false)
}
}
true, Go代码断行规则
type ConfigOne struct {
Daemon string
}
func (c *ConfigOne) String() string {
return fmt.Sprintf("print: %v", c)
}
func main() {
c := &ConfigOne{}
c.String()
}
无限循环,栈溢出, 如果结构体类型定义了 String() 方法, 使用 Printf()、Print() 、 Println() 、 Sprintf() 等格式化输出时会自动使用 String() 方法。
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 5; i++ {
go func(wg sync.WaitGroup, i int) {
wg.Add(1)
fmt.Printf("i:%d\n", i)
wg.Done()
}(wg, i)
}
wg.Wait()
fmt.Println("exit")
}
在协程中使用了
wg.Add(1)
使用了 sync.WaitGroup 副本
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
fmt.Printf("i:%d\n", i)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("exit")
}
func main() {
a := [3]int{0, 1, 2}
s := a[1:2]
s[0] = 11
s = append(s, 12)
s = append(s, 13)
s[0] = 21
fmt.Println(a)
fmt.Println(s)
}
[0 11 12]
[21 12 13]
func Test92(t *testing.T) {
fmt.Println(strings.TrimRight("ABBA", "BA"))
}
输出空字符串, TrimRight() 会将第二个参数字符串里面所有的字符拿出来处理, 只要与其中任何一个字符相等,便会将其删除。想正确地截取字符串,可以参考 TrimSuffix() 函数。
func Test93(t *testing.T) {
var src, dst []int
src = []int{1, 2, 3}
copy(dst, src)
fmt.Println(dst)
}
输出 [], 如果想要将 src 完全拷贝至 dst,必须给 dst 分配足够的内存空间。
func Test93(t *testing.T) {
var src, dst []int
src = []int{1, 2, 3}
dst = make([]int, len(src))
copy(dst, src)
fmt.Println(dst)
}
或者直接使用append
func Test93(t *testing.T) {
var src, dst []int
src = []int{1, 2, 3}
dst = append(dst, src...)
fmt.Println(dst)
}
type User struct {
Name string
}
func (u *User) SetName(name string) {
u.Name = name
fmt.Println(u.Name)
}
type Employee User
func main() {
employee := new(Employee)
employee.SetName("Jack")
}
编译不通过, 当使用 type 声明一个新类型,它不会继承原有类型的方法集。
A. map 反序列化时 json.unmarshal() 的入参必须为 map 的地址;
B. 在函数调用中传递 map,则子函数中对 map 元素的增加不会导致父函数中 map 的修改;
C. 在函数调用中传递 map,则子函数中对 map 元素的修改不会导致父函数中 map 的修改;
D. 不能使用内置函数 delete() 删除 map 的元素
A
A. 当一个 goroutine 获得了 Mutex 后,其他 goroutine 就只能乖乖的等待,除非该 goroutine 释放这个 Mutex;
B. RWMutex 在读锁占用的情况下,会阻止写,但不阻止读;
C. RWMutex 在写锁占用情况下,会阻止任何其他 goroutine(无论读和写)进来,整个锁相当于由该 goroutine 独占;
D. Lock() 操作需要保证有 Unlock() 或 RUnlock() 调用与之对应;
A B C , 106