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。两个程序员吵起来

包的原理

包的本质实际上就是创建不同的文件夹,来存放程序文件‘’

image

 

image

包的基本概念

说明 go 的每一个文件都属于一个包,也就是说go是以包的形式来管理文件和项目目录结构的

包的作用

1)区分相同名字的函数、变量等标识

2)当程序文件很多时,可以很好的管理项目

3)控制函数、变量等访问范围,即作用域

包的相关说明

打包基本语法

package util

引入包的基本语法

import “包的路径”

示例

image

自定义 包

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,这样才能跨包访问。

image

 

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

image

 

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 中,函数调用的机制底层分析

 

 

image

 

(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

  流程分析

image

 

 示例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

   流程图

image

 

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
所以是合法的。

  理解

image

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~

  执行顺序

image

 匿名函数

介绍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 里,deferreturn 的执行顺序;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)值类型默认是值传递:变量直接存储值,内存通常在栈中分配

image

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

image

 

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

image

变量作用域

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方法的话,结果是未定义的

image

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....返回指针

 

 image

示例

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

  内存分析

image

示例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()继续执行

  

 

posted @ 2026-03-10 15:37  烟雨楼台,行云流水  阅读(1)  评论(0)    收藏  举报