func TestDefer(t *testing.T) {
defer func() { fmt.Println("1")}()
defer func() { fmt.Println("2")}()
defer func() { fmt.Println("3")}()
panic("error")
}
func f(n int) (r int) {
defer func() {
fmt.Println("anc")
r += n
recover()
}()
defer func() {
fmt.Println("temp")
}()
var f func()
defer f() // 这里 f() 没有被定义,发生错误,会继续调用之前的defer
f = func() {
r += 2
}
return n + 1 // 相当于 r = n + 1
}
func main() {
fmt.Println(f(3))
}
// 输出:
// temp
// anc
// 7
当协程碰到 panic
的时候,会遍历该协程的 defer 并执行,如果在 defer 执行的过程遇到 recover 则停止 panic ,返回 recover 继续向下执行,否则遍历完 defer,然后抛出 panic 信息
func TestForRange(t *testing.T) {
slice := []int{0, 1}
for i := 0; i < len(slice); i++ {
fmt.Println(&slice[i])
}
for _, v := range slice {
fmt.Println(&v)
}
}
for range 循环的时候会创建每个元素的副本,而不是元素的引用
new、make
都是用来分配内存,但是适用的类型不同
new(T)
会为 T 类型的新值分配已置零的内存空间,并返回地址,适用于值类型
make(T)
会返回初始化之后的 T 类型的值类型。只适合 slice,map,channel
func TestNew(t *testing.T) {
list := new([]int)
list = append(list, 1)
}
new([]int)
之后的list是一个 *[]int
类型的指针,不能对指针执行 append
操作
func TestAppendSlice(t *testing.T) {
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2)
fmt.Println(s1, s2)
}
append()
第二个参数不能直接使用 slice , 需要使用 ...
操作符
func TestStructEqual(t *testing.T) {
stu1 := struct {
age int
name string
}{age: 10, name: "小明"}
stu2 := struct {
age int
name string
}{age: 10, name: "小明"}
fmt.Println(stu1 == stu2)
stu3 := struct {
language map[string]string
}{language: map[string]string{"go": "verygood", "php": "bad"}}
stu4 := struct {
language map[string]string
}{language: map[string]string{"go": "verygood", "php": "bad"}}
fmt.Println(stu3 == stu4) // 这里会报错
}
结构体只能比较是否相等,不能比较大小
相同类型的结构体才能比较,结构体是否相同不仅仅与属性相关,还与属性的顺序有关
如果结构体内的成员可以比较,则该结构体才能进行比较
type int1 int
type int2 = int
var i int = 0
var i1 int1 = i
var i2 int2 = i
fmt.Println(i1, i2)
int1
是基于 int 类型创建的,int2
是创建了 int 类型的别名
func TestRun(t *testing.T) {
a := []int{7, 8, 9}
fmt.Printf("%+v\n", a) // [7 8 9]
ap(a)
fmt.Printf("%+v\n", a) // [7 8 9]
aap(a)
fmt.Printf("%+v\n", a) // [1 8 9]
}
func ap(a []int) {
a = append(a, 10)
fmt.Printf("%+v\n", a) // [7 8 9 10]
}
func aap(a []int) {
a[0] = 1
}
append
会导致低层数组被重新分配,所以原切片还是原切片
const (
x = iota
_
y
z = "zz"
k
p = iota
)
func TestIota(t *testing.T) {
fmt.Println(x, z, k, p) // 0 2 zz zz 5
}
iota
只能在常量表达式中使用- 每次
const
出现时,都会让iota
初始化为 0 - const中每新增一行常量声明将使 iota 计数一次
- 可以使用自定义枚举类型
- 可以使用下划线
_
跳过不想要的值 - 可以使用位掩码表示
x = 1 << iota
- 太多了,自己去网上了解吧
var x = nil // error
var y interface{} = nil
var z string = nil // error
var k error = nil
nil
值只能赋值给指针、chan、func、interface、map、slice
func init() {}
-
https://juejin.im/post/6844903864555012104
-
init()
函数是用于程序执行前进行包的初始化的函数,初始化变量等 -
一个包可以出现多个
init()
函数,一个源文件也可以出现多个 -
同一个包中多个
init()
函数的执行顺序没有明确定义,但是不同包的init()
函数是根据包导入的依赖关系决定的 -
init()
函数在代码中不能被显式调用、不能被引用,否则会出现变异错误 -
一个包被引用多次
A import B、C import B、A import C
B 被引用多次,但 B 只会初始化一次 -
引入包不能出现死循环
A import B、 B import A
if nil == func() int { return 1 } {
fmt.Println("ok")
}else {
fmt.Println("不相等") // 输出
}
这里是 函数跟 nil 比
i := func() int { return 1 }
switch i.(type) { // 错误
case int:
fmt.Println("int")
case string:
fmt.Println("string")
case interface{}:
fmt.Println("interface")
default:
fmt.Println("不知道类型")
}
只有接口类型才可以使用类型选择
fmt.Println(x, y, z, k, p)
var m map[person]int
p := person{"tom"}
fmt.Println(m[p]) // 0
这里输出为 0,打印map中不存在的值时,返回元素类型的零值
hello := func(num ...int) { num[0] = 18 }
i := []int{5, 6, 7}
hello(i...) // 可变参数传入切片的时候需要用到 ...
fmt.Println(i) // [18, 6, 7]
a := 1
b := 1.1
fmt.Println(a + b)
编译错误,不同类型不能相加
type coder interface {
code()
debug()
}
type Gopher struct {
language string
}
func (p Gopher) code() {
fmt.Printf("I am coding %s language\n", p.language)
}
func (p *Gopher) debug() {
fmt.Printf("I am debuging %s language\n", p.language)
}
func TestInterface3(t *testing.T) {
var c coder = &Gopher{"Go"} // 这里必须使用指针类型
c.code()
c.debug()
var d coder
fmt.Println(d == nil) // true
fmt.Printf("c: %T, %v\n", d, d) // nil, nil
var e *Gopher
fmt.Println(e == nil) // true
d = e
fmt.Println(d == nil) // false
fmt.Printf("c: %T, %v\n", d, d) // *main.Gopher, nil
}
接口值的零值是指动态类型
和动态值
都为 nil
。当仅且当这两部分的值都为 nil
的情况下,这个接口值就才会被认为 接口值 == nil
。
type MyError struct {}
func (i MyError) Error() string {
return "MyError"
}
func TestInterface4(t *testing.T) {
err := Process()
fmt.Println(err) // nil
fmt.Println(err == nil) // false
fmt.Printf("err %T, %v\n", err, err) // *main.MyError, nil
fmt.Printf("nil %T, %v\n", nil, nil) // nil, nil
}
func Process() error {
var err *MyError = nil
return err
}
a := [5]int{1,2, 3, 4, 5}
t := a[3:4:5]
fmt.Println(t) // 4
操作符 [i, j]
从索引 i
到索引 j
结束,第三个参数为新切片容量,但是不能超过原数组(切片)底层数组的大小
计算:[i:j:k]
- 长度:
j - i
- 容量:
k - i
cap 适用于 array, slice, channel
s := make(map[string]int)
delete(s, "name") // 删除不存在的键值对时不会报错
fmt.Println(s["name"]) // 0 (零值)
func hello (i int) {
fmt.Println(i) // 5
}
func TestRun3(t *testing.T) {
i := 5
defer hello(i)
i = i + 5
}
defer 的时候会保存一份副本
str := "string"
str[0] = 'x' // 报错
fmt.Println(str)
go 中字符串是只读的
var s1 []int // is nil
var s2 = []int{} // not nil
if s1 == nil {
fmt.Println("is nil")
} else {
fmt.Println("not nil")
}
nil
切片和空切片
s := [3]int{1, 2, 3}
b := s[:0]
c := s[:2]
d := s[1:2:cap(s)]
fmt.Println(s, len(s), cap(s)) // [1 2 3] 3 3
fmt.Println(b, len(b), cap(b)) // [] 0, 3
fmt.Println(c, len(c), cap(c)) // [1 2] 2 3
fmt.Println(d, len(d), cap(d)) // [2] 1 2
s[1:2]
为 左闭右开 区间 [1, 2)
。如果没有指定第三个参数,则新切片的cap
与原切片相等
type Person struct {
age int
}
func main() {
person := &Person{28}
defer fmt.Println(person.age) // 28
defer func(p *Person) {
fmt.Println(p.age) // 29
}(person)
defer func() {
fmt.Println(person.age) // 29
}()
person.age = 29
}
// 输出 29 29 28
person.age
相当于是把28当做 defer函数的参数,放到栈中
永远不要使用一个指针指向一个接口类型,因为它已经是一个指针。
var x string = nil
if x == nil {
x = "default"
}
fmt.Println(x)
golang的字符串类型不能赋值为 nil ,也不能跟 nil 比较
var a bool = true
func main() {
defer func(){
fmt.Println("1")
}()
if a == true {
fmt.Println("2")
return
}
defer func(){
fmt.Println("3")
}()
}
s1 := []int{1, 2, 3}
s2 := s1[1:]
s2[1] = 4
fmt.Println(s1, s2) // [1, 2, 4] [2, 4]
s2 = append(s2, 5, 6 ,7) // [1, 2, 4] [2, 4, 5, 6, 7]
fmt.Println(s1, s2)
当使用了 s[1:]
获得切片 s2, 此时 s2 会和 s1 共享一个底层数组
而 append
操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1
if a := 1; false {
}else if b := 2; false {
} else {
fmt.Println(a, b) // 1 2
}
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
的输出是无序的
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
}
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
当程序执行到 defer calc("1", a, calc("10", a, b))
的时候,会先执行 calc()
函数
defer 定义的函数是延迟函数,故 calc("1",1,3)
会被延迟执行;
下同
type People interface {
Show()
}
type Student struct {}
func (stu *Student) Show() {}
func main() {
var s *Student
if s == nil {
fmt.Println("s is nil") // 输出
}else{
fmt.Println("s is no tnil")
}
var p People = s
if p == nil {
fmt.Println("p is nil")
}else {
fmt.Println("p is not nil") // 输出
}
}
var p2 *int
func foo () (*int, error) {
var i int = 5
return &i, nil
}
func bar() {
fmt.Println(*p2)
}
func main() {
p2, err := foo()
if err != nil {
fmt.Println(err)
return
}
bar()
fmt.Println(*p2)
}
对于使用了 :=
的变量,如果新变量与同名已定义的变量不在同一个作用域中,那么Go会新定义这个变量。修改成如下就好了
var err error
p2, err = foo()
bar()
fmt.Println(p2)
a, b := 1.1 , 3
fmt.Printf("%T\n", a) // float64
a, d := 4, 5
fmt.Printf("%T\n", a) // float64
a, b := "a" , 3
fmt.Printf("%T\n", a) // float64
a, d := 4, 5 // 编译错误
fmt.Printf("%T\n", a)
var m = [...]int{1, 2, 3}
for i, v := range m {
go func() {
fmt.Println(i, v)
}()
}
time.Sleep(time.Second * 3)
// 输出 2 3 2 3 2 3
变量 i,v
在每次循环体中都会被重用,而不是重新声明
各个goroutine中输出的都是循环结束的 i , v
可以使用如下两种方法
for i, v := range m {
go func(i, v int) {
fmt.Println(i, v)
}(i, v)
}
for i, v := range m {
i, v := i, v
go func() {
fmt.Println(i, v)
}
}
当然这里的输出也不是顺序输出