[TOC]
>[success]## **一、 函数介绍**
>[info]### 1.1 定义
函数:有输入、有输出,用来执行一个指定任务的代码块。
~~~
func functionname([parametername type]) [return type] {
//function body
} //其中参数列表和返回值列表都是可选的
~~~
解释:
~~~
func 函数名([参数名 类型])[返回值 类型] {
函数体
}
~~~
<br>
<br>
>[info]### **1.2 特点**
golang函数的特点:
1)不支持重载,即一个包不能有两个名字一样的函数
2)函数也是一种类型,一个函数可以赋值给变量
3)匿名函数
4)多返回值
>[info]### **1.3 return函数**
:-: ![](https://img.kancloud.cn/b5/7a/b57ad89ff4596829bdcfd2e07cb75d07_415x102.png)
<br>
>[]#### **多返回值**
返回多个值时要用括号包起来
~~~
package main
import "fmt"
func test(num1,num2 int) (int,int){
res1 := num1 + num2
res2 := num1 - num2
return res1,res2
}
func main(){
a,b := test(20,10)
fmt.Printf("a = %v\nb = %v",a,b)
}
~~~
**运行结果**
`a = 30`
`b = 10`
<br>
<br>
>[]#### **只接受一个返回值(_)来忽略**
~~~
package main
import "fmt"
func test(num1,num2 int) (int,int){
res1 := num1 + num2
res2 := num1 - num2
return res1,res2
}
func main(){
a,_ := test(20,10)
fmt.Printf("a = %v\nb = %v",a)
}
~~~
**运行结果**
a = 30
b = %!v(MISSING)
<br>
<br>
>[info]## **二、函数递归调用**
说名:函数体内又调用了本身,称之为递归调用
~~~
package main
func test(n int){
if n > 2{
n--
test(n)
}
}
func main(){
test(4)
}
~~~
~~~
package main
import "fmt"
func test(n int){
if n > 2{
n--
test(n)
}else{
fmt.Println("n = ",n)
}
}
func main(){
test(4)
}
~~~
<br>
>[success]### **2.1 递归调用的原则**
1)执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
2)函数的局部变量是独立的,不会相互影响
3)递归必须向退出递归的条件逼近,否则就是无限递归,死龟了:)
4)当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回
给谁,同时当函数执行完毕或者返回时,该函数本身也会被系统销毁
>[success]## **三、init函数**
>[info] ### **3.1 基本介绍**
每一个包都有一个init函数,该函数在main函数之前执行,
>[info] ### **3.2 案例演示**
~~~
package main
import "fmt"
func init(){
fmt.Println("init")
}
func main(){
fmt.Println("main")
}
~~~
**运行结果**
**init**
**main**
>[warning] ### **注意事项**
1)go中在当前文件中的执行顺序,全局变量 ---> init函数----> main函数
代码如下:
~~~
package main
import "fmt"
var sum = test()
func test() int{
fmt.Println("test()")
return 100
}
func init(){
fmt.Println("init")
}
func main(){
fmt.Println("main")
}
~~~
**运行结果**
**test()**
**init**
**main**
<br>
2)如果引入其他文件的全局变量和init函数,那么执行顺序如下:
:-: ![](https://img.kancloud.cn/bf/b0/bfb0e65edf3e26d5b34eb5f9abfd59b7_344x130.png)
<br>
<br>
>[success]## **四、匿名函数**
>[info]### **4.1 基本介绍**
Go支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。
>[info] ### **4.2 匿名函数使用方式**
1)匿名函数使用方式1
在定义匿名函数时就直接调用,[案例演示]
~~~
package main
import "fmt"
func main(){
res := func(n,m int) int{
return n + m
}(10,20) // 这里是直接调用
fmt.Println(res)
}
~~~
2)匿名函数使用方式2
将匿名函数赋给一一个变量(函数变量),再通过该变量来调用匿名函数
此时匿名函数`func(n3 int , n4 int) int 赋值给了res`
res的数据类型是一个函数类型,通过调用ret来完成调用
【案例演示】
~~~
package main
import "fmt"
func main(){
res := func(n,m int) int{
return n + m
}
sum := res(100,200)
fmt.Println("sum = ",sum)
sum1 := res(30,20)
fmt.Println("sum1 = ",sum1)
}
~~~
**运行结果**
`sum = 300`
`sum1 = 50`
<br>
<br>
全局匿名函数
如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序有效。[案例演示]
~~~
package main
import "fmt"
var (
res = func(n,m int) int{
return n + m
}
)
func main(){
ret := res(200,200)
fmt.Println(ret)
}
~~~
**运行结果**
400
<br>
<br>
>[success]## **五、闭包**
>[info]### **5.1闭包介绍:**
闭包就是一个函数与其相关的引用环境,组合成一个整体
>[info]### **5.2** **strings 和 strconv 包**
>[info]### **5.3案例演练:**
~~~
package main
import "fmt"
func Addper()func(int)int{
var sum = 10
return func(i int) int {
fmt.Printf("匿名函数接收到了%v\n",i)
sum = sum + i
return sum
}
}
func main(){
f := Addper()
fmt.Println(f(1))
fmt.Println(f(10))
fmt.Println(f(100))
}
~~~
**运行结果**
匿名函数接收到了1
11
匿名函数接收到了10
21
匿名函数接收到了100
121
<br>
返回的是一个匿名函数。但是这个匿名函数引用到函数外的i .因此这个匿名函数就和i形成一个整体,构成闭包。(第6行到10行就是闭包)
1)大家可以这样理解:闭包是类.函数是操作,i是字段。函数和它使用到n构成闭包。
2)当我们反复的调用f函数时,因为i是初始化一次,因此每调用一次就进行累计。
3)我们要搞清楚闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包。
<br>
<br>
>[info]### **5.4闭包的最佳实践**
请编写一个程序,具体要求如下
1)编写一个函数makefuffix(suffix string)可以接收一个文件后缀名(比如.jpg),并回一个闭包
2)调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返
3)回文件名.jpg,如果已经有.jpg后缀,则返回原文件名。要求使用闭包的方式完成
4)strings.HasSuffix(判断后缀名)
func [HasSuffix](https://github.com/golang/go/blob/master/src/strings/strings.go?name=release#376 "View Source")[](https://studygolang.com/static/pkgdoc/pkg/strings.htm#pkg-index)
~~~
package main
import (
"fmt"
"strings"
)
func MaKeSuffix(suffix string)func(string)string{
return func(name string) string {
if !strings.HasSuffix(name,suffix){
return name + suffix
}
return name
}
}
func main(){
res := MaKeSuffix(".jpg")
fmt.Println(res("witer"))
fmt.Println(res("f"))
fmt.Println(res("image"))
}
~~~
**运行结果**
witer.jpg
f.jpg
image.jpg
<br>
<br>
>[success]## **六、函数 defer**
>[info]### **6.1 基本介绍**
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer (延时机制)
>[info]### **6.2 案例演示**
~~~
package main
import "fmt"
func test(n,m int)int{
defer fmt.Println("n",n)
defer fmt.Println("m",m)
res := n + m
fmt.Println("res = ",res)
return res
}
func main(){
f := test(10,20)
fmt.Println(f)
}
~~~
**运行结果**
res = 30
m 20
n 10
30
<br>
<br>
>[info]### **defer细节**
1)当go执行到一个defer时, 不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中【我为了方便,暂时称该栈为defer栈】然后继续执行函数下一个语句
2)当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(注:遵守栈先入后出的机制),所以同学们看到前面案例输出的顺序。
3)在defer 将语句放入到栈时,也会将相关的值拷贝同时入栈。请看一段代码:
4)在golang编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的链接,或者是锁资源),可以执行 `defer file.Close() defer connect .CloseO
`
5)在defer后,可以继续使用创建资源
6)当函数完毕后,系统会依次从defer栈中,取出语句,关闭资源三造,
7)这种机制,非常简洁,程序员不用再为在什么时机关闭资源而烦心。
<br>
<br>
>[success]## **七、函数参数的传递方式**
:-: ![](https://img.kancloud.cn/05/7d/057d75729c7e428da2f0918f57a30ffa_500x302.png)
>[info]### **7.1 基本介绍**
我们在讲解函数注意事项和使用细节时,已经讲过值类型和引用类型了,这里
我们再系统总结一下,因为这是重难点,**值类型参数默认就是值传递**,而引用类型
**参数默认就是引用传递。**
两种传递方式
1)值传递
2)引用传递
其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,**地址拷贝效率高**,因为数据量小,而值拷贝诀定拷贝的数据大小,数据越大,效率越低。
>[info]### **7.2 值类型和引用类型**
1)值类型: 基本数据类型int系列, float系列, bool, string、数组和结构体struct
2)引用类型: 指针、slice切片、 map、管道chan、interface 等都是引用类型
    ●函数参数的传递方式
        1)值类型默认是值传递: 变量直接存储值,内存通常在栈中分配[案例:画出示意图]
:-: ![](https://img.kancloud.cn/bf/15/bf1528a7d599abf1cabf56a268debdc7_294x95.png)
        2)引用类型默认是引用传递:变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。[案例, 并画出示意图]
:-: ![](https://img.kancloud.cn/25/6a/256a998eda076ec5b8d54a77afcf876d_326x92.png)
>[success]## **八、变量的作用域**
>[info] ### 基本介绍
1)函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部。
2)函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效。
~~~
package main
import "fmt"
var(
age = 23
Name = "Boy"
)
func test(){
age := 18
Name := "jack"
fmt.Printf("age = %v\nName = %v",age,Name)
}
func main(){
fmt.Println(age)
fmt.Println(Name)
test()
}
~~~
**运行结果**
23
Boy
age = 18
Name = jack
1)如果变量是在一个代码块,比如for/if中, 那么这个变量的的作用域就在该代码块
**练习题**
如下代码,会报错
:-: ![](https://img.kancloud.cn/51/ea/51ea5598415666d5f99c9cdf6980b2d5_298x74.png)
**函数的练习**
1)在终端输入一个整数,打印出相应的金字塔
:-: ![](https://img.kancloud.cn/f9/96/f9962feaa745a59e1899a1a6f2ae3d9a_296x213.png)
>[success]## **九、string常用的函数**
说明:字符串在我们程序开发中,使用的是非常多的,常用的函数需要掌握[带看手册或者官方编程指南]:
>[info]**9,1 统计字符串的长度**
按字节len(str)-----(len是按照字节遍历,并非按照字符)
~~~
func main(){
c := "hello"
fmt.Println(len(c))
}
~~~
>[info]**9.2 字符串遍历**
同时处理有中文的问题r:= **[ ]rune(str)**,;例如遍历 a:=“hallo北京”
~~~
package main
import "fmt"
func main(){
c := "hello北京"
res := []rune(c)
for i := 1;i < len(res);i++{
fmt.Printf("hello北京的长度是:%c\n",res[i])
}
}
~~~
>[info]**9.3 字符串转整数**:
n, err := strconv.Atoi("12")
~~~
package main
import (
"fmt"
"strconv"
)
func main(){
sum := "12345"
re,err := strconv.Atoi(sum)
if err != nil{
fmt.Println("转换错误",err)
}else{
fmt.Println("转换成功",re)
}
}
~~~
>[info]**9.4 整数转字符串**
str = strconv.Itoa(12345)
~~~
package main
import (
"fmt"
"strconv"
)
func main(){
sum := 123456
str := strconv.Itoa(sum)
fmt.Printf("str value is【%v】\nstr type is【%T】",str,str)
}
~~~
**运行结果**
str value is【123456】
str type is【string】
>[info] **9.5 字符串转[]byte**
var bytes = [ ]byte("hello go")
~~~
package main
import (
"fmt"
)
func main(){
sum := []byte("hello")
fmt.Println(sum)
}
~~~
**运行结果**
`[104 101 108 108 111]`
>[info] **9.6 []byte 转字符串:**
**`str = string( [ ]byte{97, 98, 99})`**
~~~
package main
import (
"fmt"
)
func main(){
sum := string([]byte{97, 98, 99})
fmt.Printf("sum value %v\nsum type is %T",sum,sum)
}
~~~
**运行结果**
**sum value abc**
**sum type is string**
- Golang语言之旅
- 第一章:初始小节以及安装
- 一:Golang语言特性
- 二:Windows上安装Go语言开发包
- 三:在Mac OS上安装Go语言开发包
- 第二章:GO语言注意事项
- 一:Dos的常用指令
- 第三章:Go初识小菜
- 一:Go语言之变量与常量
- 二:Go内置值-引用类型
- 三:基本的数据类型
- 四:字符串(char)
- 五:布尔类型(bool)
- 六:字符串类型(string)
- 七:基本数据类型的默认值
- 八:基本数据类型的互相转换
- 九:基本数据类型和string类型的相互转换
- 十:Golang指针
- 十一:值类型和引用类型
- 十二:标识符和命名规范
- 十三:系统保留关键字and预定义标识符
- 十四:fmt常用方法解析
- 第四章:Go运算符
- 一:运算符的基本介绍
- 二:算术运算符
- 2.1:算数运算符细节
- 三:关系运算符
- 3.1:关系运算符细节
- 四:逻辑运算符
- 4.1:逻辑运算符细节及案例
- 五:Go赋值运算符
- 5.1:案例演示赋值运算符的基本使用
- 5.2:赋值运算符的特点
- 六:Go位运算符
- 七:其他运算符
- 八:运算符的优先级
- 九:控制台输入语句
- 十:进制
- 十一:位运算
- 第五章:流程控制大纲
- 一:if语句
- 二:switch语句
- 三:for循环
- 第六章:函数-包-错误处理
- 一:Go函数
- 二:Go包
- 三:匿名函数
- 四:闭包
- 五:函数defer
- 六:函数参数的传递方式
- 七:变量的作用域
- 八:时间和日期相关函数
- 九:new和recover异常
- 十:数组(Array)切片(Section)
- 十一:切片(slice)
- 十二:3 数组的排序和查找
- 第七章:Map
- 第一节:Map基础认识
- 第二节:Map初始化和make
- 第三节:Map增删改查
- 第四节:Map的切片
- 第五节:Map的注意事项
- 第八章:面向对象(上)
- 第一节:结构体(值类型)
- 第二节:方法
- 第三节:面向对象编程应用实例
- 第九章:面向对象(下)
- 第一节:面向对象之抽象
- 第二节:面向对象之继承
- 第三节:面向对象之多态
- 第四节:接口
- 第十章:文件操作
- 第一节:文件基本介绍
- 第二季:写文件实例操作
- 第三节:JSON
- 第十一章:单元测试
- 第一节:单元测试介绍
- 第二节:单元测试案例
- 第三节:单元测试总结
- 第四节:单元测试案例
- 第十二章:goroutine和channel
- 第一节:goroutine基本介绍
- 第二节:goroutine入门案例
- 第三节:goroutione调度模型
- 第四节:Golang设置运行的CPU数量
- 第十二章:channel
- 第一节:channel基本介绍
- 第二节:channel基本使用
- 第三节:channel案例演示
- 第四节:channel 使用的注意事项
- 第五节:channel练习题
- 第六节:channel的遍历和关闭
- 第七节:goroutione和channel结合
- 第八节:channel细节处理
- 第十二章:并发模式
- 第十三章:反射reflect
- 第一节:反射基本介绍
- 第二节:反射重要的函数和概念
- 第三节:反射快速入门案例
- 第四节:反射注意事项
- 第五节:反射练习题
- 第六节:反射最佳实践