Go函数和方法
Go包
包的基本概念
go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的
包的三大作用
- 区分相同名字的函数、变量等标识符
- 当程序文件很多时,可以很好的管理项目
- 控制函数、变量等访问范围,即作用域
包的使用
打包基本语法
package util
导包基本语法
import "包的路径"
注意事项
在给一个文件打包时,该包对应一个文件夹
当一个文件要使用其他包函数或变量时,需要先引入对应的包
为了让其他包的文件,可以访问到本包的函数,则该函数名的首字母需要大写
1 2 3
func Call(...){ ... }
package指令在文件第一行,然后是import指令
1 2 3 4 5 6 7 8
package error import ( "errors" "fmt" "strconv" "testing" )
在访问其他包函数变量时,其语法是:包名.函数名
1 2 3
m := cm.CreateConcurrentMap(99) m.Set(cm.StrKey("key"), 10) t.Log(m.Get(cm.StrKey("key")))
如果包名较长,Go支持给包取别名,注意细节:取别名后,原来的包名就不能使用了
1 2 3 4
import ( cm "github.com/easierway/concurrent_map" "testing" )
在同一包下,不能有相同的函数名。
如果要编译成一个可执行程序文件,就需要将这个包声明为
main
,即package main
这个就是一个语法规范,如果是写一个库,包名可以自定义
Go函数
为完成某一功能的程序执行(语句)的集合,称为函数。
在Go中,函数分为:自定义函数、系统函数
基本语法
|
|
- 形参列表:表示函数的输入
- 函数中的语句:表示为了实现某一功能代码块
- 函数可以有返回值,也可以没有
示例
|
|
函数参数的传递方式
- 值传递:基本数据类型int系列、float系列、bool、string、数组和结构体struct
- 引用传递:指针、slice切片、map、管道chan、interface等
不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。
变量作用域
- 函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部
- 函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效
- 如果变量是在一个代码块,比如for/if中,那么这个变量的作用域就在改代码块
函数注意事项
函数的形参列表可以是多个,返回值列表也可以是多个
形参列表和返回值列表的数据类型可以是值类型和引用类型
函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其他包文件使用,类似public,首字母小写,只能被本包文件使用,其他包文件不能使用,类似private
函数中的变量是局部的,函数外不生效
基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值
如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用。
Go函数不支持重载
在Go中,函数也是一种数据类型,可以赋值给一个变阿玲,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
为了简化数据类型定义,Go支持自定义数据类型
- 基本语法:
type 自定义数据类型名 数据类型 // 相当于一个别名
- 基本语法:
支持函数返回值命名
1 2 3 4 5
func call(n1 int, n2 int) (sum int, sub int) { sum = n1 + n2 sub = n1 - n2 return }
使用
_
表示符,忽略返回值1 2 3 4
func TestFunc(t *testing.T) { sum, _ := call(1, 2) fmt.Println(sum) }
Go支持可变参数
- args是slice,通过args[index]可以访问到各个值
- 如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表最后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// 支持0到多个参数 func call2(args ...int) int { sum := 0 for _, arg := range args { sum += arg } return sum } // 支持1到多个参数 func call3(n1 int, args ...int) int { sum := n1 for _, arg := range args { sum += arg } return sum } func TestFunc(t *testing.T) { println(call2(1, 2, 3, 4)) println(call3(10, 1, 2, 3, 4)) }
init
函数
每一个源文件都可以包含一个init
函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init
会在main
函数前被调用
|
|
注意细节
- 如果一个文件同时包含全局变量定义,
init
函数和main
函数,则执行的流程是变量定义->init函数->main函数 - init函数最主要的作用,就是完成一些初始化的工作
匿名函数
Go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。
使用方式1
在定义匿名函数时就直接调用
|
|
使用方式2
将匿名函数赋给一个变量(函数变量),在通过该变量来调用匿名函数
|
|
全局匿名函数
如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序有效
闭包
闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
|
|
defer延时机制
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供了defer
(延时机制),类似于finally。
|
|
细节说明
- 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数下一个语句
- 当函数执行完毕后(或者抛出异常后),在从defer栈中,一次从栈顶取出语句执行(注:遵守栈先入后出的机制)
- 在defer将语句放入到栈时,也会将相关的值拷贝同时入栈