作者:秃秃爱健身,多平台博客专家,某大厂后端开发,个人IP起于源码分析文章 。
源码系列专栏:Spring MVC源码系列、Spring Boot源码系列、SpringCloud源码系列(含:Ribbon、Feign)、Nacos源码系列、RocketMQ源码系列、Spring Cloud Gateway使用到源码分析系列、分布式事务Seata使用到源码分析系列、JUC源码系列
基础系列专栏:30天熟悉GO语言(建设中)
码文不易,如果感觉博主的文章还不错,请点赞、收藏 ⭐️支持一下博主哇
联系方式:Saint9768,加我进技术交流群,一起学习进步、早日开启养老模式✈️
Go系列文章:
- GO开篇:手握Java走进Golang的世界
- 2 Go开发环境搭建、Hello World程序运行
- 3 Go编程规约和API包
- 4 Go的变量、常量、运算符
- 5 Go 基本数据类型
- 6 Go 复杂数据类型之指针
- 7 Go流程控制之分支结构if、switch
- 8 Go流程控制之循环结构for range、goto、break、continue
Go专栏传送链接:https://blog.csdn.net/saintmm/category_12326997.html
函数是一个基本的代码块,是用于完成某一功能的程序指令集;可以对特定的功能进行提取,形成一个代码片段,这个代码片段即函数。
我们可以通过函数划分不同的功能,逻辑上每个函数执行的是指定的任务。
提高代码的复用性,减少代码的冗余,提高代码的可维护性。
overload
),即:一个包不能有两个名字一样的函数nested
),即:一个包不能有两个名字一样的函数函数的定义格式如下:
func name( [parameter_list] ) [return_types] {
函数体
}
由
func关键字声明,包含一个函数名、参数列表、返回值列表和函数体。
示例1:两数相加
func addAndFormat(num1, num2 int) int {
sum := num1 + num2
return sum
}
示例2:两数相加,返回相加结果和结果说明
func addAndFormat(num1, num2 int, format string) (int, string) {
sum := num1 + num2
return sum, fmt.Sprintf(format, sum)
}
格式:
func name(arg1 T, arg2 T) (T, T) {
...
return r1, r2
}
特点:
格式:
func name(arg1 T, arg2 T) (r1 T, r2 T) {
...
return
}
特点:
return可以不用携带值的原因?
r1
和 r2
是两个初始化的变量;
r1
和 r2
中;函数可以被多次调用,在函数调用时传递的参数为实际参数(实参
) ,其有具体的值,用来给函数形式参数(形参
) 传递数据。
格式:
r1, r2 := name(param1, param2, param3)
如果接收多个值时,某个值不想使用,可以选择使用关键字_
替代,表示不使用这个返回值。
r1, _ := name(param1, param2, param3)
以函数声明中的示例2为例:
1> 返回值全部接收:
package main
import "fmt"
func main() {
format := "two num add, the result is : %d "
sum, str := addAndFormat(1, 2, format)
fmt.Println(sum)
fmt.Println(str)
}
// 两数相加,返回相加结果和结果说明**
func addAndFormat(num1, num2 int, format string) (int, string) {
sum := num1 + num2
return sum, fmt.Sprintf(format, sum)
}
2> 只接收一个返回值:
package main
import "fmt"
func main() {
format := "two num add, the result is : %d "
sum, _ := addAndFormat(1, 2, format)
fmt.Println(sum)
}
Go语言中,基本数据类型和数组默认都是值传递的,即:进行值拷贝;在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改时,将不会影响到原来的值。
示例:定义一个swap函数,用于交换两个int类型数据的值
package main
import "fmt"
func main() {
a := 3
b := 6
fmt.Printf("交换前 a 的值为 : %d\n", a)
fmt.Printf("交换前 b 的值为 : %d\n", b)
swap(a, b)
fmt.Printf("交换后 a 的值为 : %d\n", a)
fmt.Printf("交换后 b 的值为 : %d\n", b)
}
// 两值交换
func swap(x, y int) int {
var temp int
temp = x
x = y
y = temp
return temp
}
调用swap()函数交换a,b值,交换前后 a,b的值没有改变;
所以值传递不会改变传入函数实参的值,它只是复制一份数据用于函数体的执行。
如果希望在函数内的变量能修改函数外的变量,可以传入变量的地址 &
,函数内以指针的方式操作变量;从效果来看类似引用传递
。
示例:
package main
import "fmt"
func main() {
a := 3
b := 6
fmt.Printf("交换前 a 的值为 : %d\n", a)
fmt.Printf("交换前 b 的值为 : %d\n", b)
swap(&a, &b)
fmt.Printf("交换后 a 的值为 : %d\n", a)
fmt.Printf("交换后 b 的值为 : %d\n", b)
}
// 两值交换
func swap(x, y *int) int {
var temp int
temp = *x
// 将y地址上的值复制到x的地址上
*x = *y
*y = temp
return temp
}
PS:
不定参数传值指:函数的参数类型固定、参数数量不固定;Go语言中可变参数本质上是 slice
,并且该 slice 只能有一个,且必须是最后一个。
// 0个或多个参数
func myfunc1(args ...int) {
}
// 1个或多个参数
func add(a int, args…int) int {
}
// 2个或多个参数
func add(a int, b int, args…int) int {
}
args
是一个slice,我们可以通过 arg[index]
依次访问所有参数,通过 len(arg)
来判断传递参数的个数;函数中调用其他函数。
示例:test函数中调用test1函数
package main
import "fmt"
func main() {
test(3, 6)
}
func test1(a, b int) {
fmt.Println(a + b)
}
func test(a int, b int) {
fmt.Println("into function test")
test1(a, b)
}
解决措施:指定不定参arr
需要传递到函数testIndefiniteParam2
的数据个数;
1> 指定个数:
package main
import "fmt"
func main() {
testIndefiniteParam(1, 2, 3, 4)
}
// 不定参不能直接传递给另外一个不定参
func testIndefiniteParam(arr ...int) {
// 传递指定个数的数据,不报错
// 下标起始0,结束4(不包含4);仅仅为索引4之前的数据
testIndefiniteParam2(arr[0:4]...)
}
func testIndefiniteParam2(arr ...int) {
fmt.Println(arr)
}
testIndefiniteParam2(arr[0:]...)
递归指函数在运行的过程中自己调用自己。
构成递归的条件:
示例:斐波那契数列(fibonacci)
package main
import "fmt"
func fibonacci(i int) int {
if i == 0 {
return 0
}
if i == 1 {
return 1
}
return fibonacci(i-1) + fibonacci(i-2)
}
func main() {
var i int
for i = 0; i < 10; i++ {
fmt.Printf("%d\n", fibonacci(i))
}
}
匿名函数指不需要定义函数名的一种函数实现方式,匿名函数由一个不带函数名的函数声明和函数体组成;在Go语言中,函数可以像普通变量一样被传递 & 使用,并且支持随时在代码里定义匿名函数。
构造函数时,函数没有名称;想调用函数时,需要把匿名函数赋值给一个变量,或 在构造时直接调用。
func1 := func (arg1 T, arg2 T) T {
...
return r1
}
这里,func1
是一个函数类型的变量,可以直接通过func1(param1, param2)
调用函数。
示例:
package main
import "fmt"
func main() {
// 1> 匿名函数赋值给变量
func1 := func (a int, b int) int {
res := a + b
fmt.Printf("a + b = %d \n", res)
return res
}
func1(2,3)
}
func (arg1 T, arg2 T) T {
...
return r1
}(param1, param2)
构造函数时,函数声明的右大括号}
后紧跟要传递的参数(param1, param2);这样,构造完函数后会马上调用。
示例:
package main
import "fmt"
func main() {
//2> 匿名函数构造时直接调用
func (a int, b int) int {
res := a + b
fmt.Printf("a + b = %d \n", res)
return res
}(2, 3)
}
在Go语言中,函数也是一种数据类型,可以将函数赋值给一个变量,则该变量就是一个函数类型的变量了,通过该变量可以对函数调用。(匿名函数中有介绍)
所谓的声明函数类型,实际就是为已存在的函数起一个别名。尤其当我们引用二方包、三方包时,可以给别人的函数取个别名。
语法:
type 自定义数据类型名 func ( [parameter_list] ) [return_types]
示例:
package main
import "fmt"
// 定义函数类型 为已存在的数据类型起别名
type funcTwoParamOneReturn func(int, int) int
func main() {
// 自定义函数类型使用
var f funcTwoParamOneReturn
f = func2
fmt.Printf("%T", f)
}
// func(int, int)
func func1(a int, b int) {
fmt.Println(a + b)
}
// func(int, int) int
func func2(x int, y int) int {
fmt.Println(x + y)
return x + y
}
对已有函数设置别名时,函数的参数类型/个数、返回值类型/个数 需要一一对应,否则会报错;
和Java语言一样,在Go语言中,有一些函数无需导包即可使用,这样的内置函数有15个:
本文介绍了函数的一些基本概念,比如:函数是什么?为什么要使用函数?函数的特点?怎么声明一个函数?如何调用一个函数?嵌套函数是什么?匿名函数怎么声明使用?Go中内置函数有哪些?
和Java语言一样:
_
接收返回值用于丢弃,Java直接不用变量接方法的返回。和Java语言不同的是:
Java中的方法定义:
访问修饰符 返回值类型 方法名(参数1类型 参数1,参数2类型 参数2...) {
return 返回值;
}
Go中的函数定义:
func 函数名(变量1 变量类型,变量2 变量2类型...) (返回值1 类型1,返回值2 类型2...) {
return;
}