(3).数组类型
数组是相同数据类型的一组数据的集合,数组一旦定义长度就不能修改,数组可以通过下标(或者索引)来访问元素。
1.数组的定义 var identify [n]type=[n]type{value} 2.假如说定义的数组长度没有超过定义的长度,那么就补空 3.数组的初始化,用"..."可以不用设置固定长度,会自动根据我们添加的值进行分配空间 var identify=[...]int{xxx} 4.可以指定索引赋值 arr := [...]int{索引:value,索引:value} arr := [...]int{0: 1, 2: 2, 5: 3} for _, i2 := range arr { println(i2) } -> 1 0 2 0 0 3 5.数组的值可以被修改,不像字符串那样不可以被修改。 6.获取数组的长度 len(identify) 7.数组的遍历: 普通for 增强for -----------
(4).切片类型 -> 引用类型
- 切片的定义和初始化
切片同属的讲:就是一个动态数组,增加了自动扩容的功能。切片是一个拥有相同类型元素的可变长度的序列。
1.切片的定义: 声明一个数组只要不添加长度即可 (1). var identify []type=[]type{} (2). identify []type (3). identify:=[]type{} 2.切片是引用类型,可以使用make函数来创建切片。 var slice []type=make([]type,len) 可以简写为: slice:=make([]type,len)
make([]T,len,capacity) ->这里的len是数组的长度并且是切片的初始长度
3.获取长度 len(str) csp(str) ------------------------------------- 4.切片的初始化->和数组的初始化一样 identify:=[]type{xxxx} 5.切片的初始化使用数组->可以先设置一个数组然后切片切取 s:=[3]int{1,2,3} -> slice:=s[:] 6.切片的遍历 和数组的遍历一样 for 和 增强for
切片是一个动态数组,可以使用append()函数添加元素,go语言中并没有删除元素的专用方法,我们可以使用切片本身的特性来删除元素,由于,切片是引用类型,通过赋值的方式,会修改原有的内容,go提供copy()函数来拷贝切片
- 切片的CRUD
make() 用于分配地址
1.添加元素 append(数组,添加的元素) (1).添加到元素的尾部 var str = []int{} ints := append(str, 10, 20, 30) //进行添加的操作 (2).添加到指定元素的后面 ---- 整形指定位置插入 func insertByIndex(index int, value int, slice []int) []int { var str2 = make([]int, len(slice)) copy(str2, slice) //把ints拷贝给str2 str3 := []int{} //定义一个空切片 str3 = append(slice[:index], value) //先获取头部 str3 = append(str3, str2[index:]...) return str3 } ---------- 2.删除元素 (1).利用空切片进行删除的操作 ints->是一个数组,删除索引为1的值 var slice []int = []int{} intss := append(slice, ints[:1]...) intss = append(intss, ints[2:]...) fmt.Printf("%d", intss) (2).直接追加的方式删除 A=append(a[:index],a[index+1]...) ints = append(ints[:1], ints[2:]...) fmt.Printf("%d", intss) 3.修改 -> 直接 slice[index]=? 4.查找 (1).通过下标进行寻找 -> slice[index] (2).通过值进行查找索引 ->遍历 ---------------------------- 5. copy()函数 (1).如果是直接赋值的话,那么内存地址一样,修改s2的时候同一个地址的s1也会被修改 直接赋值: ---->s1:=s2 (2).如果利用copy()函数的话,那么内存地址不一样。修改s2那么s1不会改动 基本步骤: 先分配给s1地址-> var s2=make([]int,4) //切片类型,四个空间 开始拷贝 copy(s1,s2) -> 意思就是把s2拷贝给s1
(5).函数类型 ✅
函数在go语言中属于 一级公民 ,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称、参数列表和返回值类型,这些构成了函数的签名。
1 Go语言中函数的特性
- go语言中有3中函数:
普通函数
,匿名函数
,方法
(定义在struct上的函数) - Go语言中
不允许函数重载
,也就是说不允许函数同名
- Go语言中的
函数不能嵌套函数
,但可以嵌套匿名函数
- 函数
可以作为参数传递给另一个函数
- 函数的返回值可以是一个
函数
- 函数是一个值,
可以将函数赋值给变量
,使得这个变量也成为函数 - 函数调用的时候,如果
有参数传递给函数,则先拷贝参数的副本,再讲副本传递给函数
函数参数可以没有名称
。
- Go语言函数的定义
1. 定义一个普通函数 func function_name([parameter list] type) [return_type]{ 函数体 }
- 函数的返回值
函数可以有0个或则多个返回值,返回值需要指定数据类型,返回值通过return关键字来指定。
- return 关键字中指定了参数时,返回值可以不用名称。如果 return 省略参数,则返回值部分必须带名称。
- 当返回值有名称时,必须使用括号包围,逗号分隔,即使只有一个返回值
- 即使返回值重名了,return中也可以强制指定其他返回值的名称,也就是说return的优先级更高。
- 命名的返回值时预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变量不能重复定义。
- return 中可以有表达式,但不能出现赋值表达式,这和其他语言可能有所不同。列如 return a+b是正确的,但return c=a+b是错误的。
1. 没有返回值 func test1(){} 2. 有参数且返回值有名字 func test(a int) (sum int){ sum=1+a return sum} 3.有参数但返回值没有名字 func test(a int) (int){sum:=1+a return sum} 4有参数且有多个返回值 func test()(sum int,sum2 int){return sum,sum2} 5.覆盖命名返回值 func test()(a int){return sum} ->sum会覆盖a 6.如果返回值不想要,可以用匿名变量进行接受
- Go函数的参数
- go函数可以有0个或多个参数,且参数需要指定数据类型
- 声明函数时的参数列表叫做形参,调用时传递的参数叫做实参
- go语言的函数是通过值传递进行传递的,意味着传递给函数的参数是一个拷贝的副本,形参改变实参不会变化。
- go语言的函数可以使用可变长参数,有时候并不能呢确定参数的个数。
1. 可变参数 ->可变参数只能放在最后且只有一个 func test(args...int){ 函数体 }
- 函数类型和函数变量
可以使用type 关键字来定义一个函数类型
1.定义一个函数类型的f1,f1是一个类型并不是一个变量名 type f1 func(int,int) int 2.相同类型的函数赋值给函数变量 package main import "fmt" func test(a int) int { return a } func main() { type f1 func(int) int // 这里的就是定义一个f1这样的类型 var ff f1 //定义f1类型的变量ff ff = test // 把test变量赋值给ff变量 fmt.Printf("%v", ff(2)) //开始调用 } ---------------------**********-------------------------
6.高阶函数
go语言的函数,可以作为函数的参数,传递给另外一个函数,可以作为另外一个函数的返回值返回,高阶函数就是参数类型带函数参数的函数。
1.go语言函数作为参数 package main func test(a int) int { return a } func test2(b int, f func(int) int) int { return b + f(b) } func main() { println(test2(1, test)) } -------------------
7.匿名函数
go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现以一下简单功能的调用
。所谓匿名的函数就是: 没有名称的函数。
语法格式如下:
1.匿名函数的调用和执行 func(){函数体}() ------- // 直接执行 func() { println("您好,我是小明") }() 2.匿名函数设置变量名 max,只有匿名函数才能设置变量名。 // 给匿名设置一个变量名 max := func(a int, b int) { if a > b { fmt.Printf("%v", a) } else { fmt.Printf("%v", b) } } max(1, 2)
- 闭包
闭包可以理解成: 定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部链接起来的桥梁。或者说是函数和其引用环境的组合体。
闭包指的是一个函数和与其相关的引用环境组合而成的实体,简单说,闭包+函数+引用环境。
闭包: 函数内部定义一个函数,且外层函数的返回值类型是内层函数的函数类型。
特性: 内层函数可以调用外层变量。外层变量一直在内层函数的作用域里面
外层变量的作用域扩展至内层函数中。前提是函数没有再次被从新分配空间
package main import "fmt" func test(a, b int) func(c, d int) int { sum1 := a + b //这里是闭包,它只要函数没有再次被定义,那么他的作用域就被内层函数所包含 sum := func(c, d int) int { return sum1 + c + d } return sum //返回值是内层函数类型 } func main() { f := test(1, 2) //赋值变量类型 fmt.Printf("%T\n", f) fmt.Printf("%v", f(1, 2)) }
- 递归
函数内部调用函数自身的函数称为递归函数。
递归函数的最重要的三点:
- 递归是自己调用自己
- 必须先定义一个函数的退出条件,没有推出条件,递归将称为死循环
- go语言递归函数很可能会产生一大堆的栈溢出问题,也很可能会出现占空间内存溢出问题。
package main func test(a int) int { if a == 1 { //直接返回这个值,不会再向下执行了。 return 1 } return a + test(a-1) } func main() { println(test(5)) }
- defer 语句
Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,即延迟处理的语句按defer定义的逆序进行执行,也就是说: 先被defer的居于最后执行,最后defer的语句最先执行。
defer特性:
- 关键字defer用于注册延迟调用
- 这些调用直到return前才被执行,因此,可以用来做资源清理。
- 多个defer语句,按先进后出的方式执行。
- defer语句中变量,在defer声明就决定了。
1.如何定义一个defer语句 defer 语句[可以是输出语句/特殊函数] 2.如果有多个defer会按照先定义后执行的顺序进行处理 3.defer,只是进行了延迟的处理,但里面的参数在声明的时候就已经配置好了
- init函数
Go语言有一个特殊的函数 init
函数,先于main
函数执行,实现包级别的一些初始化操作
init函数的特点
- init先于main函数 自动执行,不能被其他函数所调用
- init函数没有输入参数、返回值
- 每个包可以有多个init函数
- 包的每个源文件也可以有多个init函数,这点比较特殊
- 同一个包的init执行顺序,Go没有明确定义,编程时要注意程序不要依赖这个执行顺序
- 不同包的init函数按照包导入的依赖关系决定执行顺序
GO初始化顺序
初始化顺序: 变量初始化->init()->main()
1. init函数的定义 func init() { //没有参数、没有返回值 不能被调用 函数体 } 2.go语言的执行顺序 | 变量初始化->init()->main() package main func init() { //没有参数、没有返回值 println("init........") // ->2 } var value int = test() //变量的初始化 ->1 func test() int { println("初始化数据....") return 100 } func main() { println("main....") // ->3 } 3.在一个包中可以定义多个init()函数,执行顺序是从上到下
(6).布尔类型
var identify bool=flag identify:=flag
(7).数字类型
Go语言支持整数和浮点型数字,并且原生支持复数,其中位的运算采用补码。
1.整数类型
有符号(xxx)、无符号(uxxx)。
%d ->十进制取值 %b->二进制取值 %o->八进制取值 %x/%X->十六进制
2.浮点型
%f ->取值 %.nf%->保留n位小数
(8).map类型 -> 引用类型
map是一种 key:value
键值对的数据结构容器。map内部实现是哈希表
(hash)。map最重要的一定是通过key来快速检索数据,key类似于索引,指向数据的值。map是引用类型
的。
map的定义与初始化
可以使用内建函数make, 也可以使用map关键字来定义map
1. 使用map来定义 var 变量名称 map[key的数据类型]value的数据类型 2. 使用make函数 变量名称:=make(map[key的数据类型]value的数据类型) (1).两种初始化方式以及两种定义方式 var m1 map[string]string // 利用map定义一个map 初始化为空 m2 := make(map[string]string) //利用make定义一个map 初始化为空 var m3 = map[string]string{"name": "李明", "age": "18", "high": "1.75"} //定义并初始化数据 var m4 = map[string]string{"age": "18"} //定义并初始化 ******************************* 3.通过key值进行查找数据 变量名[key] fmt.Printf("%v", m1["name"]) 4.判断某一个key是否存在💥 v,ok:=变量名[key] v是value ok是布尔类型 v, ok := m1["name"]
map的遍历
1. 可以只获取key 2. 可以获取key和value 3. 可以只获取value for key,value:=range 变量名{} ----- m1 = map[string]string{"name": "李明", "age": "18", "high": "1.75"} //初始化数据 for k := range m1 { fmt.Printf("%v\t", k) } for k, v := range m1 { fmt.Printf("%v %v ", k, v) } for _, v := range m1 { fmt.Printf("%v\t", v) }
(9).结构体类型
- 类型定义和类型别名
1. 类型定义语法 type NewType Type -》等同于Type ------------测试使用 package main func main() { // 类型定义->实际上就是说 MyInt 等同于 int type MyInt int // i 为MyInt类型的变量,实际上等同于int类型的变量 var i MyInt i=100 println(i) } ------------- 2. 类型别名定义 type NewType=Type ->就是起一个别名 -------测试 package main func main() { // 类型别名定义->实际上就是说 MyInt 等同于 int type MyInt=int // i 为MyInt类型的变量,实际上等同于int类型的变量 var i MyInt i = 100 println(i) }