闭包
闭包可以理解成定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。或者说
是函数和其引用环境的组合体。
闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境
。
注意:
-
全局变量特点:
常驻
内存污染
全局 -
局部变量特点:
不常驻
内存不污染
全局 -
闭包:可以让一个变量常驻内存 可以让一个变量不污染全局
-
闭包是指有权访问另一个函数
作用域
中的变量的函数创建闭包的常见的方式就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量
由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致
性能下降,建议在非常有必要的时候才使用闭包
func add() func(int) int{
var x int
return func(y int) int{
x+=y
return x
}
}
func main(){
var f = add()
fmt.Println(f(10))//10
fmt.Println(f(20))//30
fmt.Println(f(30))//60
fmt.Println("-------")
f1:=add()
fmt.Println(f1(50))//50
fmt.Println(f1(60))//110
}
递归
函数内部调用函数自身的函数称为递归函数
使用递归函数最重要的三点:
- 递归就是自己调用自己
- 必须先定义函数的退出条件,没有退出条件,递归将成为死循环
- go语言递归函数可能会产生一大堆的
goroutine
,也很可能会出现栈空间内存溢出的问题
go语言递归实例
阶乘
func f1(n int)int{
//返回条件
if n==1{
return 1
}else{
//自己调用自己
return n*f1(n-1)
}
}
func main(){
n:=5
r:=f1(n)
fmt.Println(r)
}
斐波那契数列
它的计算公式为f(n)=f(n-1)+f(n-2)且f(2)=f(1)=1
func f2(n int)int{
//返回条件
if n==1||n==2{
return 1
}else{
return f(n-1)+f(n-2)
}
}
func main(){
r:=f2(5)
fmt.Println(r)
}
defer panic recover
go语言中的defer
语句会将其后面跟随的语句进行延迟处理。在defer
归属的函数即将返回时,将延迟处理的语句
按defer
定义的逆序进行执行,也就是说,先被defer
的语句最后被执行,最后被defer
的语句,最先被执行。
defer特性
- 关键字
defer
用于注册延迟调用 - 这些调用直到
return
前才被执行。因此,可以用来做资源清理 - 多个
defer
语句,按先进后出的方式执行 - defer语句中的变量,在defer声明就决定了
defer用途
- 关闭文件句柄
- 锁资源释放
- 数据库连接释放
go语言defer语句实例
//查看执行顺序
func main(){
fmt.Println("start")//1
defer fmt.Println("step1")//5
defer fmt.Println("step2")//4
defer fmt.Println("step3")//3
fmt.Println("end")//2
}
panic和recover
go语言目前是没有异常机制,但是使用panic/recover
模式来处理错误。
panic可以在任何地方引发,但recover只有在defer调用的函数有效
func fn1(a int,b int){
defer func(){
err:=recover()
if err!=nil{
fmt.Println("error",err)
}
}()
fmt.Println(a/b)
panic("这里有异常")
}
func main(){
fn1(10,0)
fmt.Println("end...")
}
init
函数
go语言有一个特殊的函数init
函数,优先于main
函数执行,实现包级别的一些初始化操作
init
函数的主要特点
init
函数陷于main函数自动执行,不能被其他函数调用init
函数没有参数和返回值- 每个包包含有多个
init
函数 - 包的每个源文件也可以有多个
init
函数,这点比较特殊 - 同一个包的
init
执行顺序,go没有明确定义,编程时要注意程序不要依赖这个执行顺序 - 不同包的
init
函数按照包导入的依赖关系决定执行顺序
go初始化顺序
初始化顺序:变量初始化->init()
->main()
var i int = initVar()
func init() {
fmt.Println("init...")
}
func initVar() int {
fmt.Println("初始化变量...")
return 200
}
func main() {
fmt.Println("main...")
}
11.指针
go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针
而无需拷贝
数据。
类型指针不能进行偏移和运算。
go语言中的指针操作非常简单,只需要记住两个符号:&
(取地址)和*
(根据地址取值,解引用)
指针地址和指针类型
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。go语言中使用&
字符放在变量签名对变量进行取地址操作。
go语言中的值类型(int,float,bool,string,array,struct)
都有对应的指针类型,如:*int,*int64,*string
等等。
指针语法
一个指针变量指向了一个值的内存地址。(也就是说我们声明了一个指针后,可以像变量赋值一样,把一个值的内存地址放入到指针当中)。
类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:
var var_name *var-type
//指针变量名 用于指定变量是作为一个指针 指针类型
指针声明实例
var ip *int//指向整型
var fp *float32//指向浮点型
指针使用实例
var ip *int//指针变量
var sp *string
var i int = 24//正常变量
var s string = "小A"
ip = &i//把正常变量的地址存到指针变量里
sp = &s
fmt.Println(*ip)//解引用输出指针变量里存的正常变量的地址的值
fmt.Println(*sp)
指向数组的指针
定义语法
var ptr [MAX]*int//表示数组里面的元素类型是指针类型
注意:
- 不能直接取数组的地址给数组指针变量
- 不能直接解引用存储数组地址的数组指针变量
- 应该循环存储数组每一个元素的地址到数组指针变量
- 循环的从数组指针变量里解引用得到的数组地址
实例演示
const MAX int = 6
a := [MAX]int{1, 2, 3, 4, 5, 6}
var ap [MAX]*int
//ap=&a这样写是错误的
for i := 0; i < MAX; i++ {
ap[i] = &a[i]
}
fmt.Println(ap)
//fmt.Println(*ap)这样写是错误的
for _, v := range ap {
fmt.Println(*v)
}
12.结构体
go语言没有面向对象的概念了,但是可以使用结构体来实现,面向对象编程的一些特性,例如:继承
,组合
等特性。
go语言结构体的定义
结构体的定义和类型定义类似,只不过多了个struct
关键词,语法结构:
type struct_variable_type struct{
member definition
member definition
...
}
type
:结构体定义关键字
struct_variable_type
:结构体类型名称
struct
:结构体定义关键字
member definition
:成员定义
go语言结构体定义实例
下面我们定义一个人的结构体Person
type Person struct{
id int
name string
age int
email string
}
以上我们定义一个Person结构体,有四个成员,来描述一个Person的信息
type Person struct{
id,age int
name,email string
}
声明一个结构体变量
声明一个结构体变量和声明一个普通变量相同,例如:
var tom Person
fmt.Println(小A){0,0}
kite:=Person{}
fmt.Println(Alex)//{0,0}
访问结构体成员
可以使用点运算符(.
),来访问结构体成员,例如:
func main(){
type Person struct{
id,age int
name,email string
}
var sk Person
sk.id=1
sk.name="sk"
sk.age=20
sk.email="sk@elysium.org.cn"
fmt.Println(tom)//{1 20 sk sk@elysium.org.cn}
}
匿名结构体
如果结构体是临时
使用,可以不用起名字,直接使用,例如:
func main(){
var cat struct{
id int
name string
}
dog.id=1
dog.name="哈基米"
fmt.Println(cat)
}
今天就写到这里了,欢迎大家指正!