Golang学习记录6——函数2(包引入)

=====================================================================

  1. 我们不可能把所有的函数放在同一个源文件中,可以分门别类的把函数放在不同的源文件中
  2. 解決同名问题:两个人都想定义一个同名的函数,在同一个文件中是不可以定义相同名字的函数的,此时可以用包来区分。

包的定义

  1. 目录结构:
code/
│   └── demo6/
│       └── utils/
│       	└── util.go
│       └── main/
│       	└── mian.go
└── go.mod
  1. 包的定义:
package utils   //包的声明,规范上与文件夹名一致
 
import (       //倒入所需函数
	"fmt"
)
 //定义包中的函数 utils1
func Utils1(a int)  {
	fmt.Println("程序调用了utils包中的utils1函数。")
 }

主函数:

package main

import (
	//"chapter5/demo6/untils"   // package chapter5/demo6/untils is not in std (/usr/local/go/src/chapter5/demo6/untils)
	//上面的包是在GOROOT下src中寻找的
	"code/chapter5/demo6/untils"    //gomod管理包,从项目开始导路径
)
func main()  {
	//特别注意。包内函数要被外界函数访问,函数首字母必须大写!否则就算有定义,外部也是访问不到的(说为定义)
	//utils.utils1(10)       ./main.go:9:8: undefined: utils.utils1

	utils.Utils1(10)
}
程序调用了utils包中的utils1函数。
  1. 详细说明:
    • 文件夹名,go文件名,包名没有必然联系。文件夹——用于导入包时候路径;go文件名——可任意;包名——调用包内函数。
    • 在package声明的时候,尽量使得包名和所在的文件夹同名(更加易读,但可不同)
    • mian包是入口包,一般放在main文件夹下
    • 在自定义包的时候,首先需要注意的是,需要包外调用的函数名一定要大写,否则在外部调用的时候,会显示没有此函数。./main.go:9:8: undefined: utils.utils1
    • 一个目录下的同级文件归属一个包,但可在该文件夹下的子文件夹定义另一个包,如:file1,file2,file3各属于一个包。(如果和文件有同级文件夹,即使子文件夹放的还是同一个包,也无法搜索到,会被认为未定义)——相当于在搜索包的时候,只会搜索导入文件夹下的go文件。
myproject/
└── mypackage/
    ├── file1.go
    ├── folder1/
    │   └── file2.go
    └── folder2/
        └── file3.go
  • 同一个目录下不能有重复的函数(名称),因为在main函数中调用的时候,定位函数为包所在文件夹+包+函数,格式如下:
package main

import "包所在路径"

func main(){.函数()
}
PS:基于后面两点限制,同一文件夹下,不能定义不同的包,所以不存在“同一文件夹下有不同的包,不同包下有同名函数”。

包的导入

  1. GOPATH方式进行管理包:
  • 使用这种方式,首先要配置环境变量GOPATH,项目文件必须放在GOPATH/src
  • 使用这种方式进行包管理,在导入自定义包的时候,包放在GOPATH/src目录之后进行导入。
  • PS:使用这种方式,所有的包程序都糊去GOPATH/src路径下进行寻找,使用go get 下载的资源 也会放在gopath/src下
  1. 使用go mod进行包管理

标准库包:
标准库包是Go语言官方提供的包,它们通常位于Go语言安装的根目录下的 src 文件夹中。例如,标准库包 fmt 的路径可能是 GOROOT/src/fmt,其中 GOROOT 是你的Go语言安装路径。
直接使用包的名称导入,无需指定完整路径。例如:

import "fmt"

第三方包:
第三方包通常存储在版本控制系统(如GitHub)上。当你使用 go get 或Go Modules下载这些包时,它们会被存放在你的工程目录下的 go/pkg/mod 目录中,每个包都有一个对应的子目录,用于存放不同版本的包。
使用包的完整路径导入,通常是版本控制系统的路径。例如:

import "github.com/gin-gonic/gin"

在代码中引用了名为 gin 的第三方包,该包位于GitHub上的 github.com/gin-gonic 仓库中。导入路径应该准确反映包的位置,以便Go工具能够正确下载和管理依赖。

自定义包:
自定义包是你自己创建的包,通常存储在你的项目中。你可以将自定义包放在项目的 pkginternal 或其他自定义目录中,具体位置取决于你的项目结构。
在项目文件下的自定义包的导入方式取决于自定义包的相对位置。假设你有以下项目结构:

myproject/
├── cmd/
│   └── main.go
├── pkg/
│   └── mypackage/
│       └── mypackage.go
└── go.mod

如果你的 main.go 文件位于 cmd 目录下,而你的自定义包 mypackage 位于 pkg 目录下,你可以使用相对路径导入

// 在 main.go 中导入自定义包
import "../pkg/mypackage"

或者,如果使用 Go Modules 进行包管理,可以使用模块路径

// 在 main.go 中导入自定义包
import "myproject/pkg/mypackage"

确保你的导入路径相对或绝对地反映了自定义包的位置,这样可以确保Go工具正确地解析和导入你的自定义包。

重命名包
在导入包的时候可以重命名包,并且在后续使用的时候,可以使用重命名代替包名。

package main

import (
	test "code/chapter5/demo6/utils"  
)
func main()  {
	test.Utils1(10)
}

初始化函数init()

  1. 初始化函数
    可以用来进行一些初始化的操作,每一个源文件都可以包含—个init函数,该函数会在main函数执行前被Go运行框架调用。
package main

import "fmt"

func init()  {
	fmt.Println("Init函数被执行")
}

func main()  {
	fmt.Println("main函数被执行")
}
Init函数被执行
main函数被执行
  1. 全局变量定义,init函数,main函数的执行流程?
package main

import "fmt"

//全局变量接受函数返回值——如果该变量那个被执行,则输出
var a int= test()

func test () int {
	fmt.Println("全局变量被执行")
	return 1
}

func init()  {
	fmt.Println("Init函数被执行")
}

func main()  {
	fmt.Println("main函数被执行")
}
全局变量被执行
Init函数被执行
main函数被执行
  1. 多个源文件都有init函数的时候,如何执行:
    文件结构:
code/
├── chapter5/
│   └── funcinit/
│   	└── main/
│      		└── mypackage.go
│       └── utils
│      		└──u1.go
│      		└──u2.go
└── 

main.go

package main

import (
	"fmt"
	"code/chapter5/demo7/funinit/utils"
)

//全局变量接受函数返回值——如果该变量那个被执行,则输出
var a int= test()

func test () int {
	fmt.Println("全局变量被执行")
	return 1
}

func init()  {
	fmt.Println("Init函数被执行")
}

func main()  {
	//u1.F1()
	u1.F2()
	fmt.Println("main函数被执行")
}

u1.go

package u1 
import "fmt"
var a int= test()

func test () int {
	fmt.Println("u1.go全局变量被执行")
	return 1
}

func init()  {
	fmt.Println("u1.go.init函数被执行")
}
func F1()  {
	fmt.Println("f1函数被执行")
}

u2.go

package u1
import "fmt"
func init()  {
	fmt.Println("u2.go.init函数被执行")
}

func F2()  {
	fmt.Println("f2函数被执行")
}

输出:

u1.go全局变量被执行
u1.go.init函数被执行
u2.go.init函数被执行
全局变量被执行
Init函数被执行
f2函数被执行
main函数被执行

从上面的输出可以看到:

  1. 包中不同的go文件都包含一个init函数,所以init函数特殊,可以同名。
  2. 主函数中,即使不调用u2.go中的函数,其中的init函数同样会执行。
  3. 包中的全局变量最先执行,即使不调用该源文件中的函数
  4. 执行顺序:导入包中所有源文件的init函数(不同文件中的init函数执行顺序和go文件名有关),全局变量,主函数init函数、main函数(函数执行按顺序)。

从代码运行角度看:
代码运行顺序
程序从main函数开始执行,在导入包的时候,会加载包中所有的源文件(但不执行其中的函数)(但和main.go一样,只是不执行main函数,会先执行全局变量和init函数),当加载完成,则会继续执行main.go中语句,依次往下,在main函数中,包内函数被调用时执行。

请添加图片描述

匿名函数

  1. Go支持匿名函数,匿名函数只会执行一次
  2. 匿名函数使用方式(mian函数内部定义):
  • 定义匿名函数时就直接调用,这种方式匿名函数只能调用一次(常用
package main

import "fmt"

func main()  {
	
	//变量a直接收匿名函数传回的返回值
	//定义的同时就调用
	a := func (num1 int, num2 int) int {       //此处a为匿名函数返回的值
		return num1+num2
	}(1,2)      //1,2为匿名函数传入的形参

	fmt.Println(a)
}
  • 将匿名函数赋给一个变量(该变量就是函数变量了),再通过该变量来调用匿名函数。这种方式实际上是给了匿名函数一个函数名,通过该函数名也可以多次调用。 重复调用可以直接定义成普通函数
package main

import "fmt"


func main()  {
	
	//将匿名赋给变量,使用函数变量进行调用
	a := func (num1 int, num2 int) int {     //此处a为匿名函数类型
		return num1+num2
	}          //此处不传入形参

	fmt.Printf("%T\n",a)

	fmt.Println(a(1,2))   //调用匿名函数
	fmt.Println(a(2,2))   //调用匿名函数
}
func(int, int) int
3
4
  1. 以上的匿名函数使用都是在main函数内部进行调用,如何让一个匿名函数,可以在整个程序中有效呢?将匿名函数给一个全局变量就可以了。 其实还是上文中第二部分多次使用的形式
package main

import "fmt"

//定义函数类型的全局变量,以便整个程序搜能使用(包括其他包)
var A = func ()  {        //注意事项::=不能第一全局变量
	fmt.Println("匿名函数被调用")	
}

func test()  {

	fmt.Println("调用test函数")
	A()
}
func main()  {

	test()
	A()

	
}
调用test函数
匿名函数被调用
匿名函数被调用

PS::=定义变脸的形式只能使用在函数内部,不能用这种方式定义全局变量!!!!全局变量只能使用var定义

闭包

  • 闭包就是一个函数与其相关的引用环境组合的一个整体
package main

import "fmt"
//getSum函数,传入参数为空,返回值为一个函数
func getSum() func (int) int{   //此处返回变量类型

	var n int = 10
	return func (num int) int{   //传入参数为int类型,返回为int类型  //只能定义为匿名函数
		n = n + num      //此处是可以使用外部的参数的  //可以将n理解为这个函数的全局便利那个
		return n
	}    
}

func main()  {
	test := getSum()   //调用了getSum函数,则test为一个函数类型便利那个
	
	fmt.Println(test(1))
	fmt.Println(test(2))
	fmt.Println(test(3))

}
11
13
16

可以发现,函数会存储值,所以可以将需要存储(引用)的值放入外层函数里面,外面的参数可以理解为返回函数的全局变量

  • 闭包的本质:
    闭包本质一个匿名函数,只是这个函数引入外界的变量/参数
    匿名函数+引用的变量/参数=闭包==(引用的变量/参数外层)==
  • 特点:
    (1)返回的是一个匿名函数,但是这个匿名函数引用到函数外的变量/参数,因此这个匿名函数就和变量/参数形成一个整体,构成闭包
    (2)闭包中使用的变量/参数会一直保存在内存中,所以会一直使用。闭包不可滥用
  • 如果不使用闭包,也可以使用全局变量进行存储值,可以在函数内部改变全局变量
package main

import "fmt"

//定义全局便利那个
var sum  int = 0     //全局变量存储和

//定义累加函数
func getSum(num int) int{
	sum += num    //改变全局变量的值
	return sum
}

func main()  {
	test := getSum  //重命名函数
	
	fmt.Println(test(1))
	fmt.Println(test(2))
	fmt.Println(test(3))

}

1
3
6

defer关键字

在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer关键字

package main

import "fmt"

func main()  {
	defer fmt.Println("defer1")
	defer fmt.Println("defer2")
	
	fmt.Println("1")
	fmt.Println("2")
}
1
2
defer2
defer1

从上面的运行结果我们可以观察到,defer的执行类似于栈的操作:遇到defer不执行,入栈,当函数中其他语句执行完成,则开始出栈。
所以当需要关闭某个资源的时候,可以不用思考在何处关闭,或者在程序结束前忘记关闭,defer会在程序结束前执行

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值