go 语言进阶之函数
函数
函数避免代码冗余,不利于代码的维护
为完成某一功能的程序指令(语句)的集合,称为函数
在go 中,函数分为:自定义函数、系统函数(查看go编程手册)
函数基本语法
func 函数名 (形参列表)(返回值类型列表){
执行语句
return 返回值列表
}
1) 形参列表: 表示函数的输入
2)函数中的语句: 表示为了实现某一功能代码块
3)函数可以有返回值,也可以没有
示例
package main
import (
"fmt"
)
//将计算功能放到一个函数内,需要使用,调用即可
func cal(s int,x int, c string) (int){
var d int
if c == "*" {
// fmt.Println(s,c,x,"=",s*x)
d =s*x
} else if c == "/"{
// fmt.Println(s,c,x,"=",s/x)
d = s/x
} else if c == "+"{
// fmt.Println(s,c,x,"=",s+x)
d = s+x
}else if c == "-"{
// fmt.Println(s,c,x,"=",s-x)
d = s - x
}else if c == "%" {
//fmt.Println(s,c,x,"=",s%x)
d = s % x
}
return d
}
func main() {
var s int
var x int
var c string
fmt.Println("输入第一个数")
fmt.Scanln(&s)
fmt.Println("输入第二个数")
fmt.Scanln(&x)
fmt.Println("输入操作符")
fmt.Scanln(&c)
re := cal(s,x,c)
fmt.Println(re)
//fmt.Println(as)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo01\mian.go
// 输入第一个数
// 6
// 输入第二个数
// 8
// 输入操作符
// *
// 48
包的介绍
1)实际开发中,我们往往需要在不同的文件中,去调用其他文件的定义的函数,如main.go中,使用utils.go文件中的函数,如何实现
2)现在有两个程序员,共同开发一个go项目,程序员xiaoming 希望定义函数Cal,程序员小强也希望定义函数叫cal。两个程序员吵起来
包的原理
包的本质实际上就是创建不同的文件夹,来存放程序文件‘’


包的基本概念
说明 go 的每一个文件都属于一个包,也就是说go是以包的形式来管理文件和项目目录结构的
包的作用
1)区分相同名字的函数、变量等标识
2)当程序文件很多时,可以很好的管理项目
3)控制函数、变量等访问范围,即作用域
包的相关说明
打包基本语法
package util
引入包的基本语法
import “包的路径”
示例

自定义 包
package utils // 包名
import (
"fmt"
)
//
func Cal(s int,x int, c string) (int){ // 函数首字母必须大写
var d int
if c == "*" {
// fmt.Println(s,c,x,"=",s*x)
d =s*x
} else if c == "/"{
// fmt.Println(s,c,x,"=",s/x)
d = s/x
} else if c == "+"{
// fmt.Println(s,c,x,"=",s+x)
d = s+x
}else if c == "-"{
// fmt.Println(s,c,x,"=",s-x)
d = s - x
}else if c == "%" {
//fmt.Println(s,c,x,"=",s%x)
d = s % x
}else{
fmt.Println("操作符错误")
}
return d
}
执行调用
package main
import (
"fmt"
"src01/go_code/src/chapter05/demo02/utils"// 引用包
)
func main() {
var s int
var x int
var c string
fmt.Println("输入第一个数")
fmt.Scanln(&s)
fmt.Println("输入第二个数")
fmt.Scanln(&x)
fmt.Println("输入操作符")
fmt.Scanln(&c)
re := utils.Cal(s,x,c) //调用包里的函数
fmt.Println(re)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run .\src\chapter05\demo02\main\mian.go
// 输入第一个数
// 23
// 输入第二个数
// 87
// 输入操作符
// *
// 2001
包事项和细节
1)在给文件打包时,该包对应一个文件夹,比如这里的utils 文件夹对应的包名就是utils,文件的包名通常与文件所在的文件夹名一致,一般为小写字母
2)当一个文件要使用其他包函数或变量时,需要先引入对应的包
(1)引入方法1: import “包名”
(2)引入方法2:import (
“包名”
)
3) package指令在文件第一行,然后是import
4)在import包时,路径从$GOPATH的src 下开始,不用带src ,编译器会自动从src下开始引入
5) 为了让其包的文件,可以访问到本包的函数,则函数名的首字母则需要大写,类似于其他语言的public,这样才能跨包访问。

6)在访问其他函数时,其语法是包名.函数名,比如这里main.go文件中

7)如果包名较长,go支持给包名取别名,注意细节:取别名后,原来的包名就不能使用了
示例
package main
import (
"fmt"
util "src01/go_code/src/chapter05/demo02/utils"// 引用包,取别名
)
func main() {
var s int
var x int
var c string
fmt.Println("输入第一个数")
fmt.Scanln(&s)
fmt.Println("输入第二个数")
fmt.Scanln(&x)
fmt.Println("输入操作符")
fmt.Scanln(&c)
re := util.Cal(s,x,c) //调用包里的函数,用别名访问
fmt.Println(re)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run .\src\chapter05\demo02\main\mian.go
// 输入第一个数
// 23
// 输入第二个数
// 87
// 输入操作符
// *
// 2001
在同一个包下,不能有相同函数名、变量(全局变量名),否则重复定义
如果要编译成一个可执行的文件,就需要将这个包声明为main,即package main 。这个就是语法规范,如果你写成一个库,包名可以自定义
编译指令,在项目目录下,编译路径不需要带src ,编译器会自动带
PS D:\golang\goproject\src\src01\go_code> go build -o src\chapter05\demo02\chenxi1.exe .\src\chapter05\demo02\main
编译时需要编译main包所在的文件夹
PS D:\golang\goproject\src\src01\go_code> go build -o src\chapter05\demo02\chenxi1.exe .\src\chapter05\demo02\main
项目的目录结构最好按照规范来组织
编译后生成一个默认名的可执行文件,在$GOPATH目录下,可以指定名字和目录,比放在bin目录下
PS D:\golang\goproject\src\src01\go_code> go build -o src\chapter05\demo02\chenxi1.exe .\src\chapter05\demo02\main
函数调用机制
调用方法:给方法必要的输入,方法返回结果
函数调用过程
为了更好理解函数调用过程,看示例。并画图,这个很重要
1)传入一个数+1 test
2)计算两个数并返回 getSum
return 语句
基本语法
go 函数支持返回多个值,这一点是其他编程语言没有的
func 函数名字 (形参列表) (返回值类型列表){
语句....
return 返回值
}
1)如果返回多个值,在接收时,希望忽略某个返回值,则使用_符合表示站位忽略
2)如果返回值只有一个,(返回值类型列表) 可以不写()
golang 中,函数调用的机制底层分析

(1)在调用一个函数时,会给一个函数分配一个新的空间,编译器会通过自身处理让这个新空间和其他的空间独立出来
(2)在每个函数对应的栈中,数据空间是独立的,不会共享
(3) 当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间
2)计算两个数,并返回
package main
import (
"fmt"
)
//将计算功能放到一个函数内,需要使用,调用即可
func test(s int) {
s++
fmt.Println("test",s)
}
func getSum (n1 int,n2 int) int{
n3:= n1+n2
fmt.Println("函数里打印",n3)// 函数调用执行
//当函数有return 语句时,就是将结果返回给调用者
return n3
}
func main() {
var s int = 9
test(s)
s1 := getSum(8,9)// 返回并赋值给,s1
fmt.Println("打印函数返回赋给变量的值",s1)
fmt.Println("打印函数返回值",getSum(3,5))//也可以直接调用,打印返回结果
fmt.Println("s=",s)//7
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo03\mian.go
// test 10
// 函数里打印 17
// 打印函数返回赋给变量的值 17
// 函数里打印 8
// 打印函数返回值 8
// s= 9
示例
package main
import (
"fmt"
)
func getsumandsub (n1 int,n2 int) (int,int){
n3 := n1+n2
n4 := n1-n2
return n3 , n4
}
func main() {
s2,s3 := getsumandsub(9,5)
fmt.Printf("和:%v, 差:%v\n",s2,s3)
fmt.Println(getsumandsub(12,67))//直接调用
s4,_ :=getsumandsub(98,65)//忽略第二个返回值
fmt.Printf("和:%v\n",s4)
}
// 执行结果
// 和:14, 差:4
// 79 -55
// 和:163
函数-递归调用
基本介绍一个函数在一个函数体内又调用了本身,称为递归调用
示例
package main
import (
"fmt"
)
func test (n int){
if n >2 {
n-- //1. 3
//2.2
test(n)
}
fmt.Println("n=",n)
}
func main() {
test(4)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo04\mian.go
// n= 2
// n= 2
// n= 3
流程分析

示例2
package main
import (
"fmt"
)
func test2 (n int){
if n >2 {
n-- //1. 3
//2.2
test2(n)
}else{
fmt.Println("n2=",n)// 打印2
}
}
func main() {
test2(4)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo04\mian.go
// n= 2
递归调用
1)执行一个函数时,就创建一个新的受保护的独立空间
2)函数的局部变量是独立的,不会互相影响
3)递归必须向退出递归的条件比较,否则就无限递归了
4)当一个函数执行完毕,或者遇到return ,就会返回,遵守谁调用,就将结果给谁,执行完毕或者返回时,该函数本身也会被销毁
示例
package main
import (
"fmt"
//"net"
)
func test3 (n int) int {
if (n==1 || n==2){
return 1
}else {
return test3(n-1) + test3(n-2)
}
}
func main() {
fmt.Println(test3(20))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo04\mian.go
// 6765
示例2
package main
import (
"fmt"
//"net"
)
func test3 (n int) int {
if (n==1 || n==2){
return 1
}else {
return test3(n-1) + test3(n-2)
}
}
func f(n int) int {
if n == 1{
return 3
}else {
return 2 *f(n-1)+1
}
}
func main() {
fmt.Println(test3(6))
fmt.Println(f(5))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo04\mian.go
// 8
// 63
示例
package main
import (
"fmt"
)
func c(n int) int{
if n>10 || n<1{
return 0
}else if n==10{
return 1
}else{
return (c(n+1)+1)*2
}
}
func main() {
fmt.Println(c(1))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo04\mian.go
// 1534
函数注意事项和细节讨论
1)函数的形参列表可以是多个,返回值也可以是多个
2)形参列表和返回值列表的数据类型可以是值类型和引用类型
3)函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其他包文件使用,类似于public ,首字母小写,只能被本包文件使用,其他包文件不能使用,类似private
4) 函数中的变量是局部变量。函数外不生效
func test() {
//n1 是test 函数的局部变量,只能在test 函数中使用
var n1 = 100
fmt.Println(n1)
}
5)基本数据类型和数组默认都是值传递,即进行值拷贝。在函数内修改,不会影响原来的值
package main
import (
"fmt"
//"net"
)
func test2(n1 int) {
//n1 是test 函数的局部变量,只能在test 函数中使用
n1+= 10
fmt.Println("test2(),n1=",n1)
}
func main() {
// fmt.Println(test3(6))
// fmt.Println(f(5))
//fmt.Println(c(1))
var n1 int = 16
test2(n1)
fmt.Println("man() n1=",n1)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo05\mian.go
// test2(),n1= 26
// man() n1= 16
6)如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内部可以通过指针方式操作变量(&内存取地址值;*根据地址值找指定的变量)
package main
import (
"fmt"
//"net"
)
func test2(n1 *int) { //传指针(值传递);n1就是*int
//n1 是test 函数的局部变量,只能在test 函数中使用
*n1+= 10
fmt.Println("test2(),n1=",*n1)
}
func main() {
// fmt.Println(test3(6))
// fmt.Println(f(5))
//fmt.Println(c(1))
var n1 int = 16
fmt.Println("man() 调用test之前 n1=",n1)
test2(&n1)//传指针(值传递)
fmt.Println("man()调用test后 n1=",n1)
}
//执行结果
// man() 调用test之前 n1= 16
// test2(),n1= 26
// man()调用test后 n1= 26
流程图

7)go 不支持函数重载
8) 在go 中函数也是一种数据类型,可以赋值给一个变量,则改变量就是一个函数类型的变量了,通过变量实现对函数的调用
package main
import (
"fmt"
)
func sum(n1 int,n2 int) int{
return n1+n2
}
func main() {
a := sum
fmt.Printf("a的类型%T,sum类型%T\n",a,sum)
res :=a(10,20)
fmt.Println(res)
fmt.Println(a(20,89))
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// a的类型func(int, int) int,sum类型func(int, int) int
// 30
// 109
9)函数既然是一种数据类型,因此go中,函数可以作为形参,并且可以调用
package main
import (
"fmt"
)
func sum(n1 int,n2 int) int{
return n1+n2
}
//函数既然是一种数据类型,因此在go 中,函数可以作为形参,并且调用
func mytest(a func(int,int)int,num1 int ,num2 int ) int{
return a(num1,num2)
}
func main() {
a := sum
fmt.Printf("a的类型%T,sum类型%T\n",a,sum)
res :=a(10,20)
fmt.Println(res)
fmt.Println(a(20,89))
res2 := mytest(sum,50,60)
fmt.Println("res2",res2)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// a的类型func(int, int) int,sum类型func(int, int) int
// 30
// 109
// res2 110
10) 为了简化数据类型定义,go支持自定义数据类型
基本语法 :type 自定义数据类型名 数据类型// 理解:相当于一个别名
示例:type myint int //这是,Myint 就等价int;go语言认为是两个不同的数据类型
package main
import (
"fmt"
)
func sum(n1 int,n2 int) int{
return n1+n2
}
//函数既然是一种数据类型,因此在go 中,函数可以作为形参,并且调用
func mytest(a func(int,int)int,num1 int ,num2 int ) int{
return a(num1,num2)
}
func main() {
a := sum
fmt.Printf("a的类型%T,sum类型%T\n",a,sum)
res :=a(10,20)
fmt.Println(res)
fmt.Println(a(20,89))
res2 := mytest(sum,50,60)
fmt.Println("res2",res2)
//给int 取别名.在go中myInt 和Int 虽然都是int类型,但是go认为myint 和int 是两个不同的类型
type myInt int
var Num1 myInt
Num1 = 40
fmt.Println("Num1 ",Num1)
var Num2 int
Num2 = int(Num1)//这里需要显示强制转换,因为go认为是不同类型
fmt.Println("Num2=",Num2)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// a的类型func(int, int) int,sum类型func(int, int) int
// 30
// 109
// res2 110
// Num1 40
// Num2= 40
示例:type mySunm func(int,int) int //这时mySum 就等价一个函数类型 func (int,int)int
示例
package main
import (
"fmt"
)
func sum(n1 int,n2 int) int{
return n1+n2
}
//函数既然是一种数据类型,因此在go 中,函数可以作为形参,并且调用
type myFun func (int,int) int //这是myFun 就是 func (int,int) int
func mytest(a myFun,num1 int ,num2 int ) int{
return a(num1,num2)
}
func main() {
a := sum
fmt.Printf("a的类型%T,sum类型%T\n",a,sum)
res :=a(10,20)
fmt.Println(res)
fmt.Println(a(20,89))
res2 := mytest(sum,50,60)
fmt.Println("res2",res2)
//给int 取别名.在go中myInt 和Int 虽然都是int类型,但是go认为myint 和int 是两个不同的类型
type myInt int
var Num1 myInt
Num1 = 40
fmt.Println("Num1 ",Num1)
var Num2 int
Num2 = int(Num1)//这里需要显示强制转换,因为go认为是不同类型
fmt.Println("Num2=",Num2)
res3 := mytest(sum,50,56)
fmt.Println("res3=",res3)
}
为什么要写成 func(int,int) int
因为 Go 是强类型语言。
如果要把函数作为参数传进去:
func calc(a int, b int, f func(int,int) int) int {
return f(a,b)
}
这里必须告诉编译器:
👉 f 是什么类型
👉 接收什么参数
👉 返回什么结果
否则编译器根本不知道怎么调用它。
看函数
func add(a int, b int) int {
return a + b
}
这里:
• add 是 函数名
• func(int,int) int 才是 函数类型
参数位置需要的是“类型”,不是“名字”
在 Go 里,函数定义规则是:
func 函数名(参数名 参数类型) func test(x int)
-
x是参数名 -
int是参数类型
类型必须是一个“类型”。
不能写成这样
func calc(a int, b int, add) int // ❌ 错误
为什么错?
因为:
add 不是类型 add 是一个具体函数 就像 var x int 不能写成 var x 10 // ❌ 10 不是类型 同理: add 不是类型
在 Go 里:
参数位置必须写“类型”
而不是写“变量名”或“函数名”。
函数名只是一个变量名
实际上 add 本质上是一个变量,类型是: func(int,int) int 举个类比 var x int var y x // ❌ x 是变量,不是类型 必须 var y int 同理: func calc(f add) // ❌ add 是变量 必须写: func calc(f func(int,int) int)
为什么传参可以是函数名
calc(10, 5, add) 这是因为: • 这里是“传值” • add 是一个值 • 它的类型是 func(int,int) int 所以是合法的。
理解

11)支持对函数返回值命名
示例
package main
import (
"fmt"
)
func sum(n1 int,n2 int) int{
return n1+n2
}
//函数既然是一种数据类型,因此在go 中,函数可以作为形参,并且调用
type myFun func (int,int) int //这是myFun 就是 func (int,int) int
func mytest(a myFun,num1 int ,num2 int ) int{
return a(num1,num2)
}
func stest (n1 int,n2 int) (sum int, sub int){// 指定返回
sum = n1+n2
sub = n1-n2
return
}
func main() {
a := sum
fmt.Printf("a的类型%T,sum类型%T\n",a,sum)
res :=a(10,20)
fmt.Println(res)
fmt.Println(a(20,89))
res2 := mytest(sum,50,60)
fmt.Println("res2",res2)
//给int 取别名.在go中myInt 和Int 虽然都是int类型,但是go认为myint 和int 是两个不同的类型
type myInt int
var Num1 myInt
Num1 = 40
fmt.Println("Num1 ",Num1)
var Num2 int
Num2 = int(Num1)//这里需要显示强制转换,因为go认为是不同类型
fmt.Println("Num2=",Num2)
res3 := mytest(sum,50,56)
fmt.Println("res3=",res3)
res4,res5 := stest(9,7)
fmt.Printf("两数和%v,两数差%v\n",res4,res5)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// a的类型func(int, int) int,sum类型func(int, int) int
// 30
// 109
// res2 110
// Num1 40
// Num2= 40
// res3= 106
// 两数和16,两数差2
12) 下划线标识符忽略返回值
13) Go支持可变参数
//支持0至多个参数
func sum (ages ... int) sum int{
}
//支持1到多个参数
func sum (n1 int ,ages...int)sum int{
}
说明:
(1) ages 是slice,通过ages [index] 可以访问到多个值
(2) 示例;可变参数一定放在形参列表最后
package main
import (
"fmt"
)
func sum(n1 int,n2 int) int{
return n1+n2
}
//函数既然是一种数据类型,因此在go 中,函数可以作为形参,并且调用
type myFun func (int,int) int //这是myFun 就是 func (int,int) int
func mytest(a myFun,num1 int ,num2 int ) int{
return a(num1,num2)
}
func stest (n1 int,n2 int) (sum int, sub int){// 指定返回
sum = n1+n2
sub = n1-n2
return
}
func sum1(n1 int,age... int) (sum int){
sum = n1
for i :=0; i<len(age);i++{
sum +=age[i]//表示age[0],表示取出age切片第一个值,其他一次类推
}
return
}
func main(){
a := sum
fmt.Printf("a的类型%T,sum类型%T\n",a,sum)
res :=a(10,20)
fmt.Println(res)
fmt.Println(a(20,89))
res2 := mytest(sum,50,60)
fmt.Println("res2",res2)
//给int 取别名.在go中myInt 和Int 虽然都是int类型,但是go认为myint 和int 是两个不同的类型
type myInt int
var Num1 myInt
Num1 = 40
fmt.Println("Num1 ",Num1)
var Num2 int
Num2 = int(Num1)//这里需要显示强制转换,因为go认为是不同类型
fmt.Println("Num2=",Num2)
res3 := mytest(sum,50,56)
fmt.Println("res3=",res3)
res4,res5 := stest(9,7)
fmt.Printf("两数和%v,两数差%v\n",res4,res5)
res6 := sum1(1,7,90,99)//可变参数
fmt.Println("res6=",res6)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// a的类型func(int, int) int,sum类型func(int, int) int
// 30
// 109
// res2 110
// Num1 40
// Num2= 40
// res3= 106
// 两数和16,两数差2
// res6= 197
函数 示例交换值
package main
import (
"fmt"
)
func sun2 (n1,n2 *int) {
*n1,*n2=*n2,*n1
}
func main(){
n6 := 12
n7 := 56
fmt.Printf("调用函数前n6=%v,n7=%v\n",n6,n7)
sun2(&n6,&n7)
fmt.Printf("调用函数后n6=%v,n7=%v\n",n6,n7)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// 调用函数前n6=12,n7=56
// 调用函数后n6=56,n7=12
init函数基本介绍
每个源文件都可以包含一个init函数,该函数会main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用
示例
init 函数通常完成初始化工作
package main
import (
"fmt"
)
func init(){
fmt.Println("init ()() .....")
}
func main(){
fmt.Println("main() ......")
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// init ()() .....
// main() ......
1)如果一个文件同时包含全部变量定义,init 函数和main函数,则执行的流程是全局变量定义->init函数->main函数
package main
import (
"fmt"
)
var age int = test()//全局变量
func test() int{
fmt.Println("12343")
return 90
}
func init(){
age = 90
//fmt.Println("age",age)
fmt.Println("init ()() .....")
}
func main(){
fmt.Println("main() ......")
fmt.Println("age",age)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// 12343
// init ()() .....
// main() ......
// age 90
2)init函数最主要的作用,就完成一些初始化的工作,比如下面案例
package utils
import (
"fmt"
)
var Age int
var Name string
func init(){
fmt.Println("utils 赋值变量····")
Age = 54
Name = "tom~"
}
执行main 函数
package main
import (
"fmt"
"src01/go_code/src/chapter05/demo08/utils" //导入函数
)
var age int = test()
func test() int{
fmt.Println("12343")
return 90
}
func init(){
age = 90
//fmt.Println("age",age)
fmt.Println("init ()() .....")
}
func main(){
fmt.Println("main() ......")
fmt.Println("test age",age)
fmt.Println("age00",utils.Age,"Name",utils.Name)
}
//执行结果
// utils 赋值变量····
// 12343
// init ()() .....
// main() ......
// test age 90
// age00 54 Name tom~
执行顺序

匿名函数
介绍Go支持匿名函数,如果某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次
匿名函数使用方式
在定义匿名函数时直接调用
package main
import (
"fmt"
)
func sun2 (n1,n2 *int) {
*n1,*n2=*n2,*n1
}
func main(){
res :=func (n1 int,n2 int ) int {
return n1+n2
}(2,3)
fmt.Println("res=",res)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo06\mian.go
// res= 5
匿名函数使用方式2
将匿名函数赋给一个变量(函数变量),在通过该变量来调用匿名函数
package main
import (
"fmt"
)
func sun2 (n1,n2 *int) {
*n1,*n2=*n2,*n1
}
func main(){
res :=func (n1 int,n2 int ) int {
return n1+n2
}(2,3)//直接定义并调用匿名函数
fmt.Println("res=",res)
sub := func (n1 int,n2 int) int {
return n1-n2
}//将匿名函数赋值给一个变量
fmt.Println("sub",sub(9,4)) //通过变量调用
}
// 执行结果
// res= 5
// sub 5
全局匿名函数
如果给匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名匿名函数,可以在整个程序有效(vscode 撤销操作wind/linuxCtrl + Z;macCmd + Z。向上向下复制行win/linux:Shift + Alt + ↓、Shift + Alt + ↑;mac :Shift + Option ;Shift + Option + ↑)
package main
import (
"fmt"
)
var (
//Fun1 就是全局匿名函数
Fun1 = func (n1 int,n2 int) int {
return n1*n2
}
)
func sun2 (n1,n2 *int) {
*n1,*n2=*n2,*n1
}
func main(){
res :=func (n1 int,n2 int ) int {
return n1+n2
}(2,3)//直接定义并调用匿名函数
fmt.Println("res=",res)
sub := func (n1 int,n2 int) int {
return n1-n2
}//将匿名函数赋值给一个变量
fmt.Println("sub",sub(9,4)) //通过变量调用
res1 := Fun1(9,6)// 全局匿名函数的使用
fmt.Println("res1=",res1)
}
// 执行结果
// res= 5
// sub 5
// res1= 54
闭包
基本介绍:闭包是一个函数和与其相关引用环境组合的一个整体;闭包是引用了自由变量的函数,被引用的自由变量和函数一同存在,及时已经离开了自由变量的环境也不会被释放或删除,在闭包中可以继续使用这个变量
示例
package main
import (
"fmt"
)
func Addupoer () func (int) int{
var n int = 10
return func(x int) int {
n = n+x
return n
}
}
func main(){
f :=Addupoer()//返回的是函数,f 是变量存储的是函数,他有初始值
fmt.Println(f(1))//11 初始值是10 f调他加1 也就是11,此时n=11(闭包特性,被引用的自由变量和函数一同存在,及时已经离开了自由变量的环境也不会被释放或删除,在闭包中可以继续使用这个变量)
fmt.Println(f(2))//13//根据闭包特性f函数只要不消n还是11 f传2 11+2=13;所以返回13
c := Addupoer()
fmt.Println("新闭包c(6)返回",c(6))//返回16
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo10\mian.go
// 11
// 13
// 新闭包c(6)返回 16
1)addUpper 是一个函数返回数据类型是func (int)int
2)闭包说明
var n int = 10
return func(x int) int {
n = n+x
return n
}
返回是一个函数,但是这个匿名引用到了函数外的n,因此这个函数就和n形成了一个闭包
反复调用f 函数是,因为n在f函数时初始化1次,所以不会反复初始化,它根据f函数调用次数变化,除非f函数销毁,否则函数里的变量一直存在
package main
import (
"fmt"
)
func Addupoer () func (int) int{
var n int = 10
return func(x int) int {
n = n+x
return n
}
}
func Acc(n int) func ()int{
return func() int {
n++
return n
}
}
func main(){
f :=Addupoer()//返回的是函数,f 是变量存储的是函数,他有初始值
fmt.Println(f(1))//11 初始值是10 f调他加1 也就是11,此时n=11(闭包特性,被引用的自由变量和函数一同存在,及时已经离开了自由变量的环境也不会被释放或删除,在闭包中可以继续使用这个变量)
fmt.Println(f(2))//13//根据闭包特性f函数只要不消n还是11 f传2 11+2=13;所以返回13
c := Addupoer()
fmt.Println("新闭包c(6)返回",c(6))//返回16
fmt.Println(f(6))
fmt.Printf("%p\n",f)
fmt.Printf("%p\n",c)
// d := Acc(1)
// c1 := Acc(10)
// fmt.Printf("%p\n",d)
// fmt.Printf("%p\n",c1)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo10\mian.go
// 11
// 13
// 新闭包c(6)返回 16
// 19
// 0x91b3c0
// 0x91b3a0
5)搞清楚闭包关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包
闭包的最佳实践(示例)
package main
import (
"fmt"
"strings"
)
func Addupoer () func (int) int{
var n int = 10
return func(x int) int {
n = n+x
return n
}
}
func Acc(n int) func ()int{
return func() int {
n++
return n
}
}
func makeSuffix() func (string) string{
var f string
return func(s string) string {
if strings.HasSuffix(s,".jpg") {
f = s
return f
}else{
f = s+".jpg"
return f
}
}
}
func ma(x string) func (string) string{
return func(s string) string {
if strings.HasSuffix(s,x){
return s
}else {
return s+x
}
}
}
func main(){
f :=Addupoer()//返回的是函数,f 是变量存储的是函数,他有初始值
fmt.Println(f(1))//11 初始值是10 f调他加1 也就是11,此时n=11(闭包特性,被引用的自由变量和函数一同存在,及时已经离开了自由变量的环境也不会被释放或删除,在闭包中可以继续使用这个变量)
fmt.Println(f(2))//13//根据闭包特性f函数只要不消n还是11 f传2 11+2=13;所以返回13
c := Addupoer()
fmt.Println("新闭包c(6)返回",c(6))//返回16
fmt.Println(f(6))
fmt.Printf("%p\n",f)
fmt.Printf("%p\n",c)
// d := Acc(1)
// c1 := Acc(10)
// fmt.Printf("%p\n",d)
// fmt.Printf("%p\n",c1)
t := makeSuffix()
c1 := ma(".jpg")
fmt.Println("文件",t("1.jpg"))
fmt.Println("文件2",t("2"))
fmt.Println("文件3",c1("4.jpg"))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo10\mian.go
// 11
// 13
// 新闭包c(6)返回 16
// 19
// 0xe8b420
// 0xe8b400
// 文件 1.jpg
// 文件2 2.jpg
// 文件3 4.jpg
函数中defer 介绍
在函数中,经常需要创建资源(比如:数据库连接、文件句柄、锁),为了函数执行完毕后,及时的释放资源,Go的设计者提供defer(延迟机制);在defer归属的函数即将返回时,将延迟处理的语句按defer的逆序进行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行
示例
package main
import (
"fmt"
)
func sun(n1 int,n2 int) int{
defer fmt.Println("ok1 n1=",n1)//先进后出
defer fmt.Println("ok2 n2=",n2)// 后进先出
res := n1+n2
fmt.Println("ok res=",res)
return res
}
func main(){
sun(2,4)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo10\mian.go
// ok res= 6
// ok2 n2= 4
// ok1 n1= 2
当执行到defer 时会将defer 后面语句压入到独立的栈中,暂时不执行;当函数执行完毕后在执行defer 语句,在从栈中按照先入后出的顺序执行;类似于生活里的桶,出入口只有一个,先进去的再下面,后进去的在上面,取出先取上面的,后取下面。
在 Go 里,defer 和 return 的执行顺序;return 先给返回值赋值 → 再执行 defer → 最后函数真正返回
package main
import (
"fmt"
)
func sun(n1 int,n2 int) int{
defer fmt.Println("ok1 n1=",n1)//先进后出
defer fmt.Println("ok2 n2=",n2)// 后进先出
res := n1+n2
fmt.Println("ok res=",res)
return res
}
func test() int {
i := 0
defer func() {
i++
}()
return i
}
func main(){
sun(2,4)
fmt.Println(test()) //结论先return,最后defer
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo10\mian.go
// ok res= 6
// ok2 n2= 4
// ok1 n1= 2
// 0
defer 细节
1)当go 执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,后续继续执行函数下语句
2)当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(遵守现金后出机制;跟生活里的桶一样)
3)在defer 将语句放入栈时,也会将相关的值同时拷贝入栈
package main
import (
"fmt"
)
func sun(n1 int,n2 int) int{
defer fmt.Println("ok1 n1=",n1)//先进后出 2
defer fmt.Println("ok2 n2=",n2)// 后进先出 8
n1 ++ //n1=3
n2 ++ //n2=5
res := n1+n2 //8
fmt.Println("ok res=",res) //8
return res
}
func main(){
sun(2,4)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo10\mian.go
// ok res= 8
// ok2 n2= 4
// ok1 n1= 2
最佳实现
defer 最主要的价值是在,当函数执行完毕后,可以及时释放函数创建的资源
1)在golang编程中通常做法,创建资源后,比如(打开了文件,获取了数据库链接,或是锁资源),可以执行defer file.Close() defer connect.Close()
2) 在defer 后,可以继续使用的创建资源
3)当函数执行完毕后,系统会依次按照现金后出原则取出语句,关闭资源
4)这种机制非常简洁,程序员不用再为在什么时候时机关闭资源
函数参数的传递方式
基本介绍: 函数注意事项使用细节,再针对值类型和引用类型总结一下,值类型参数默认是值传递,而引用类型参数默认就是引用传递
两种值传递方式
1)值传递: 直接传递的是值拷贝本身
2)引用传递 : 传递的数据内存地址值
其实不管是值传递,还是引用传递,传递给函数的都是变量副本,不同的是,值传递的是值拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝取决于数大小,数据越大,效率越低
1)值类型:基本数据类型int系列,float系列,bool,string,数组和结构体struct
2)引用类型:指针、slice切片、map、管道、interface 等都是引用类型
函数参数的传递方式
1)值类型默认是值传递:变量直接存储值,内存通常在栈中分配

2)引用类型默认是引用传递:变量存储的是一个地址,这个地址对应的空间才真正存储数据,内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由gc回收

3)内存的栈区和堆区示意图

变量作用域
1)函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部
//局部变量
func test () {
//name和age变量的作用域,仅限于test()函数内部
Name:="tom"
age:=10
fmt.Println(Name,age)
}
2)函数外部声明/定义的变量叫全局变量。作用域在整个包都有效,如果首字母是大写,则作用域在整个程序有效
package main
import (
"fmt"
)
func sun(n1 int,n2 int) int{
defer fmt.Println("ok1 n1=",n1)//先进后出
defer fmt.Println("ok2 n2=",n2)// 后进先出
n1 ++ //n1=3
n2 ++ //n2=5
res := n1+n2 //8
fmt.Println("ok res=",res)
return res
}
//函数外定义的变量;称为全局变量
var age int = 50 // 首字母小写整个包可以使用
var Name string = "cx" //首字母大写整个程序可以使用
//局部变量
func test () {
//name和age变量的作用域,仅限于test()函数内部
Name:="tom"
age:=10
fmt.Println(Name,age)
}
func main(){
fmt.Println("age=",age)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo13\mian.go
// age= 50
3)如果变量是在一块代码块,比如for /if 中,那么这个变量的作用域就该在该代码块
package main
import (
"fmt"
//"image"
)
func sun(n1 int,n2 int) int{
defer fmt.Println("ok1 n1=",n1)//先进后出
defer fmt.Println("ok2 n2=",n2)// 后进先出
n1 ++ //n1=3
n2 ++ //n2=5
res := n1+n2 //8
fmt.Println("ok res=",res)
return res
}
//函数外定义的变量;称为全局变量
var age int = 50 // 首字母小写整个包可以使用
var Name string = "cx" //首字母大写整个程序可以使用
//局部变量
func test () {
//name和age变量的作用域,仅限于test()函数内部
Name:="tom"
age:=10
fmt.Println(Name,age)
}
func main(){
fmt.Println("age=",age)
if true{
i := 10
fmt.Println(i)
}
fmt.Println("if 里i",i)//i 在if 代码块里定义,所以生效范围仅限于if代码块里; src\chapter05\demo13\mian.go:34:24: undefined: i
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo13\mian.go
// # command-line-arguments
// src\chapter05\demo13\mian.go:34:24: undefined: i
示例
package main
import (
"fmt"
)
var name = "tom"
func test01() {
fmt.Println("全局变量",name)
}
func test02(){
name := "jack"//编译器选择就近原则
fmt.Println("test02局部变量",name)
}
func main(){
fmt.Println("main函数",name)
test01()
test02()
test01()
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo14\mian.go
// main函数 tom
// 全局变量 tom
// test02局部变量 jack
// 全局变量 tom
示例 Name := "cx" 等价var Name string Name="cx" 因为初始化可以,赋值属于执行操作,必须在函数里执行;src\chapter05\demo14\mian.go:8:1: syntax error: non-declaration statement outside function
package main
import (
"fmt"
)
var name = "tom"
Name := "cx" //等价var Name string Name="cx" 因为初始化可以,赋值属于执行操作,必须在函数里执行;src\chapter05\demo14\mian.go:8:1: syntax error: non-declaration statement outside function body
func test01() {
fmt.Println("全局变量",name)
}
func test02(){
name := "jack"
fmt.Println("test02局部变量",name)
}
func main(){
fmt.Println("main函数",name)
test01()
test02()
test01()
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo14\mian.go
// # command-line-arguments
// src\chapter05\demo14\mian.go:8:1: syntax error: non-declaration statement outside function body
使用函数封装金字塔与九九乘法表
package main
import (
"fmt"
)
func s (t int) {
for i :=1;i <=t;i++{
for k :=1; k<=t-i;k++{
fmt.Printf(" ")
}
for g :=1; g<=2*i-1;g++{
fmt.Printf("*")
}
fmt.Println()
}
}
func x(r int) {
for i:=1;i<=r;i++{
//fmt.Println(i)
for j := 1; j <= i ;j++{
fmt.Printf("%d * %d = %d ",j,i,(i*j))
fmt.Printf(" ")
}
fmt.Println("")
}
}
func main(){
//fmt.Println("main函数",name)
s(8)
x(7)
}
//执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo15\mian.go
// *
// ***
// *****
// *******
// *********
// ***********
// *************
// ***************
// 1 * 1 = 1
// 1 * 2 = 2 2 * 2 = 4
// 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
// 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
// 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
// 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
// 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
二维数组转置
package main
import (
"fmt"
)
func test(t [][]int) {
s := make([][]int, 3)
for i := 0; i < 3; i++ {
s[i] = make([]int, 3)
}
for i := 0; i < 3; i++ {
for x := 0; x < 3; x++ {
s[i][x] = t[x][i]
}
}
fmt.Println(s)
}
func main(){
test([][]int{
{1,2,3},
{4,5,6},
{7,8,9},
})
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo15\mian.go
// [[1 4 7] [2 5 8] [3 6 9]]
字符串常用的系统函数
1)统计字符串长度,按字节len(str)
package main
import (
"fmt"
//"strings"
)
func main(){
var c string = "chenxi河"//编码是统一utf-8 (字母数字ASCII(占一个字节),一个汉字占用3个字节);按字节返回
q := len(c) //统计字符串的长度
fmt.Println(q)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// 9
2) 字符串遍历,同时处理有中文的问题r :=[]rune(str)
package main
import (
"fmt"
//"strings"
)
func main(){
var c string = "chenxi河"//编码是统一utf-8 (字母数字ASCII(占一个字节),一个汉字占用3个字节);按字节返回
q := len(c) //统计字符串的长度
fmt.Println(q)
s:="hello晨曦"
//遍历有中文的字符串需要先转成rune切片;之后遍历打印;否则如下乱码
// 字符串=h
// 字符串=e
// 字符串=l
// 字符串=l
// 字符串=o
// 字符串=æ
// 字符串=
// 字符串=¨
// 字符串=æ
// 字符串=
// 字符串=¦
r := []rune(s)
for i :=0;i<len(r);i++{
fmt.Printf("字符串=%c\n",r[i])
}
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// 9
// 字符串=h
// 字符串=e
// 字符串=l
// 字符串=l
// 字符串=o
// 字符串=晨
// 字符串=曦
3)字符串转整数:n,err :=strconv.Atoi("12")
package main
import (
"fmt"
"strconv"
//"strings"
)
func main(){
var c string = "chenxi河"//编码是统一utf-8 (字母数字ASCII(占一个字节),一个汉字占用3个字节);按字节返回
q := len(c) //统计字符串的长度
fmt.Println(q)
s:="hello晨曦"
r := []rune(s)
for i :=0;i<len(r);i++{
fmt.Printf("字符串=%c\n",r[i])
}
x := "89"
n,err := strconv.Atoi(x)
if err == nil {
fmt.Println("q=",n)
}else{
fmt.Println("err=",err)
}
d := "ty"
z,err := strconv.Atoi(d) //错误转换报错:strconv.Atoi: parsing "ty": invalid syntax
if err==nil {
fmt.Println("z=",z)
}else{
fmt.Println("err=",err)
}
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// 9
// 字符串=h
// 字符串=e
// 字符串=l
// 字符串=l
// 字符串=o
// 字符串=晨
// 字符串=曦
// q= 89
// err= strconv.Atoi: parsing "ty": invalid syntax
4)整数转字符串 str = strconv.ltoa(12345)
package main
import (
"fmt"
"strconv"
//"strings"
)
func main(){
str1 :=strconv.Itoa(789)//数字转字符串
fmt.Printf("str1的类型%T值是%v",str1,str1)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// str1的类型string值是789
5)字符串转[]byte: var byte= []byte("hello go")
package main
import (
"fmt"
//"strings"
)
func main(){
var botes = []byte("hello go")
fmt.Printf("botes=%v\n",botes)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// botes=[104 101 108 108 111 32 103 111]
6)[]byte 转字符串: str=string([]byte{97,98,99})
package main
import (
"fmt"
)
func main(){
str2 :=string([]byte{97,98,99})
fmt.Printf("str2=%v\n",str2)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// str2=abc
7)10进制转2,8,16进制:str=strconv.FormatInt(123,2)//2->,8.16
package main
import (
"fmt"
)
func main(){
str3:=strconv.FormatInt(123,2)//2->,8.16//转2进制
fmt.Printf("str3的2进制=%v\n",str3)
str3=strconv.FormatInt(123,8)//2->,8.16//转8进制
fmt.Printf("str3的8进制=%v\n",str3)
str3=strconv.FormatInt(123,16)//2->,8.16//转8进制
fmt.Printf("str3的16=%v\n",str3)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// str3的2进制=1111011
// str3的8进制=173
// str3的16=7b
8)查找子串是否在指定的字符串中strings.Contains("chenxi","nx") //true
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.Contains("chenxi","nx"))//如果字符串里存在指定的内容(子串)返回true,否则返回false
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// true
9)统计一个字符串里有几个指定的子串: strings.Count("ceheese","e")//4
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.Count("ceheese","e"))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// 4
10)不区分大小写的字符串判断是否相等(==是区分字母大小写的比较):fmt.Println(strings.EqualFold("abc","ABC"))//true
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.EqualFold("abc","ABC"))//true
}
// 执行结果
// true
11)返回子串的字符串第一次出现的index(索引值从0开始算),如果没有返回则是-1;strings.Index ("NLT_abc","abc")//4
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.Index("NLT_abc","abc"))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// 4
12)返回子串在字符串函数最后一次出现的index,如果没有返回-1;strings.LastIndex("go golang","go")
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.LastIndex("go golang","go"))
}
// 执行结果
// 3
13)将指定的子串替换成另一个子串,:strings.Replace("go go hello","go","go语言",n)n可以指定你希望替换几个,如果n=-1表示全部替换
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.Replace("go go hello","go","go语言",-1))
}
// 执行结果
// go语言 go语言 hello
14)按照指定的某个字符,为分隔标识,将一个字符串拆分成字符串数组:strings.Split("hello,wrold,ok",",")
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.Split("hello,wrold,ok",","))
}
// 执行结果
// [hello wrold ok]
15)将字符串的字母进行大小写转换:strings.ToLower("Go")//go strings.ToUpper("Go")//GO
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.ToLower("Go")) //全部小写
fmt.Println(strings.ToUpper("Go"))//全部大写
}
// 执行结果
// go
// GO
16)将 字符串左右两边空格去掉:strings.TrimSpace(" tn a lone gopher ntrn ")
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.TrimSpace(" tn a lone gopher ntrn "))//去掉左右两边空格
}
// 执行结果
// tn a lone gopher ntrn
17)将字符串左右两边指定的字符去掉:strings.Trim("! hello!"," !")//["hello"] //将左右两边!和" "去掉
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.Trim("! hello!"," !"))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// hello
18)将字符串左边指定的字符去掉:strings.TrimLeft("! hello!"," !")//["hello!"]//将左边!和""去掉
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.Trim("! hello!"," !"))
fmt.Println(strings.TrimLeft("! hello!"," !"))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// hello!
19)将字符串右边指定的字符去掉:strings.TrimRight("! hello!"," !")//["! hello"]//将右边!和""去掉
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.TrimRight("! hello!"," !"))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// ! hello
20)判断字符串是否以指定的字符串开头:strings.HasPrefix("ftp://192.168.10.1","ftp")//true
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.HasPrefix("ftp://192.168.10.1","ftp"))
}
// 执行结果
// true
21)判断字符串是否以指定的字符串结尾: strings.HasSuffix("NLT_abc.jpg","abc")//false
package main
import (
"fmt"
"strings"
)
func main(){
fmt.Println(strings.HasSuffix("NLT_abc.jpg","abc"))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run src\chapter05\demo18\mian.go
// false
go 时间函数
在写代码时候,经常会使用到日期相关的函数;time包提供了时间的显示和测量用的函数。日历的计算采用的是公历。
1)时间和日期相关函数,需要导入time包
2)time.Time 类型,用于表示时间
3)获取到当前时间的方法:now := time.Now()//now 的类型也是time.Time
package main
import (
"fmt"
"time"
)
func main(){
now := time.Now() //当前时间
fmt.Printf("type=%T val=%v\n",now,now)// 打印时间的类型,与当前时间
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run chapter05\demo18\mian.go
// type=time.Time val=2026-03-10 10:31:04.0434314 +0800 CST m=+0.002373701
4)获取到其他的日期信息
package main
import (
"fmt"
"time"
)
func main(){
now := time.Now() //当前时间
fmt.Println("当前年=",now.Year())
fmt.Println("当前月=",now.Month())
// 可以将返回的month,转成对应的数值
fmt.Println("当前月=",int(now.Month()))
fmt.Println("当前日=",now.Day())
fmt.Println("当前时=",now.Hour())
fmt.Println("当前分=",now.Minute())
fmt.Println("当前秒=",now.Second())
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run chapter05\demo18\mian.go
// 当前年= 2026
// 当前月= March
// 当前月= 3
// 当前日= 10
// 当前时= 10
// 当前分= 47
// 当前秒= 48
格式化2
package main
import (
"fmt"
"time"
)
func main(){
now := time.Now() //当前时间
fmt.Printf(now.Format("2006-01-02 15:04:05"))
fmt.Println()
fmt.Printf(now.Format("2006-01-02"))
fmt.Println()
fmt.Printf(now.Format("15:04:05"))
fmt.Println()
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run chapter05\demo18\mian.go
// 2026-03-10 11:57:54
// 2026-03-10
// 11:57:54
时间的常量(常量的作用:在程序中可用于获取指定时间单位的时间,比如想要100毫秒 100* time.Millisecond) type Duration
const(
Nanosecond Duration = 1 //纳秒
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Secomd = 1000 * Millisecond
Minute = 60 * Secomd
Hour = 60 * Minute
)
7) 休眠
package main
import (
"fmt"
"time"
)
func main(){
i :=0
for {
i++
//休眠
time.Sleep(time.Second) //1秒
fmt.Println(i)
if i == 3{
break
}
}
for i:=1;i<=10;i++{
time.Sleep(time.Millisecond * 100) //10 分之一毫秒
fmt.Println(i)
}
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run chapter05\demo18\mian.go
// 1
// 2
// 3
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10
8) 获取当前unix 时间戳,和unixnano时间戳
unix 时间戳:Unix将t表示为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位秒)。
unixnano 时间戳:UnixNano将t表示为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位纳秒)。如果纳秒为单位的unix时间超出了int64能表示的范围,结果是未定义的。注意这就意味着Time零值调用UnixNano方法的话,结果是未定义的

package main
import (
"fmt"
"time"
)
func main(){
now := time.Now() //当前时间
fmt.Printf("unxi 时间戳=%v unxinano=%v\n",now.Unix(),now.UnixNano())
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code> go run chapter05\demo18\mian.go
// unxi 时间戳=1773118145 unxinano=1773118145641368400
示例
package main
import (
"fmt"
"strconv"
"time"
)
func test03(){
str := ""
for i:=0;i < 100000;i++{
str += "hello"+strconv.Itoa(i)
}
}
func main(){
//在执行test03前;先获取当前的unix时间戳
start := time.Now().Unix()
test03()
stop := time.Now().Unix()
fmt.Printf("执行test时间%v秒\n",stop-start)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter05\demo20\mian.go
// 执行test时间9秒
内置函数
golang 设计者为了编程方便,提供了一些函数,这些函数可以直接使用,称为内置函数
1)len :用来求长度,例如string 、array、slice、map、channel
2)new: 用来分配内存主要用来分配值类型,比如int、flost32、struct....返回指针

示例
package main
import (
"fmt"
)
func main(){
num := 100
fmt.Printf("num的类型=%T,num的值=%v,num的地址值=%v\n",num,num,&num)
num1 :=new(int)
//num1的类型=*int,
// num1的值=0xc00000a0e0,
// num1的地址值=0xc000058030
fmt.Printf("num1的类型=%T,num1的值=%v,num1的地址值=%v\n",num1,num1,&num1)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter05\demo21\mian.go
// num的类型=int,num的值=100,num的地址值=0xc00000a0a8
内存分析

示例2
package main
import (
"fmt"
)
func main(){
num := 100
fmt.Printf("num的类型=%T,num的值=%v,num的地址值=%v\n",num,num,&num)
num1 :=new(int)
//num1的类型=*int,
// num1的值=0xc00000a0e0,
// num1的地址值=0xc000058030
fmt.Printf("num1的类型=%T,num1的值=%v,num1的地址值=%v,num1指针指向的的值是%v\n",num1,num1,&num1,*num1)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter05\demo21\mian.go
// num1的类型=*int,num1的值=0xc00000a0e0,num1的地址值=0xc000058030,num1指针指向的的值是0
示例2
package main
import (
"fmt"
)
func main(){
num := 100
fmt.Printf("num的类型=%T,num的值=%v,num的地址值=%v\n",num,num,&num)
num1 :=new(int)
fmt.Printf("num1的类型=%T,num1的值=%v,num1的地址值=%v,num1指针指向的的值是%v\n",num1,num1,&num1,*num1)
*num1 = 40//改变值
fmt.Printf("num1的类型=%T,num1的值=%v,num1的地址值=%v,num1指针指向的的值是%v\n",num1,num1,&num1,*num1)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter05\demo21\mian.go
// num1的类型=*int,num1的值=0xc00000a0e0,num1的地址值=0xc000058030,num1指针指向的的值是0
// num1的类型=*int,num1的值=0xc00000a0e0,num1的地址值=0xc000058030,num1指针指向的的值是40
3)make: 用来分配内存。主要用来分配引用数据类型,例如chan、map、slice。
错误处理
package main
import (
"fmt"
)
func test(){
num1 := 10
num2 := 0
res:= num1/num2
fmt.Println("res=",res)
}
func main(){
test()
fmt.Println("下面的代码和逻辑..........")
}
// 执行结果
// panic: runtime error: integer divide by zero
// goroutine 1 [running]:
// main.test()
// D:/golang/goproject/src/src01/go_code/src/chapter05/demo22/mian.go:9 +0x9
// main.main()
// D:/golang/goproject/src/src01/go_code/src/chapter05/demo22/mian.go:14 +0x13
// exit status 2
(1)在默认情况,当发生错误后(panic),程序就会退出(崩溃)
(2)如果希望,当发生错误时,可以捕获错误,并进行处理,保障代码继续执行。还可以捕获到错误给管理员一个提示(邮件或者短信)
(3)引出错误处理机制
基本说明
1)go 语言追求简洁优雅,所以,go语言不支持传统的try...catch...finally这种处理
2)go中引入的处理方式为:defer,panic,recover
3)这几个异常的使用场景可以简单描述,go中可以抛出一个panic的异常,然后在后面defer中通过recover捕获这个异常,然后正常处理
使用defer + recover形式处理异常
package main
import (
"fmt"
)
func test(){
//使用defer + recover来处理和捕获异常
defer func() {
err := recover() //是内置函数,可以捕获异常
if err !=nil{ //说明捕获到错误
fmt.Println(err)
}
}()
num1 := 10
num2 := 0
res:= num1/num2
fmt.Println("res=",res)
}
func main(){
test()
fmt.Println("下面的代码和逻辑..........")
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter05\demo22\mian.go
// runtime error: integer divide by zero
// 下面的代码和逻辑..........
错误处理的好处
进行错误处理后,程序不会轻易挂掉;如果加入预警代码,就可以让程序更健壮
自定义错误
go程序中,也会支持自定义错误,使用errors.New 和panic 内置函数
1)errors.New("错误说明"),返回一个error类型的值,表示一个错误
2)panic 内置函数,接收一个interface{}类型的值(也就是任何值)作为参数。可以接收error类型的变量,输出错误信息,并退出程序
package main
import (
"errors"
"fmt"
)
// func test(){
// //使用defer + recover来处理和捕获异常
// defer func() {
// err := recover() //是内置函数,可以捕获异常
// if err !=nil{ //说明捕获到错误
// fmt.Println(err)
// }
// }()
// num1 := 10
// num2 := 0
// res:= num1/num2
// fmt.Println("res=",res)
// }
//函数去读init.conf
func readConf(name string) (err error){
if name == "init.conf"{
fmt.Println("启动")
return nil
}else{
// 返回一个自定义错误
return errors.New("指定文件错误")
}
}
func test01() {
err := readConf("init.conf")
if err != nil {
//如果读取文件发生错误,就输出这个错误并终止程序
panic(err)
}
fmt.Println("test02()继续执行")
}
func main(){
// test()
// fmt.Println("下面的代码和逻辑..........")
test01()
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter05\demo22\mian.go
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter05\demo22\mian.go
// panic: 指定文件错误 //文件不一致报错
// goroutine 1 [running]:
// main.test01()
// D:/golang/goproject/src/src01/go_code/src/chapter05/demo22/mian.go:36 +0x76
// main.main()
// D:/golang/goproject/src/src01/go_code/src/chapter05/demo22/mian.go:45 +0xf
// exit status 2
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter05\demo22\mian.go
// 启动
// test02()继续执行

浙公网安备 33010602011771号