Featured image of post golang学习十一:包的访问权限、变量作用域、闭包

golang学习十一:包的访问权限、变量作用域、闭包

包概述

  • 包(package)是Go语言中组织单元.包是逻辑上的分组.而物理上的分组是不同的文件夹,文件夹和包一般是对应的
  • 把多个文件放入同一个文件夹中,这些文件就是在同一个包中.
  • 虽然允许源码文件的package和文件夹名不同但是最终编译后都会把文件的package编译成文件夹名称.所以为防止错误最好把文件的package和文件夹名称设置成相同的
  • 一个Go语言项目必须要有main包,其他自定义名称的包个数任意,根据自己的需求即可.
  • Go语言在寻找包时会从GOPATH/src 路径中寻找包,如果不存在去GOROOT/src(Go语言标准库源码所在文件夹)下找
  • 不同包下资源可以相互访问,在导入其他包后,可以访问包下首字母大写的内容
  • 同包下不同文件中全局资源可以随意访问

自定义包

  • 新建项目后在项目下新建src文件夹,在src文件夹中新建demo文件
  • 在demo文件中新建demo1.go和demo2.go文件
  • demo1.go文件源码如下
1
2
3
4
5
6
7
package demo//包为demo

import "fmt"

func demo1(){
	fmt.Println("执行demo1")
}
  • demo2.go文件源码如下
1
2
3
4
5
6
7
8
package demo//包为demo

import "fmt"

func Demo2()  {//函数名大写才能被其他包访问
	fmt.Println("执行demo2")
	demo1()//同包下内容任意访问
}
  • 在项目根目录下新建main.go,源码如下
1
2
3
4
5
6
7
package main

import "demo"

func main() {
	demo.Demo2()
}
  • 运行整个项目后,发现可以正常调用Demo2()函数
  • 整个程序目录结构如下

变量作用域

  • 变量声明位置决定了变量的可访问范围(哪里能调用到变量)
  • Go语言中变量的有效范围如下
    • 函数级别:变量声明在函数内部,只有在函数内部才能访问,称变量为局部变量
    • package 包级别,在当前包下都可以访问.称变量为全局变量.变量声明在函数外面
    • 应用级别,在整个应用下任何包内都可以访问.通过首字母大小写控制

局部变量

  • 局部变量一定是在函数内部
  • 在哪个{}内部声明,只能在哪个{}内部访问
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func test1() {
	i := 2 //从此处开始到test1结束}任何位置都能调用i
	if i>=2{
		j:=3
		fmt.Println(i+j)//此处可以访问i
	}
	fmt.Println(i)
	//fmt.Println(j)//此处不能调用j,超出声明j时{}外
}
func test2() {
	fmt.Println(i) //此处无法调用test1()中的i
}

全局变量

  • 全局变量声明到函数外部,整个包都可以访问
  • 如果全局变量首字母大写,跨包也可以访问.
  • 声明全局变量时规范是
1
2
3
4
var (
	变量名
	变量名=
)
  • 全局变量代码示例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var (
	name = "smallming"
	age  = 17
)

func demo1() {
  	fmt.Println("名字:",name)
}

func demo2() {
  	fmt.Println("年龄:",age)
}

闭包概述

  • 闭包不是Go语言独有的概念,在很多编程语言中都有闭包
  • 闭包就是解决局部变量不能被外部访问一种解决方案
  • 是把函数当作返回值的一种应用

代码演示

  • 总体思想为:在函数内部定义局部变量,把另一个函数当作返回值,局部变量对于返回值函数就相当于全局变量,所以多次调用返回值函数局部变量的值跟随变化
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {
	//res其实就是test1返回值函数,和之前匿名函数变量一个道理
	res := test1()
	fmt.Println(res()) //输出2
	fmt.Println(res()) //输出3
	fmt.Println(res()) //输出4
}

//注意此处,返回值类型是func int
func test1() func() int {
	i := 1
	return func() int {
		i = i + 1
		return i
	}
}
  • 如果重新调用test1()会重新声明及赋值局部变量i
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

func main() {
	f := test1()
	fmt.Println("f的地址", f) //输出匿名函数地址
	fmt.Println("f:", f()) //调用匿名函数输出2
	fmt.Println("f:", f()) //调用匿名函数输出3
	k := test1()
	fmt.Println("k的地址", k) //输出匿名函数地址,与f相等
	fmt.Println("k:", k()) //调用匿名函数输出2
	fmt.Println("f:", f()) //输出:4
	fmt.Println("k:", k()) //输出:3
}

func test1() func() int {
	i := 1
	return func() int {
		i++
		// 每调用一次test1()输出的地址不一样
		fmt.Println("i的地址:", &i)
		return i
	}
}

golang学习一:从环境配置开始到HelloWorld入门 golang学习二:golang自带的工具 olang学习三:golang基础语法 golang学习四:流程控制 golang学习五:常用数学函数与数组 golang学习六:for循环 golang学习七:goto和label golang学习八:切片 golang学习九:sort包、map、双向链表、双向循环链表 golang学习十:函数 golang学习十一:包的访问权限、变量作用域、闭包 golang学习十二:值传递和引用传递 golang学习十三:结构体 golang学习十四:golang中的面向对象 golang学习十五:错误异常处理 golang学习十六:文件操作 golang学习十七:反射 golang学习十八:XML操作 golang学习十九:日志 golang学习二十:golang并发编程入门 golang学习二十一:select和GC

Licensed under CC BY-NC-SA 4.0