category
学习思考
date
Sep 28, 2022
icon
Origin
password
slug
Golonglearning
status
Published
summary
tags
Tags
type
Post
变量:
变量的引入:
一个程序就是一个世界
不论是使用那种吧高级程序语言编写程序,变量都是其程序的基本组成单位
变量的介绍:
变量相当于内存中一个数据存储空间的表示
变量的使用步骤:
1、声明 2、赋值 3、使用
var age int = 18
变量的四种使用方式:
支持一次性声明多个变量(多变量声明)
全局变量和局部变量的使用:
package main import "fmt" // 全局变量:定义在函数外的变量 var n7 = 100 var n8 = 9.7 // 设计者认为上面的全局变量写法太麻烦了,可以一次性进行声明 var ( n9 = 500 n10 = "netty" ) func main() { //定义在{}中的变量叫:局部变量 // 第一种:变量的使用方式 var num int = 18 fmt.Println(num) // 第二种:指定变量的类型,但是不赋值,使用默认值 var num2 int fmt.Println(num2) // 第三种:如果没有写变量的类型,会根据等号后面的值进行判定变量的类型(自动类型推断) var num3 = 10 fmt.Println(num3) // 第四种:省略var关键字,注意 := 不能写为 = sex := "男" fmt.Println(sex) fmt.Println("==============================") // 声明多个变量 var n1, n2, n3 int fmt.Println(n1, n2, n3) var n4, name, n5 = 10, "jack", 11.89 fmt.Println(n4, name, n5) n6, height := 6.9, 100.6 fmt.Println(n6, height) fmt.Println(n7) fmt.Println(n8) fmt.Println(n9) fmt.Println(n10) }
变量的数据类型:

这么多数据类型,使用的时候如何选择?
Golang程序中整型变量在使用时,遵循保小不保大的原则,
即:在保证程序正确运行下,尽量占用空间小的数据类型。
浮点类型介绍:
简单来说,就是用于存放小数值的,比如3.14、0.28、-7.19等等。
PS:底层存储空间和操作系统无关
PS:浮点类型底层存储:符号位 + 指数位 + 尾数位 ,所以尾数位只是存了一个大概,很可能出现精度的损失。
指针:
实际上指针就是内存地址
总结:最重要的就是两个符号:
1 & 符号可以获取地址(取内存地址)
2 * 符号可以根据地址取值
- 可以通过指针该拜年指向值
package main import "fmt" func main() { var num int = 10 fmt.Println(num) var ptr *int = &num *ptr = 20 fmt.Println(num) }
- 指针变量接收的一定是地址值

- 指针变量的地址不可以不匹配
- 基本数据类型(又叫值类型),都有对应的指针类型,形式为 *数据类型,比如int的对应的指针就是 int,float32对应的指针类型就是float32。以此类推
运算符:
运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等
一句话:为了提高优先级,可以加()
算数运算符:
+、-、*、/、%、++、--
介绍:算术运算符是对数值类的变量进行运算的,比如,加减乘除
package main import "fmt" func main() { // + 加号 1、正数 2、相加操作 3、字符串拼接 var n1 int = +10 fmt.Println(n1) var n2 int = 4 + 7 fmt.Println(n2) var s1 string = "abc" + "def" fmt.Println(s1) // 除号: // 两个int类型数据运算,结果一定为正数类型 fmt.Println(10 / 3) // 浮点类型参与运算,结果为浮点 fmt.Println(10.0 / 3) // % 取模 等价公式 : a%b = a -a/b*b // ++自增操作: var a int = 10 a++ fmt.Println(a) // go语言里,++ 、 -- 操作非常简单,只能单独使用,不能参与到运算中 // go语言里,++ -- 只能在变量的后面,不能写在变量的前面, 错误写法。 }
赋值运算符:
=、+= 、 -=*=、
获取用户终端输入:
介绍:在编程中,需要接收用户输入的数据,就可以使用键盘输入语句获取
API:func ScanIn ,ScanIn类似于ScanIn,但会在换行时菜停止扫描,最后一个条目后必须有换行或者达到结束位置。
func Scanf 从标准输入扫描文本,根据format参数指定的格式化将成功读取的空白分隔的值保存进成功传递给本函数的参数。返回成功扫描的条目个数和遇到的任何错误。
代码:
package main import "fmt" func main() { // 实现功能:键盘录入学生的年龄,姓名,成绩,是否是VIP // 方式1 :Scanln var age int fmt.Println("请输入学生的年龄:") // 传入age的地址的目的:在Scanln函数中,对地址中的值进行改变的时候,实际外面的age被影响了。 // 录入数据的时候,类型一定要匹配,因为底层会自动判断类型。 fmt.Scanln(&age) var name string fmt.Println("请输入学生的姓名:") fmt.Scanln(&name) var score float32 fmt.Println("请输入学生的成绩:") fmt.Scanln(&score) var isVip bool fmt.Println("请输入学生是否是VIP:") fmt.Scanln(&isVip) // 将上述数据在控制台打印输出 fmt.Printf("学生的年龄为:%v 姓名为:%v成绩为:%v是否为VIP:%v", age, name, score, isVip) // 方式2 :Scanf fmt.Println("请录入学生的姓名,年龄,成绩,是否为VIP,使用空格进行分隔") fmt.Scanf("%s %d %f %t", &age, &name, &score, &isVip) }
流程控制:
流程控制的作用:
流程控制语句是用来控制程序中各个语句执行顺序的语句,可以吧语句组合成能完成一定功能的小逻辑模块。
分支结构-Switch:
注意事项:
1)、switch后是一个表达式(即常量、变量、一个有返回值的函数等都可以)
2)、case后面的表达式如果是常量值(字面),则要求不能重复
3)、case后的各个值的数据类型,必须和switch的表达式数据类型一致
4)、case后面可以带多个值,使用逗号间隔,比如case 值1,值2
5)、case后面带break
6)、default语句不是必须的,位置也是可以随意的
7)、switch后也可以不带表达式,当作if分支来使用
8)、switch后也可以直接声明、定义一个变量,分号结束,不推荐
9)、switch穿透,利用faltthrough关键字,如果在case语句块后加faltthrough,则会继续执行下一个case,也叫switch穿透
循环结构-for循环
go语言只有for循环,没有while循环和do-while循环
for循环语句是支持迭代的一种通用结构,是最有效,最灵活的循环结构。for循环在第一次反复之前要进行初始化,即执行初始表达式;随后,对布尔表达式进行判定,若判定结果为true,则执行循环体,否则,中止循环;最后在每一次反复的时候,进行某种形式的”进步“,即执行迭代因子。
关键字:
break :
每个switch分支后面都省略了break 关键字,break关键字的作用是结束当前分支
总结:break 可以用在switch分支中,每个case粉之后,都用break 结束当前分支,但是在go语言中,break可以省略不写。
break可以结束正在执行的循环,break 结束离它最近的循环。如果想要结束外层循环,可是使用标签:

如果标签没有使用到,那么标签不用加,否则报错:定义未使用
continue:
作用:结束本次循环,继续下一次循环
深入理解:结束离它近的循环,继续下一次循环,也可以加标签。
goto:
Golang的goto语句可以无条件地转移到程序中指定的行。goto语句通常与条件语句配合使用。可用来实现条件转移。在go程序设计中一般不建议使用goto语句,以免造成程序流程的混乱。
函数:
为什么要使用函数:
1)、提高代码的复用,减少代码的冗余,代码的维护性也提高了
2)、函数的定义:为完成某一功能的程序指令(语句)的集合,称为函数
3)、基本语法
func 函数名(形参列表)(返回值类型列表){
执行语句
return + 返回值列表
}
函数名:
遵循标识符命名规范:见名知意 addNum,驼峰命名addNum
首字母不能是数字
首字母大写该函数可以被本包文件和其他包文件使用(类似public)
首字母小写只能被本包文件使用,其他包文件不能使用(类似private)
返回值列表:
可以返回0 个数据也可以返回多个数据
通过案例感受内存分析
package main import "fmt" // 自定义函数: 功能交换两个输 func swap(num1 int, num2 int) { var t int t = num1 num1 = num2 num2 = t } func main() { // 嗲用函数: 交换10 和20 var num1 int = 10 var num2 int = 20 fmt.Printf("交换前的两个数 : num1 = %v,num2 = %v \n", num1, num2) swap(num1, num2) fmt.Printf("交换后的两个数 : num1 = %v,num2 = %v \n", num1, num2) }
改:
package main import "fmt" // 自定义函数: 功能交换两个输 func swap(num1 int, num2 int) (int, int) { var t int t = num1 num1 = num2 num2 = t return num1, num2 } func main() { // 嗲用函数: 交换10 和20 var num1 int = 10 var num2 int = 20 fmt.Printf("交换前的两个数 : num1 = %v,num2 = %v \n", num1, num2) num1, num2 = swap(num1, num2) fmt.Printf("交换后的两个数 : num1 = %v,num2 = %v \n", num1, num2) }
- Golong中不支持重载
- Golong中支持可变参数(如果你希望函数带有可变数量的参数)
package main import "fmt" // 定义int类型的可变参数,传入0ge 。。。 n个可变数据 func test(args ...int) { // 在函数内部处理可变参数的时候,将可变参数当作切片来处理 // 遍历可变参数 for i := 0; i < len(args); i++ { fmt.Println(args[i]) } } func main() { test() fmt.Println("-------") test(1) test(1, 2, 3, 4, 5) }
- 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值
package main import "fmt" func test(num int) { num = 30 fmt.Println("test---", num) } func main() { var num int = 10 test(num) fmt.Println("main----", num) }
- 以值传递的方式的数据类型,如果希望在函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果来看类似引用传递
package main import "fmt" // 参数的类型是指针 *代表地址 func test(num *int) { // 对地址对应的变量进行改变数值 *num = 30 } func main() { var num int = 10 // 调用test函数的时候,传入num的地址 & fmt.Println("地址----", &num) test(&num) fmt.Println("main----", num) }
和&的区别 :
- & 是取地址符号 , 即取得某个变量的地址 , 如 ; &a
- 是指针运算符 , 可以表示一个变量是指针类型 , 也可以表示一个指针变量所指向的存储单元 , 也就是这个地址所存储的值 。
- 在go语言中,函数也是一种数据类型,可以赋值给一个变量, 则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
- 函数既然是一种数据类型,因此在go中,函数可以作为形参,并且调用(把函数本身当作一种数据类型)
- 为了简化数据类型定义,go支持自定义数据类型
基本语法:type 自定义数据类型名 数据类型
可以理解为:相当于起了一个别名
例如:type mylnt int ——> 这个 mylnt 就等价于 int来使用了
例如:type mySum func(int,int) int. ——> 这时mySum 就等价于一个函数类型func(int,int) int
- 支持函数返回值命名
package main import "fmt" // 定义一个函数: func test(num int) { fmt.Println(num) } // 定义一个函数,把另一个函数作为形参 func test02(num1 int, num2 float32, testFunc func(int)) { } type myFunc func(int) func test03(num1 int, num2 float32, testFunc myFunc) { fmt.Println("--------test03") } func test04(num1 int, num2 int) (int, int) { // 定义一个函数 求两个数的和和差 fmt.Println("--------test04") resul01 := num1 + num2 resul02 := num1 - num2 return resul01, resul02 } func test05(num1 int, num2 int) (sum int, sub int) { // 定义一个函数 求两个数的和和差 fmt.Println("--------test04") sum = num1 + num2 sub = num1 - num2 return } func main() { a := test //a 对应的类型是:func(int) ,test函数对应的类型是: func(int) fmt.Printf("a 对应的类型是:%T ,test函数对应的类型是: %T", a, test) // 通过该变量可以对函数调用 a(10) //等价于 test(10) test02(10, 3.19, test) test02(10, 3.19, a) // 自定义数据类型: 给int类型起了别名,myInt类型 type myInt int var num1 myInt = 30 fmt.Println("num1", num1) // 虽然是别名,但是在go中变异识别的时候还是认为myInt和int不是一种数据类型 var num2 int = 30 num2 = int(num1) fmt.Println(num2) }
包:
使用包的原因:
1)、我们不可能把所有的函数放在同一个源文件中,可以分门别类的把函数放在不同的原文件中
2)、解决同名问题:两个人都想定义一个同名的函数,在同一个文件中是不可以定义相同名字的函数的。因此可以用包来区分。
使用:
1)、package进行包的声明,建议:包的声明这个包和所在的文件夹同名
2)、main包是程序的入口包,一般main函数会放在这个包下
main函数一定要放在main包下,否则不能编译执行
3)、打包语法 : package 包名
4)、引入包的语法:improt“包的路径”,包名是从%GOPATH/src/后开始计算的,使用/进行路径分割
5)、如果有多个包,建议一次性导入,格式如下:
import(
“fmt”
“gocode/testproject01/unit5/demo09/crm/dbutils”
)
6)、在函数调用的时候前面要定位所在的包
7)、首字母大写,函数可以被其他包访问
8)、一个目录下不能有重复的函数
9)、包名和文件夹名字,可以不一样
10)、一个目录下的同级别文件购书一个包
11)、包到底是什么:
在程序层面,所有使用相同 package 包名的源文件组成的代码模块
在源文件层面就是一个文件夹
init函数:
1)、初始化函数,可以用来进行一些初始化的操作,每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被go运行框架调用。init 函数 要比main函数先执行
2)、全局变量定义,init函数,main函数的执行流程?
全局变量定义 → init函数 → main函数的执行流程
3)、多个源文件都有init函数的时候,如何执行
优先执行引用的包的init 然后执行本地init,引用包的init 按照引用顺序执行
匿名函数:
1)、go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数(用的多)
import "fmt" func main() { // 定义匿名函数: result := func(num1 int, num2 int) int { return num1 + num2 }(10, 20) fmt.Println(result) }
2)、匿名函数的使用方式:
在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次
将匿名函数赋给一个变量(该变量就是函数变量了),在通过该变量来调用匿名函数
3)、如何让一个匿名函数,可以在整个程序中有效呢?将匿名函数给一个全局变量就可以了
闭包:
- 什么是闭包:
闭包就是一个函数和与其相关的引用环境组合的一个整体
匿名函数中易用的那个变量会一直保存在内存中,可以一直使用。
- 闭包的本质:闭包的本质依旧是一个匿名函数,只是这个函数引入外界的变量/参数
匿名函数 + 引用变量/参数 = 闭包
- 特点:
1)、 返回的是一个匿名函数,但是这个匿名函数引用刀函数外的变量/参数,因此这个匿名函数就和变量/参数形成一个整体,形成闭包。
2)、闭包中使用的变量/参数会一直保存在内存中,所以会一直使用—》 意味着闭包不可以滥用(对内存消耗大)
- 可以不使用闭包吗 : 不使用闭包的时候,我想保留的值,不可以反复使用
- 闭包应用场景:闭包可以保留上次引用
defer:
在函数中,程序员经常需要创建资源,为了函数执行完毕后,及时的释放资源,Go的设计者提供defer关键字
package main import "fmt" // 函数的名字是 getSum 参数为空 返回值是一个函数,这个函数的参数是一个int类型的参数,返回值也是int类型 func add(num1 int, num2 int) int { // 在go语言中,程序遇到defer关键字,不会立即执行defer后的语句,而是将defer压入一个栈中,然后继续执行函数后面的语句 defer fmt.Println("num1= ", num1) defer fmt.Println("num2= ", num2) // 栈的特点是先入后出 // 在函数执行完毕后,从栈中取出语句开始执行 var sum int = num1 + num2 defer fmt.Println("sum= ", sum) return sum } func main() { add(30, 60) }
发现:遇到defer关键字,会将后面的代码语句压入栈中,也会将相关的值拷贝入栈中不会随着函数后面的变化而变化。
应用场景:
比如想关闭某个使用的资源,在使用的时候随手defer,因为defer有延迟执行机制(函数执行完毕再执行defer压入栈的语句),所以你用完随手写了关闭,比较省心,省事儿。比如数据库连接。
系统函数:
字符串:
1)、统计字符串的长度,按字节进行统计。
len(str) 使用内置函数也不用导包,直接用就行
2)、字符串遍历:
r:=[]rune(str)
3)、字符串转整数:
n,err := strconv.Atoi(”66)
4)、整数转字符串
str = strconv.Itoa(6887)
5)、查找子串是否在指定的字符串中
strings.Contains(”javaandgolang”,”go”)
6)、统计一个字符串有几个指定的子串
strings.Count(”javaandgolang”,”Go”)
7)、不区分大小写的字符串比较
fmt.Println(strings.EqualFold(”go”,”Go”))
8)、返回子串在字符串第一次出现的索引值,如果没有返回 -1
strings.IIndex(”javaandgolang”,”a”)
9)、字符串的替换
strings.Replace(”goandjavagogo”,”go”,”golang”,n)
n可以指定你希望替换几个,如果n = -1 标识全部替换,替换两个 n 就是 2
10)、按照指定的某个字符,为分割标识,将一个字符串分割成字符串数组
strings.Spilt(“go-python-java”,”-”)
11)、将字符串的字母进行大小写的转换
strings.ToLower(“Go”)
strings.ToLower(“go”)
12)、将字符串左右两边的空格去掉
strings.TrimSpace(” go and java ”)
13)、将字符串左右两边的指定的字符去掉
strings.Trim(”~golang~”,”~”)
14)、将字符串左边指定的字符去掉
strings.TrimLeft(”~golang~”,”~”)
15)、将字符串右边的指定的字符去掉
strings.TrimRight(”~golang~”,”~”)
16)、判断字符串是否以指定的字符串开头
strings.HasPrefix(”http://java.sun.com/jsp/jstl/fmt”,”http”)
17)、判断字符串是否以指定的字符串结束:
strings.HasSuffix(”demo.png”,”png”)
package main import ( "fmt" "strconv" "strings" ) func main() { // 统计字符串的长度,按字节进行统计 // 在golang中,汉字是utf-8字符集,一个汉字3个字节,空格是一个字节。 str := "golang 你好" fmt.Println(len(str)) // 对字符串进行遍历: // 方式一:利用键值循环: for-range for i, value := range str { fmt.Printf("索引为:%d,具体值为: %c \n", i, value) } // 方式二:利用切片 r := []rune(str) for i := 0; i < len(r); i++ { fmt.Printf("%c \n", r[i]) } //字符串转整数 func Atoi num1, err := strconv.Atoi("666") fmt.Println(num1, err) //整数转字符串 func Itoa str1 := strconv.Itoa(88) fmt.Println(str1) // 统计一个字符串有几个指定的子串: count := strings.Count("golangandjava", "ga") fmt.Println(count) // 不区分大小写的字符串比较 flag := strings.EqualFold("hello", "HELLO") fmt.Println(flag) // 区分大小写进行字符串比较 fmt.Println("hello" == "HELLO") // 返回子串在字符串中第一次出现的索引数 fmt.Println(strings.Index("golangandjava", "ga")) }
日期和时间函数:
1)、时间和日期的函数,需要导入time包,所以获取当前时间就要调用Now函数
package main import ( "fmt" "time" ) func main() { now := time.Now() fmt.Printf("%v````````对应的类型为 %T", now, now) // 2023-01-24 08:26:33.955499 +0800 CST m=+0.000164793 // Now 函数的返回值是一个结构体 对应的类型是 time.Time // 调用结构体中的方法: fmt.Printf(" 年: %v \n", now.Year()) fmt.Printf(" 月: %v \n", now.Month()) fmt.Printf(" 日: %v \n", now.Day()) fmt.Printf(" 时: %v \n", now.Hour()) fmt.Printf(" 分: %v \n", now.Minute()) fmt.Printf(" 秒: %v \n", now.Second()) // 将日期以年月日时分秒按照格式 fmt.Println("------------------------------") fmt.Printf("当前年月日是: %d-%d-%d 时分秒:%d-%d-%d \n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()) }
错误处理:
错误处理/捕获机制:
go中追求代码的优雅,引入机制:defer + recover机制处理错误
内置函数recover
func recover
func recover() interface{
}
内置函数recover允许程序管理恐慌过程中的go程。在defer的函数中,执行recover调用会取回传至panic调用的错误值,恢复正常执行,停止恐慌过程。若recover在defer之外被调用,它将不会停止恐慌过程序列。在此情况下,或当该值go程不在恐慌过程中时,或提供给panic的实参为nil时,recover就会返回nil
package main import "fmt" func main() { test() fmt.Println("上面的除法执行成功") fmt.Println("正常执行下面的逻辑") } func test() { // 利用 defer 和 recover 来捕获错误,defer后加上匿名函数的调用 “()” defer func() { // 调用 recover 内置函数, 可以捕获错误 err := recover() // 如果没有捕获错误,返回值为零值nil if err != nil { fmt.Println("错误已经捕获") fmt.Println("err是:", err) } }() num1 := 10 num2 := 0 result := num1 / num2 fmt.Println(result) }
自定义错误:
需要调用 errors包下的new函数,操作
package main import ( "errors" "fmt" ) func main() { err := test() if err != nil { fmt.Println("自定义错误结果是:", err) } fmt.Println("上面的除法执行成功") fmt.Println("正常执行下面的逻辑") } func test() (err error) { // 利用 defer 和 recover 来捕获错误,defer后加上匿名函数的调用 “()” num1 := 10 num2 := 0 if num2 == 0 { // 抛出自定义错误; return errors.New("除数不能为0哦~~~~~") } else { // 如果除数不为 0 ,那么正常执行就可以了 result := num1 / num2 fmt.Println(result) return nil } }
有一种情况:程序出现错误以后,后续代码就没必要执行,想让程序中断,退出程序:
借助:builtin包下的 panic函数,内置函数panic停止当前go程的正常执行。
package main import ( "errors" "fmt" ) func main() { err := test() if err != nil { fmt.Println("自定义错误结果是:", err) panic(err) } fmt.Println("上面的除法执行成功") fmt.Println("正常执行下面的逻辑") } func test() (err error) { // 利用 defer 和 recover 来捕获错误,defer后加上匿名函数的调用 “()” num1 := 10 num2 := 0 if num2 == 0 { // 抛出自定义错误; return errors.New("除数不能为0哦~~~~~") } else { // 如果除数不为 0 ,那么正常执行就可以了 result := num1 / num2 fmt.Println(result) return nil } }
数组:
import "fmt" func main() { // 给出 5 个学生的成绩,求出成绩的总和,平均数 score1 := 95 score2 := 85 score3 := 75 score4 := 65 score5 := 55 // sum sum := score1 + score2 + score3 + score4 + score5 // avg avg := sum / 5 fmt.Printf("成绩的总和为:%v,成绩的平均数为:%v", sum, avg) }
缺点:
成绩变量定义个数太多,成绩管理费劲,维护费劲,数组解决问题
package main import "fmt" func main() { // 给出 5 个学生的成绩,求出成绩的总和,平均数 // 定义一个数组 var scores [5]int // 将成绩存入数组 scores[0] = 95 scores[1] = 85 scores[2] = 75 scores[3] = 65 scores[4] = 55 // sum sum := 0 for i := 0; i < len(scores); i++ { // 定义一个变量专门接受成绩的和 sum += scores[i] } // avg avg := sum / len(scores) fmt.Printf("成绩的总和为:%v,成绩的平均数为:%v", sum, avg) }
总结 :
定义格式:
var 数组名[数组大小]数组类型
例如: var scores [5]int
package main import "fmt" func main() { var arr [3]int16 // 获取数组的长度 fmt.Println(len(arr)) // [0 0 0 ] fmt.Printf("arr 的地址为:%p \n", &arr) // 第一个空间的地址 fmt.Printf("arr[0] 的地址为:%p \n", &arr[0]) fmt.Printf("arr[1] 的地址为:%p \n", &arr[1]) fmt.Printf("arr[2] 的地址为:%p \n", &arr[2]) }
DAP server listening at: 127.0.0.1:5021
Type 'dlv help' for list of commands.
3
arr 的地址为:0xc0000180a8
arr[0] 的地址为:0xc0000180a8
arr[1] 的地址为:0xc0000180aa
arr[2] 的地址为:0xc0000180ac
数组的有点:访问、查询、获取 速度快
数组每个空间占用的字节数 取决于数组类型 地址 = 数组【0】 + i * 数组类型所占字节
go语言中的数组是基本数据类型,传递方式是值传递,如果想要使用引用传递,则使用地址的方式进行更改。
切片:
1)、切片(slice)是golang中一种特有的数据类型
2)、数组有特定的用处,但是却有一些代办(数组长度不可变),所以在go语言的代码里并不是特别常见。相对的切片确实随处可见的,切片是一种建立在数组类型之上的抽象,他构建在数组之上并且提供了更强大的能力和便捷。
3)、切片(slice)是对数组一个连续片段的饮用,所以切片是一个引用类型。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的冬天窗口
package main import "fmt" func main() { // 定义数组 var intarr [6]int = [6]int{3, 6, 9, 1, 4, 7} // 切片构建在数组之上: // 定义一个切片 名字为slice ,[]动态变化的数组长度不写,int类型 // 切出的一段片段- [1:3] 从 1 开始 到 3 结束(不包含3) var slice []int = intarr[1:3] fmt.Println("intarr", intarr) fmt.Println("slice", slice) fmt.Println("slice的元素个数:", len(slice)) fmt.Println("slice的容量:", cap(slice)) fmt.Println("数组中下标为1位置的地址:%p", &intarr[1]) fmt.Println("切片中下标为0位置的地址:%p", &slice[0]) slice[0] = 16 fmt.Println("intarr", intarr) fmt.Println("slice", slice) }

切片的定义:
1)、方式1: 定义一个切片,然后让切片去引用一个已经创建好的数组
2)、方式2: 通过make内置函数来创建切片。基本语法:var 切片名[type] = make([],len,[cap])
make 在底层创建一个数组,外界是访问不到的,不可以直接对数组进行维护操作。
3)、方式3:定义一个切片,直接就指定具体数组,使用原理类似make的方式
package main import "fmt" func main() { // 定义切片 :三个参数,切片对应类型,切片长度,切片的容量 slice := make([]int, 4, 20) fmt.Println(slice) //[0 0 0 0] fmt.Println("切片的长度", len(slice)) fmt.Println("切片的容量", cap(slice)) slice[0] = 66 slice[1] = 88 fmt.Println(slice) //[66 88 0 0] slice2 := []int{1, 4, 7} fmt.Println(slice2) // [1 4 7] fmt.Println("切片的长度", len(slice2)) fmt.Println("切片的容量", cap(slice2)) }

注意事项:
1)、切片在定义以后不可以直接使用。需要让其引用到一个数组,或者make一个空间供切片来使用
2)、切片使用不能越界
3)、简写方式:
- var slice = arr[0:end] ———> var slice = arr[:end]
- var slice = arr[start:len(arr)] ———> var slice = arr[start:]
- var slice = arr[0:len(arr)] ————→ var slice = arr[:]
4)、切片可以继续切片
5)、切片可以动态增长 使用append slice2 := append(slice,88,50) 。底层追加元素的时候进行数组扩容,老数组扩容为新数组。创建一个新数组,将老数组中的元素复制到新数组,再追加88、50。使用新数组接收。老数组是不变的。往往使用追加的时候是给slice追加 slice := append(slice,88,50)。底层的新数组还是不能直接维护,需要通过切片直接维护。
6)、切片拷贝。
映射
1)、映射(map),go语言中内置的一种类型,它将键值相关联,我们可以通过key来获取对应的value。类似其它语言的集合
2)、var map变量名 map[keytype]valuetype
key、value 的类型 :bool、数字、string、指针、channel、还可以是只包含前面几个类型的接口、结构体、数组。key通常为int、string类型。value 通常为数字(整数、浮点数)string、map、结构体
特点:
1)、使用前一定要make
2)、map的key-value是无序的
3)、key是不可以重复的,如果遇到重复,后一个value会覆盖前一个value
package main import "fmt" func main() { // 方式一 // 定义 map 变量 var a map[int]string // 只声明map 内存是没有分配空间的 // 必须通过make 函数初始化 a = make(map[int]string, 10) a[200095452] = "张三" a[200095123] = "李四" a[202125452] = "王五" a[200095123] = "朱六" fmt.Println(a) // 方式二 b := make(map[int]string) b[200095452] = "张三" b[200095123] = "李四" fmt.Println(b) // 方式三 c := map[int]string{200095452: "张三", 1: "二二"} fmt.Println(c) }
面向对象:
1、golang语言面向对象编程说明:
- golang也支持面向对象编程(oop),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说golang支持面向对象编程特性是比较准确的。
- golang没有类(class),go语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你也可以理解成golang是基于struct来实现oop特性的
- golang面向对象编程非常简洁,去掉了传统oop语言的方法重载,构造函数和析构函数、隐藏的this指针等等。
- golang仍然有面向对象编程的继承、封装和多态的特性,知识实现的方式和其它oop语言不一样,比如继承golang没有extends关键字,继承是通过匿名字段来实现的。
2)、结构体的引入:
package main import "fmt" // 定义 老师的结构体 type Teacher struct { // 变量名字大写,外界可以访问 Name string Age int School string } func main() { // 创建老师结构体的实例、对象、变量 var ma Teacher // fmt.Println(ma) // { 0 } ma.Name = "马" ma.Age = 33 ma.School = "school" fmt.Println(ma) }
第二种创建方式:
func main() { var t Teacher = Teacher{} fmt.Println(t) t.Age = 31 t.Name = "ss" t.School = "aa" fmt.Println(t) }
第三种创建方式:
func main() { var t *Teacher = new(Teacher) // t 是指针 ,指向的就是地址,应该给地址指向的字段进行复制 (*t).Name = "bb" (*t).Age = 43 // 为了符合程序员的编程习惯,go提供了简化的赋值方式 t.School = "school" fmt.Println(*t) }
第四种创建方式:
func main() { var t *Teacher = &Teacher{} t.Age = 33 t.Name = "cc" fmt.Println(t) }
结构体之间的转换:
1)、结构体是用户单独定义的类型,和其它类型进行转换时需要完全相同的字段(名字、个数和类型)
2)、结构体进行type重新定义(相当于取别名),golang认为是新的数据类型,但是互相间可以强转
断言:
1)、什么事断言:
go语言里面有一个语法,可以直接判断是否是该类型的变量:value,ok = elemen.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型
to be continued
- 作者:LiuJixue
- 链接:https://liujixue.cn/article/Golonglearning
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。





