Go基础之函数方法接口包
Posted AC_Jobim
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go基础之函数方法接口包相关的知识,希望对你有一定的参考价值。
Go基础(二)之函数、方法、接口、包
一、函数
函数的基本语法:
func 函数名 (形参列表) (返回值列表)
执行语句...
return 返回值列表
1、形参列表:表示函数的输入
2、函数中的语句:表示为了实现某一功能代码块
3、函数可以有返回值,也可以没有;
-
函数的调用
func main() /* 调用函数并返回最大值 */ ret = max(100, 200) fmt.Printf( "最大值是 : %d\\n", ret ) /* 函数返回两个数的最大值,一个返回值可以不加括号 */ func max(num1, num2 int) int /* 定义局部变量 */ var result int if (num1 > num2) result = num1 else result = num2 return result
-
函数首字母
大写
该函数可以被本包文件和其它包文件使用,类似public;首字母小写
,只能被本包文件使用,其它包文件不能使用,类似private -
Go函数不支持函数重载
-
在Go中
函数也是一种数据类型
,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用func getSum(n1 int, n2 int) int return n1 + n2 func main() a := getSum //将函数赋值给一个变量,此时变量a是函数类型 fmt.Printf("a的类型%T, getSum类型是%T\\n", a, getSum) // a的类型func(int, int) int, getSum类型是func(int, int) int res := a(10, 40) // 等价 res := getSum(10, 40) fmt.Println("res=", res) //res= 50
1.1 函数参数
参数的传递:
-
基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
func test01(n1 int) n1 = n1 + 10 fmt.Println("test01() n1= ", n1) //test01() n1= 30 func main() num := 20 test01(num) fmt.Println("main() num= ", num) //main() num= 20
-
**如果希望函数内的变量能修改函数外的变量(指的是默认以值传递的方式的数据类型),可以传入变量的地址&,函数内以指针的方式操作变量。**从效果上看类似引用。
// n1 就是 *int 类型 func test02(n1 *int) fmt.Printf("n1的值=%v\\n", n1) //n1的值=0xc04200e0b0 *n1 = *n1 + 10 fmt.Println("test02() n1= ", *n1) // test02() n1= 30 func main() num = 20 fmt.Printf("num的地址=%v\\n", &num) //num的地址=0xc04200e0b0 test02(&num) fmt.Println("main() num= ", num) // main() num= 30
-
值类型和引用类型
- 值类型:基本数据类型int系列, float 系列, boo1, string 、数组和结构体struct
- 引用类型:指针、slice切片、map、管道chan、interface 等都是引用类型
-
不管是指针、引用类型,还是其他类型参数,都是值拷贝传递。区别只是拷贝目标对象还是拷贝指针而已。
可变参数:
基本语法:
//支持o到多个参数
func sum(args... int) sum int
//支持1到多个参数
func sum(n1 int, args... int) sum int
-
可变参数本质就是一个切片,args[index]可以访间到各个值,只能接收一个到多个同类型参数,且必须放在列表尾部
func sum(n1 int, args... int) int sum := n1 //遍历args for i := 0; i < len(args); i++ sum += args[i] //args[0] 表示取出args切片的第一个元素值,其它依次类推 return sum func main() res4 := sum(10, 90, 10,100) fmt.Println("res4=", res4) //res4= 210
-
将切片作为变参时,需进行展开操作,如果是数组,先将其转换为切片
func test(a ...int) fmt.Println(a) //[10 20 30] func main() a := []int10, 20, 30 //先将数组转成slice test(a...) //将slice展开
函数作为另外一个函数的参数:
-
函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
func getSum(n1 int, n2 int) int return n1 + n2 //函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用 func myFun(funvar func(int, int) int, num1 int, num2 int ) int return funvar(num1, num2) func main() //看案例 res2 := myFun(getSum, 50, 60) //将getSum函数作为myFun函数的参数 fmt.Println("res2=", res2)
1.2 返回值
-
返回值列表也可以是多个
func swap(x, y string) (string, string) return y, x func main() a, b := swap("Google", "Runoob") fmt.Println(a, b)
-
使用
_
标识符,忽略返回值func cal(n1 int, n2 int) (int, int) sum := n1 + n2 sub := n1 - n2 return sum, sub func main() res1, _ := cal(10, 20) //忽略第二个返回值 fmt.Printf("res1=%d\\n", res1) //res1=30
命名返回值:支持对函数返回值命名(优缺点共存)
-
命名返回值和参数一样,可当作函数局部变量使用,最后由return隐式返回
//支持对函数返回值命名 func getSumAndSub(n1 int, n2 int) (sum int, sub int) sub = n1 - n2 sum = n1 + n2 return func main() a1, b1 := getSumAndSub(1, 2) fmt.Printf("a=%v b=%v\\n", a1, b1) //a=3 b=-1
1.3 匿名函数
Go支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。
-
在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。
func main() //在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次 res1 := func (n1 int, n2 int) int return n1 + n2 (10, 20) fmt.Println("res1=", res1) //res1= 30
-
将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
func main() //将匿名函数func (n1 int, n2 int) int赋给 a变量 //则a 的数据类型就是函数类型 ,此时,我们可以通过a完成调用 a := func (n1 int, n2 int) int return n1 - n2 res2 := a(10, 30) fmt.Println("res2=", res2) //res2= -20 res3 := a(90, 30) fmt.Println("res3=", res3) //res3= 60
闭包:
闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
//累加器
func AddUpper() func (int) int
var n int = 10
return func (x int) int
n = n + x
return n
func main()
//使用前面的代码
f := AddUpper()
fmt.Println(f(1))// 11
fmt.Println(f(2))// 13
fmt.Println(f(3))// 16
- 返回的是一个匿名函数,但是这个匿名函数引用到函数外的n ,因此这个匿名函数就和n形成一个整体,构成闭包。
1.4 延迟处理defer
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer (延时机制)。
- 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数下一个语句。
- 当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(注:遵守栈先入后出的机制)。
- 在defer将语句放入到栈时,也会将相关的值拷贝同时入栈
func sum(n1 int, n2 int) int
//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)
//当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行
defer fmt.Println("ok1 n1=", n1) //defer 3. ok1 n1 = 10
defer fmt.Println("ok2 n2=", n2) //defer 2. ok2 n2= 20
//增加一句话
n1++ // n1 = 11
n2++ // n2 = 21
res := n1 + n2 // res = 32
fmt.Println("ok3 res=", res) // 1. ok3 res= 32
return res
func main()
res := sum(10, 20)
fmt.Println("res=", res) // 4. res= 32
执行结果:
-
最佳实践:当函数执行完毕后,可以及时的释放函数创建的资源
func test() //关闭文件资源 file = openfile(文件名) defer file.close() //其他代码
1.5 错误处理
在Go语言中,错误被认为是一种可以预期的结果;而异常则是一种非预期的结果,发生异常可能表示程序中存在BUG或发生了其它不可控的问题。
错误:
Go中的错误类型:error
type error interface
Error() string
函数通常可在最后一个返回值中返回错误信息,自定义错误:errors.New(“错误说明”),会返回一个error类型的值,表示一个错误
func myF(f float64) (float64, error)
if f < 0
return 0, errors.New("Not legal input ")
// 实现
return 0.0, nil
func main()
_, e := myF(-1)
_, e2 := myF(2)
fmt.Println(e) // Not legal input
fmt.Println(e2) // <nil>
异常处理:
Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种处理
Go中引入的处理方式为: defer, panic, recover。Go中可以抛出一个panic 的异常,然后在defer中通过recover捕获这个异常,然后正常处理
-
defer
是Go提供的一种延迟执行机制,每次执行 defer,都会将对应的函数压入栈中。在函数返回或者 panic 异常结束时,Go 会依次从栈中取出延迟函数执行。 -
panic
用于主动抛出程序执行的异常,会终止其后将要执行的代码,并依次逆序执行 panic 所在函数可能存在的 defer 函数列表。panic 内置函数 ,接收一个interface类型的值(也就是任何值了)作为参数。可以接收error类型的变量,输出错误信息,并退出程序. -
recover
关键字主要用于捕获异常,将程序状态从严重的错误中恢复到正常状态。 必须在 defer 函数中才能生效。
defer+panic+recover的代码样例:
func my(i int) int
defer func()
if err := recover(); err != nil
fmt.Println("发生了异常", err)
()
if i != 5
return i
else
panic("panic")
return -1
func main()
for i := 0; i < 10; i++
a := my(i)
fmt.Println(a)
代码输出结果:
0
1
2
发生了异常 panic
0
4
使用defer+recover来处理错误:
func test()
//使用defer + recover 来捕获和处理异常
defer func()
err := recover() // recover()内置函数,可以捕获到异常
if err != nil // 说明捕获到错误
fmt.Println("err=", err)
//这里就可以将错误信息发送给管理员....
fmt.Println("发送邮件给admin@sohu.com~")
()
num1 := 10
num2 := 0
res := num1 / num2
fmt.Println("res=", res)
func main()
test()
执行结果:
1.6 内置函数
Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。它们有时可以针对不同的类型进行操作,例如:len、cap 和 append,或必须用于系统级的操作
append -- 用来追加元素到数组、slice中,返回修改后的数组、slice
close -- 主要用来关闭channel
delete -- 从map中删除key对应的value
panic -- 停止常规的goroutine (panic和recover:用来做错误处理)
recover -- 允许程序定义goroutine的panic动作
real -- 返回complex的实部 (complex、real imag:用于创建和操作复数)
imag -- 返回complex的虚部
make -- 用来分配内存,返回Type本身(只能应用于slice, map, channel)
new -- 用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
cap -- capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
copy -- 用于复制和连接slice,返回复制的数目
len -- 来求长度,比如string、array、slice、map、channel ,返回长度
print、println -- 底层打印函数,在部署环境中建议使用 fmt 包
new的使用:
func main()
num1 := 100
fmt.Printf("num1的类型%T , num1的值=%v , num1的地址%v\\n", num1, num1, &num1)
num2 := new(int) // *int
//num2的类型%T => *int
//num2的值 = 地址 0xc04204c098 (这个地址是系统分配)
//num2的地址%v = 地址 0xc04206a020 (这个地址是系统分配)
//num2指向的值 = 100
*num2 = 100
fmt.Printf("num2的类型%T , num2的值=%v , num2的地址%v\\n num2这个指针,指向的值=%v",
num2, num2, &num2, *num2)
执行结果:
1.7 常用的相关函数
字符串常用的系统函数:
-
统计字符串的长度,按字节 len(str)
str := "hello北" fmt.Println("str len=", len(str)) // 8
-
字符串遍历,同时处理有中文的问题 。r := []rune(str)
str2 := "hello北京" //字符串遍历,同时处理有中文的问题 r := []rune(str) r := []rune(str2) for i := 0; i < len(r); i++ fmt.Printf("字符=%c\\n", r[i])
-
字符串转整数:
n , err := strconv.Atoi(“12”)
//字符串转整数: n, err := strconv.Atoi("12") n, err := strconv.Atoi("123") if err != nil fmt.Println("转换错误", err) else fmt.Println("转成的结果是", n)
-
整数转字符串:
str = strconv.Itoa(12345)
str = strconv.Itoa(12345) fmt.Printf("str=%v, str=%T\\n", str, str) //str=12345, str=string
-
字符串转[]byte:
var bytes= []byte(“hello go”)
var bytes = []byte("hello go") fmt.Printf("bytes=%v\\n", bytes) //bytes=[104 101 108 108 111 32 103 111]
-
[]byte转字符串:
str = string([]byte97,98,99)
str = string([]byte97, 98, 99) fmt.Printf("str=%v\\n", str) //str=abc
-
10进制转2,8,16进制:
str = strconv.FormatInt(123,2)
str = strconv.FormatInt(123, 2) fmt.Printf("123对应的二进制是=%v\\n", str) //123对应的二进制是=1111011 str = strconv.FormatInt(123, 16) fmt.Printf("123对应的16进制是=%v\\n", str) //123对应的16进制是=7b
-
查找子串是否在指定的字符串中:
strings.Contains(“seafood”, “foo”) //true
b := strings.Contains("seafood", "mary") fmt.Printf("b=%v\\n", b) //b=false
-
统计一个字符串有几个指定的子串:
strings.Count(“ceheese”, “e”) //4
num := strings.Count("ceheese", "e") fmt.Printf("num=%v\\n", num) //num=4
-
不区分大小写的字符串比较(== 是区分字母大小写的):
fmt.PrintIn(strings.EqualFold(“abc”, “Abc”) // true
b = strings.EqualFold("abc", "Abc") fmt.Printf("b=%v\\n", b) //b=true fmt.Println("结果","abc" == "Abc") // 结果 false //区分字母大小写
-
返回子串在字符串第一次出现的index值,如果没有返回-1 :
strings.Index(“NLT_abc”,”abc”) //4
index := strings.Index("NLT_abcabcabc", "abc") // 4 fmt.Printf("index=%v\\n",index) //index=4
-
以上是关于Go基础之函数方法接口包的主要内容,如果未能解决你的问题,请参考以下文章