本文省去基本定义

文章目录

  • 1. 函数
    • 1.1 闭包
    • 1.2 延迟执行语句(defer)
    • 1.3 处理运行时发生的错误
    • 1.4 宕机(panic)——程序终止运行
    • 1.5 宕机恢复(recover)——防止程序崩溃
  • 2. 结构体
    • 2.1 结构体的实例化
    • 2.2 方法
    • 2.3 结构体的继承
    • 2.4 实现接口

1. 函数

1.1 闭包

闭包是一个函数和其相关环境组合的一个整体
函数+引用环境=闭包
能通过指针调用匿名函数或者函数是实现闭包的关键

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
    accumulate := Accumulate(1) //返回一个指向返回函数的指针
    fmt.Printf("%T", accumulate)
    fmt.Println(accumulate())//通过指针调用Accumulate内的匿名函数
    fmt.Println(accumulate())
}
func Accumulate(i int) func() int {
    return func() int {
        i++
        return i
    }
}

1.2 延迟执行语句(defer)

先被defer的语句最后被执行,最后被defer的语句,最先被执行。
用法: 使用延迟执行语句在函数退出时释放资源

1
2
3
4
5
func main() {
    defer fmt.Println("first")
    fmt.Println("second")
    defer fmt.Println("third")
}

1.3 处理运行时发生的错误

Go语言的设计者认为其他语言的异常机制已被过度使用,上层逻辑需要为函数发生的异常付出太多的资源。
error是Go系统声明的接口类型,代码如下:

1
2
3
type error interface{
    Error() string
}

所有符合Error() string格式的方法,都能实现错误接口。Error()方法返回错误的具体描述,使用者可以通过这个字符串知道发生了什么错误。

1
2
3
4
5
6
7
8
9
10
11
func main() {
    fmt.Println(div(0, 1))
   
    fmt.Println(div(0, 0))
}
func div(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("made,除了个0!")
    }
    return a / b, nil
}

1.4 宕机(panic)——程序终止运行

手动触发panic

1
2
3
func main() {
panic("触发异常。。。")
}

其次:

1
2
3
4
5
func main() {
    defer fmt.Println("beforepanic")
    panic("触发异常。。。")
    defer fmt.Println("afterpanic")
}


当panic()触发的宕机发生时,panic()后面的代码将不会被运行,但是在panic()函数前面已经运行过的defer语句依然会在宕机发生时发生作用。

1.5 宕机恢复(recover)——防止程序崩溃

这玩意真难用:
例1:

1
2
3
4
5
6
7
8
9
10
11
12
func main() {

    defer func() {
        //recover() //可以打印panic的错误信息
        //fmt.Println(recover())
        if err := recover(); err != nil { //产生了panic异常
            fmt.Println(err)
        }

    }() //别忘了(), 调用此匿名函数
    panic("0000")
}


例2:

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
package main

import "fmt"

func testa() {
    fmt.Println("aaaaaaaaaaaaaaaaa")
}

func testb(x int) {
    //设置recover,recover只能放在defer后面使用
    defer func() {
        //recover() //可以打印panic的错误信息
        //fmt.Println(recover())
        if err := recover(); err != nil { //产生了panic异常
            fmt.Println(err)
        }

    }() //别忘了(), 调用此匿名函数
    var a [10]int
    a[x] = 111 //当x为20时候,导致数组越界,产生一个panic,导致程序崩溃
}

func testc() {
    fmt.Println("cccccccccccccccccc")
}

func main() {
    testa()
    testb(20) //当值是1的时候,就不会越界,值是20的时候,就会越界报错。
    testc()
}

2. 结构体

2.1 结构体的实例化

  • 结构体本身是一种类型,可以像整型、字符串等类型一样,以var的方式声明结构体即可完成实例化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Student struct {
    name string
    age int
}

func main() {
    var student Student
    fmt.Printf("%T\n",student)
    fmt.Printf("%T\n",&student)
   
    student.name="lisi"
    student.age=11
    fmt.Println(student)
}
  • Go语言中,还可以使用new关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。
1
2
3
4
5
6
7
8
9
func main() {
    student := new(Student)
    fmt.Printf("%T\n",student)
    fmt.Printf("%T\n",&student)

    student.name="lisi"
    student.age=11
    fmt.Println(student)
}


在Go语言中,访问结构体指针的成员变量时可以继续使用“.”。这是因为Go语言为了方便开发者访问结构体指针的成员变量,使用了语法糖(Syntactic sugar)技术,将ins.Name形式转换为(*ins).Name。

  • 在Go语言中,对结构体进行“&”取地址操作时,视为对该类型进行一次new的实例化操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
    //student是一个指针
    student := &Student{
        name: "zhansan",
        age:  0,
    }
    fmt.Println(student)
    fmt.Printf("%T\n", student)
    fmt.Printf("%T\n", &student)
    fmt.Printf("%T\n", *student)

    student.name = "lisi"
    student.age = 11
    fmt.Println(student)
}

2.2 方法

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收器(Receiver)。

这里多了一个接收器的概念:
接收器——方法作用的目标

重点区别一下:(student *Student)与(student Student)的区别

  1. 理解指针类型的接收器
    指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的this或者self。
    由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。
1
2
3
4
5
6
7
8
9
10
func (this *Student) init() {
    this.name="init"
    this.age=21
}
func main() {
    var stu Student
    fmt.Println(stu)
    stu.init()
    fmt.Println(stu)
}
1
2
3
4
5
6
7
8
9
10
func (this Student) initNoPointer() {
    this.name="noPointerInit"
    this.age=21
}
func main() {
    var stu Student
    fmt.Println(stu)
    stu.initNoPointer()
    fmt.Println(stu)
}
  1. 理解非指针类型的接收器
    当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份。在非指针接收器的方法中可以获取接收器的成员值,但修改后无效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func (this Student) initNoPointerReturn() Student {
    this.name = "noPointerInit"
    this.age = 21 + this.age
    return this
}

func main() {
    var stu Student
    stu.age = 11
    fmt.Println(stu)
    initNoPointerReturn := stu.initNoPointerReturn()
    fmt.Println(initNoPointerReturn)
    fmt.Println(stu)
}


在计算机中,小对象由于值复制时的速度较快,所以适合使用非指针接收器。大对象因为复制性能较低,适合使用指针接收器,在接收器和参数间传递时不进行复制,只是传递指针。

2.3 结构体的继承

组合思想

2.4 实现接口

想要实现接口只需要方法格式与接口内的方法结构一致即可