avatar

目录
Golang 知识点梳理

newmake的区别

TODO

数组&切片

容量和长度

TODO

函数

匿名函数和闭包

  • 匿名函数

    • 匿名函数即没有函数名的函数
    • 因为没有函数名,所以不能像普通函数那样调用,所以匿名函数需要保存到某个变量中或者昨晚立即执行的函数。
    • 匿名函数多用于实现回调函数或闭包
    golang
    1
    2
    3
    4
    5
    6
    7
    func main() {

    func(x, y int){
    fmt.Println(x + y)
    }(1, 3)

    }
  • 闭包

    • 闭包是一个函数和其相关的引用环境组合而成的实体,即闭包=函数+引用环境
    • 闭包捕获的变量和常量是引用传递,不是值传递

defer

Go 语言中defer语句会将其后面跟随的语句进行延迟处理,在defer归属的函数即将返回时。将延迟的语句按defer定义的顺序进行逆序执行。

由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放的问题。比如:资源清理,文件关闭,解锁,记录时间等。

defer 的执行时机

在 Go 语言的函数中 return 语句在底层并不是原子操作,它分为给返回值赋值RET指令两步。

  • defer语句执行的时机就在返回赋值操作之后,RET指令执行之前。
    defer
  • defer注册要延迟执行的函数时该函数所有的参数都需要确定其值

每次defer语句执行的时候,会把函数压栈函数参数会被拷贝下来;当外层函数(非代码块,如一个for循环)退出时,defer函数按照定义的逆序执行;如果defer执行的函数为nil, 那么会在最终调用函数的产生panic.

defer语句并不会马上执行,而是会进入一个栈,函数return前,会按先进后出的顺序执行。也说是说最先被定义的defer语句最后执行。先进后出的原因是后面定义的函数可能会依赖前面的资源,自然要先执行;否则,如果前面先执行,那后面函数的依赖就没有了。

在defer函数定义时,对外部变量的引用是有两种方式的,分别是作为函数参数和作为闭包引用。作为函数参数,则在defer定义时就把值传递给defer,并被cache起来;作为闭包引用的话,则会在defer函数真正调用时根据整个上下文确定当前的值。

defer后面的语句在执行的时候,函数调用的参数会被保存起来,也就是复制了一份。真正执行的时候,实际上用到的是这个复制的变量,因此如果此变量是一个“值”,那么就和定义的时候是一致的。如果此变量是一个“引用”,那么就可能和定义的时候不一致。

结构体

  • 结构体指针

    当结构体开销比较大的时候尽量使用结构体指针,减少程序的内存开销

    golang
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    type person struct {
    name string
    age int
    }
    user := &person{
    name: "xiaoxia",
    age: 18,
    }
    user.age = 20
    fmt.Println(user)
    (*user).age = 21
    fmt.Println(user)
  • 结构体构造函数

    构造函数一般使用new开头(约定俗成)

    golang
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    type student struct {
    name string
    class string
    sno int
    }

    func newStudent(name string, class string, sno int) *student {
    return &student{
    name: name,
    class: class,
    sno: sno,
    }
    }

    student1 := newStudent("xiaoxia", "毕业班", 20200912029)
    fmt.Println(student1)

方法和接受者

golang
1
2
3
4

func (接受者变量 接收者类型) 方法名(方法参数) (返回参数) {
// 函数体
}

方法是作用于特定类型的函数

只能给当前包里的类型加方法

接受者表示的是调用该方法的具体类型,多用类型首字母小写表示

golang
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

type dog struct {
name string
}

func main() {

d := &dog{
name: "xia",
}

d.bark()
}

// bark is method for dog type , dog type is receiver, d is dog type shorthand
func (d dog) bark() {

fmt.Printf("%s: wangwang~", d.name)

}

值接收者&指针接收者

golang
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

type person struct {
name string
age int
}

func main() {

p := &person{
name: "xia",
age: 18,
}

fmt.Println(p.age) // 18

p.birthday()

fmt.Println(p.age) // 18

p.birthday2()

fmt.Println(p.age) // 19

}

// 值接受者,传拷贝值
func (p person) birthday() {
p.age++
}

// 指针接受者, 传内存地址
func (p *person) birthday2() {
p.age++
}

指针接收者使用场景

  • 当需要修改接收者中的值
  • 当接收者作为拷贝值代价比较大的对象(占内存)
  • 保证一致性,如果某个地方方法使用了指针接收者,那么其他地方也需要使用指针接收者

匿名字段

接口

在 Go 语言中,接口(interface)是一种抽象的类型,当看到一个接口类型时,唯一能知道的是通过他能做什么。

interface是一组method的集合,他做的事就像是定义了一个协议。

接口定义

golang
1
2
3
type 接口类型名 interface{
方法名(参数列表) 返回值列表
}
  • 接口名

    • 一般会在单词后面添加er,接口名最好突出该接口的类型含义
  • 方法名

    • 当方法名首字母是大写,且该接口类型名首字母也是大写时,这个方法可以被接口所在包之外的代码调用
golang
1
2
3
4
5

type hander interface{
write(content string) bool
cook()
}

接口是一个需要实现的方法的列表,只要实现了接口中的所有方法,就实现了这个接口。

空接口

空接口是指没有定义任何方法的接口,因此,任何类型都实现了空接口

空接口可以存储任意类型的变量。

  • 空接口作为函数的参数,可以接收任意类型的函数参数

    golang
    1
    2
    3
    func show(a interface{}) {
    fmt.Printf("type: %T ", a)
    }
  • 空接口作为map的值,可以保存任意值的字典

    golang
    1
    2
    3
    4
    student := make(map[string]interface{})
    student["name"] = "xia"
    student["age"] = 19
    student["is_rich"] = false

文件读写

时间

协程

ing

goroutine调度模型

GMP

GC

map 并发安全

context

进程

进程的本质

进程间通讯方式有多少种

协程暴增有哪些原因

实现斐波那契数列

使用动态规划和递归两种方式

数组实现一个最大堆

duck typing(鸭子类型)相关

文章作者: 小小小下
文章链接: /posts/golang_knowledge.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 来日方短
打赏
  • 请我吃苹果
    请我吃苹果

评论