基础笔记 | 青训营

64 阅读3分钟

defer


go语言中的defer语句会在其后面跟随的语句进行延迟处理。在defer归属的函数即将返回是,将延迟处理的语句按defer定义的逆序执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。stack,后进先出

特点

  1. 用于注册延迟调用
  2. 调用直到return前才被执行,因此可以用做资源清理
  3. 多个defer 语句,按先进后出的方式执行(stack)
  4. defer语句中的变量,在defer声明时就决定了

用途

  1. 关闭文件句柄
  2. 锁资源释放
  3. 数据库连接释放

Example

********package main
 
import "fmt"
 
func main() {
    fmt.Println("start")
    defer fmt.Println("step1")
    defer fmt.Println("step2")
    defer fmt.Println("step3")
    fmt.Println("end")
}
/*
start
end
step3
step2
step1
*/********

init函数


golang有一个特殊的函数init函数,先于main函数执行,实现包级别的一些初始化操作。

初始化顺序:变量初始化—>init()—>main()

特点

  • init函数先于main函数自动执行,不能被其他函数调用
  • init函数没有输入参数、返回值
  • 每个包可以有多个init函数
  • 包的每个源文件也可以有多个init函数
  • 同一个包的init执行顺序,golang没有明确定义
  • 不同包init函数按照包导入的依赖关系来决定执行顺序

Example

package main
 
import "fmt"
 
var i int = initVar()
 
func init() {
    fmt.Println("init2")
}
 
func init() {
    fmt.Println("init...")
}
 
func initVar() int {
    fmt.Println("initVar...")
    return 100
}
 
func main() {
    fmt.Println("main....")
}

指针


Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无需拷贝数据。

类型指针不能进行偏移和运算。

Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)

语法

一个指针变量指向了一个值的内存地址。(也就是我们声明了一个指针之后,可以像变量赋值一样,把一个值的内存地址放入到指针当中。)

类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:

var var_name *var-type var-type:为指针类型;var_name:为指针变量名;*:用于指定变量是作为一个指针。

Example

指针声明示例

var ip *int     /* /指向整型
var fp *float32 //* 指向浮点型
var ptr [MAX]*int; 表示数据里面的元素的类型是指针类型

指针使用示例

package main
 
import "fmt"
 
func main() {
    var a int = 20 /* 声明实际变量 */
    var ip *int    /* 声明指针变量 */
    ip = &a        /* 指针变量的存储地址 */
    fmt.Printf("a变量的地址是: %x\n", &a)
    /* 指针变量的存储地址 */
    fmt.Printf("ip变量储存的指针地址: %x\n", ip)
    /* 使用指针访问之 */
    fmt.Printf("*ip变量的值: %d\n", *ip)
}

指向数组的指针使用示例

package main
 
import "fmt"
 
const MAX int = 3
 
func main() {
    a := []int{1, 3, 5}
    var i int
    var ptr [MAX]*int
    fmt.Println(ptr) //这个打印出来的是[<nil> <nil> <nil>]
    for i := 0; i < MAX; i++ {
        ptr[i] = &a[i] /* 整数地址赋值给指针数组 */
    }
    for i = 0; i < MAX; i++ {
        fmt.Printf("a[%d] = %d\n", i, *ptr[i]) //*ptr[i]就是打印出相关指针的值了
    }
}

接口和类型的关系

检测是否实现了接口的全部方法,用于在编译源码时:

var _ Dialect = (*sqlite3)(nil)

一个类型实现多个接口

例如:有一个Player接口可以播放音乐,有一个Video接口可以播放视频,一个手机Mobile实现这两个接口,既可以播放音乐,又可以播放视频。

package main
 
import "fmt"
 
type Player interface {
    playMusic()
}
 
type Video interface {
    playVideo()
}
 
type Mobile struct {
}
 
func (m Mobile) playMusic() {
    fmt.Println("play music")
}
 
func (m Mobile) playVideo() {
    fmt.Println("play video")
}
 
func main() {
    m := Mobile{}
    m.playMusic()
    m.playVideo()
}

多个类型实现同一个接口

一个宠物接口Pet,猫类型Cat和狗类型Dog都可以实现该接口,都可以把猫和狗当宠物类型对待,这在其他语言中叫多态

package main
 
import "fmt"
 
type Pet interface {
    eat()
}
 
type Dog struct {
}
 
type Cat struct {
}
 
func (cat Cat) eat() {
    fmt.Println("cat eat...")
}
 
func (dog Dog) eat() {
    fmt.Println("dog eat...")
}
 
func main() {
    var p Pet
    p = Cat{}
    p.eat()
    p = Dog{}
    p.eat()
}