GO
GO
Go语言介绍
Go 即Golang,是Google公司2009年11月正式对外公开的一门编程语言,Go是静态强类型语言,是区别于解析型语言的编译型语言(静态:类型固定 强类型:不同类型不允许直接运算)。
解析型语言:源代码有解析器对代码进行解释执行,类似于python、nodejs、php
编程型语言:源代码编译生成机器语言,然后有机器直接执行机器码即可,类似c \ c++ \ java \ c# \ go
# java
有编译过程,没有直接编译成机器语言,而是字节码,机器不能识别,但java虚拟机:jvm能识别
运行java必须要有jvm
jvm:java虚拟机
jre:java运行时环境
jdk:java开发环境
#python
python是可执行文件,打包python项目或flask项目需要借助pipinstaller模块,详细前往:https://zhuanlan.zhihu.com/p/430490285
如何防止python代码泄露?
- 项目启动后,把源代码删除了,但项目停了就需要重新部署,较繁琐
- 借助pipinstaller,将代码打包成可执行文件
- 做到docker镜像中,运行容器,授予授权码(-e password)
Go语言特性
- 跨平台,是编译性语言
- 语法接近c语言
- 管道(channel),切片(slice),并发(routine)
- 有垃圾回收的机制
- 支持面向对象和面向过程的编程模式
Go语言发展
2009年谷歌公司推出的
2015年8月19日 go1.5 —— 实现的架构变化,同时保留了和旧版本的兼容性,本次更新中移除了”最后残余的C代码”---》以后再写go不用c写了,用go写---》语言的自举
2018年8月24日 go1.11 —— modules和WebAssembly支持 go mod模式 包管理
最新版是 go1.20
Go语言应用
Go语言写的:k8s、docker、七牛( 许式伟,七牛云创始人兼*CEO *)
应用领域
-
服务开发、并发、分布式系统、微服务等方向,自动化运维,区块链
-
内存KV数据库,例如boltDB、levelDB
-
云平台
Go语言发展前景
Go语言将登上语言榜前列,与C和Java并列前三甲
Go语言称霸云计算
Go将统治下一个10年
目前Go语言已经⼴泛应用于人工智能、云计算开发、容器虚拟化、⼤数据开发、数据分析及科学计算、运维开发、爬虫开发、游戏开发等领域。
Go语言简单易学,天生支持并发,完美契合当下高并发的互联网生态
Go开发环境搭建
GO的语言搭建需要go的SDK和go的编辑器
go的SDK---》开发环境----》下载适合电脑go1.20.3.windows-amd64.msi
go的编辑器--》goland(jetbrains系列全家桶),vscode---goland-2023.1.exe
试用30天或破译

第一个helloworld
# 创建项目
# 创建一个go文件
# 写代码
package main // 声明这个go文件是属于哪个包
import "fmt" // 表示导入fmt包,自动导入的,goland自动导入的
func main() {
fmt.Println("helloworld")
}
//解析
package main // 声明这个go文件属于哪个包,main有特殊含义,一个项目要运行必须要有main包,运行的入口是main包下的main函数
// 表示导入fmt包,自动导入的,goland自动导入的,使用了哪个包,编辑器会自动导入
// 包导入,不使用,会报错,编译不过
import "fmt"
// func 定义一个函数 ,main 是函数名,没有参数,没有返回值,整个项目入口
func main() {
// 在控制台输出helloworld
fmt.Println("helloworld") // a... 提示的,意思是传的helloworld实参,给了a
}
// 编译代码---》编译成可执行文件---》默认编译成当前平台可执行文件---》支持跨平台编译---》想编译出linux的---》交叉编译
//编译 go build 文件名----》编译成可执行文件
// 运行(文件名.go),直接运行 双击
// 开发阶段----》编译并运行---》生成可执行文件,不再当前路径,用完就删除
// 编辑器有个快捷运行方式,右键运行
变量命名规范
Go语言中的函数名、变量名、常量名、类型名和包名等所有的命名,都遵循一个简单的命名规则:
- 一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线
- 大写字母和小写字母是不同的:Name和name是两个不同的变量(区分大小写,但是go的大写有特殊含义)
- 关键字和保留字都不建议用作变量名
关键字:25个,编辑器中会变色
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
保留字:37个
内置常量: true false iota nil
内置类型: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
内置函数: make len cap new append copy close delete
complex real imag
panic recover
总结:
- 建议定义变量,函数名使用------》驼峰式命名方法
- go文件命名:建议使用下划线
变量的定义和使用
-
变量的完整定义
// var关键字 变量名 变量类型 = 变量值 var name string = "kimi" fmt.Println(name) // kimi var age int = 19 fmt.Println(age) //19 -
变量定义了,就必须使用,否则报错
var name string = "kimi" //name declared and not used -
类型推导(不写类型)---》自动推导出kiki是字符串类型
var name = "kimi" var age = 19 fmt.Println(name) //kimi fmt.Println(age) //19 -
虽然是类型推导出来的,但是是有类型的,并且后期不能变换类型
变量只要定义了,类型就固定了,如变量name先定义为字符串,后期就能更改为int
而python是动态强类型语言,是后期可以转换类型
-
简略声明,省略类型,必须使用 :=
name := "kimi" fmt.Println(name) //kimi -
一次性声明多个变量(其实就是基于上面三种变量定义方式的变形)
1.完整定义 //var name, age, gender string = "kimi", "18", "女" var ( name string = "kiki" age int = 18 gender string = "女" ) fmt.Println(name, age, gender) // kimi 18 女 2.类型推导 //var name, age, gender = "kimi", 18, "女" var ( name = "kiki" age = 18 gender = "女" ) fmt.Println(name, age, gender) //kimi 18 女 3.简略声明 name, age, hobby := "kimi", 19, "写代码" fmt.Println(name, age, hobby) //kimi 19 写代码 4.完整定义和类型推导混合 var ( name = "kiki" age int = 18 gender string = "女" ) fmt.Println(name, age, gender) // kimi 18 女 -
变量不能重复定义三种定义方式
var name = "kiki" //var name string = "jim" //不能重复声明变量 //name := "haha" //不能重复声明变量 name, age := "jim", 18 //有新的变量是可以这样重复声明变量 fmt.Println(name, age) //jim 18 -
变量可以先定义在赋值,必须使用完整定义(方式1)
var name string //默认值是空字符串 var age int // 默认值是0 //var age //不行 //name := //不行 //age = 19 // 不行 fmt.Println(name,age) -
简略声明用的多
a :=10 -
查看变量类型 ```go
//a := 99 a := "kimi" fmt.Printf("a的值是:%v,类型是:%T", a, a) //a的值是:kimi,类型是:string
Go语言变量类型
数字int
数字类型都表示整数,区别就是数字范围不同,一个字节是八个比特位.
32位机器是int32,64位机器是int64
| 类型 | 数字范围 |
|---|---|
| int8 | -2的7次方,2的7次方-1【-128,127】 |
| int16 | -2的15次方,2的15次方-1 |
| int32 | -2的31次方,2的31次方-1 |
| int64 | -2的63次方,2的63次方-1 |
正整数uint
| 类型 | 数字范围 |
|---|---|
| int8 | 0,2的7次方-1【-128,127】 |
| int16 | 0,2的15次方-1 |
| int32 | 0,2的31次方-1 |
| int64 | 0,2的63次方-1 |
浮点数
表示小数,下面是表示小数范围不一样
| 类型 | 范围 |
|---|---|
| float32 | 单精度浮点数,精度为小数点约7位数字 |
| float64 | 双精度浮点数,精度为小数点约15位数字 |
浮点数可以进行基本的数学运算,如加减乘除,也可进行比较运算,大于小于等于
复数
| 类型 | |
|---|---|
| complex64 | complex64类型表示64位复数,由两个float32类型的实部和虚部组成,占用8个字节 |
| complex128 | complex128类型表示128位复数,由两个float64类型的实部和虚部组成,占用16个字节。 |
rune byte
| 类型 | |
|---|---|
| rune | int32的别名,rune可以写字符串,但是取ASCII码 |
| byte | uint8的别名,byte 只能写ASCII,不能定义字符串 |
string
- 字符串是用双引号包裹的,不能换号,
- 反引号包裹的,可以换行
注意:go也有单引号包裹的,它不是字符串
布尔类型
true
false
常量
关键字const,不可使用简略定义,只能用完整定义和类型推导,常量一旦定义了值就固定了,不允许修改
const name string = "kimi" //完整定义
const name = "kimi" // 类型推导
变量、常量的作用域范围
变量只要不再同一个范围内,是可以再定义的
var name = "kiki"
func main() {
//定义常量
//const name string = "kimi"
name = "kimi" //报错
fmt.Println(name) //kimi
}
iota的使用
- 同时定义多个常量,第二个如果没有赋值,初值就是上一个的值
- iota用法一,后续定义的变量会自增,后续就可以用等于iota,他会自增
- 只要用了iota,后续都会自增1
- iota放在第几行,值就是多少,后续都会基于当前行自增

函数基础
普通函数--无参
func main() {
//1.调用普通函数
test()
}
// 1.定义普通函数,没有参数,没有返回值
func test() {
fmt.Println("我是普通函数")
}
有参函数
有参函数参数有几个就传几个,类型要严格一致且必定要指定参数类型
func main() {
//2.调用有参数的函数(类型必须严格一致,有几个就传几个值,没有关键字传参
test1(11, "kimi")
//3.调用简写的有参函数
test2(11, 12, "kimi")
}
// 2.有参数的函数,必须指定参数类型
func test1(a int, b string) {
fmt.Println(a)
fmt.Println(b)
}
// 3 有参数的函数,多个参数,类型一致可以简写 (go语言想发设法让你少写代码)
func test2(a, b int, c string) {
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}
有参函数带返回值
有参函数的返回值的类型要和参数的类型一致,返回值要用变量来接收
func main() {
//4.调用有返回值的函数
//test3(1, 2) //需要一个变量来接收
//res := test3(1, 2)
//var res = test3(1, 2)
var res int = test3(1, 2)
fmt.Println(res)
// 5 调用多个参数,多个返回值的函数 必须用多个接收,有几个就要用几个变量接收,不能多不能少
//res1, res2, res3 := test4(4, 5)
//fmt.Println(res1, res2, res3) //9 20 成功
// 5.1 就不想要第三个参数,后面不会用第三个参数
res1, res2, _ := test4(4, 5)
fmt.Println(res1, res2) //9 20
fmt.Println(_) //不能但变量用,就是个空白
}
// 4 既有参数,又有返回值的 ,只有一个返回值的,需要指明返回值类型
func test3(a, b int) int {
return a + b
}
// 5 多个参数,多个返回值
func test4(a, b int) (int, int, string) {
return a + b, a * b, "成功"
}
匿名函数
同一个包下,变量和函数只能定义一次.
匿名函数含义
匿名函数:没有函数名,一般定义在其他函数内部
f := func() {
}
func main() {
//1.匿名函数
test()
}
// 1 匿名函数加括号直接调用
func test() {
func() {
fmt.Println("我是内存函数")
}() //不直接调用会报错
}
匿名函数赋值给变量
func main() {
//2.匿名函数赋值给变量
test1()
}
// 2 匿名函数赋值给一个变量--->函数是一种类型---》在go中,函数又称一等公民(可以赋值给变量的都叫一等公民),又叫头等函数,一等函数
func test1() {
//f := func() {
// fmt.Println("我是内存函数")
//}
// f 类型是什么呀?完整定义,写类型
var f func() = func() {
fmt.Println("我是内存函数")
}
fmt.Printf("%T", f) //func()
}
函数调用后 ---返回闭包函数
函数调用,如果有返回值,才能用变量去接收res := test7()
func main() {
// 3 调用函数,返回闭包函数
res := test2() // 没有test2没有返回值是会报错的
fmt.Println(res) //func(int) 0x68de00 内存地址
res()
}
// 3 函数是一等公民,是一种类型---》函数的参数和返回值都是类型的一部分
func test2() int {
//var f func() = func() {
// fmt.Println("我是内层函数")
//}
var f1 func(int) int = func(a int) int {
fmt.Println(a)
return 10
}
var f2 func(int) string = func(c int) string {
fmt.Println("\n")
fmt.Println(c)
return "9"
}
//fmt.Printf("%T", f) //func()
fmt.Printf("%T", f1) //func()
f2(1)
return 8
}
类型重命名、可变长参数、defer
-
函数赋值给变量
函数的参数和返回值都是类型的一部分,函数可以赋值给一个变量
package main import "fmt" // 1 函数的参数和返回值都是类型的一部分,函数可以赋值给一个变量 // test3 函数,接收一个参,参数是函数类型:没有参数没有返回值 // func 有返回值,返回值是个函数:函数有两个参数,一个返回值 func test3(a func()) func(int, int) int { // 函数名--函数参数---返回值类型 a() // return func(x, y int) int { return x + y } } func main() { test3(func() { fmt.Println("被传入的函数") }) } -
类型重命名
可以给类型重复名(函数\类型)
// 如果 type Myint int 相当于我们新定义了一个类型,叫Myint // 如果 type Myint = int 只是重命名,没有新定义类型 type MyFunc func(int, int) int type Myint = int func test3(a func()) func(int, int) int { a() return func(x, y int) int { return x + y }} func main() { var a Myint = 9 var b int = 10 //var b int8 = 10 //类型不同不能相加,go是静态强类型 fmt.Println(a + b) //19 var f MyFunc = test3(func() { fmt.Println("被传入的函数") }) res := f(8, 9) fmt.Println(res) //17 } -
函数可变长参数
// 可以传任意长度的int类型参数 func test4(a ...int) { fmt.Println(a) //[1 23 56 778 5578] fmt.Printf("a的类型是:", a) // 类型是 int类型切片 []int //a的类型是:%!(EXTRA []int=[1 23 56 778 5578]) } func main() { //可变长参数 test4(1, 23, 56, 778, 5578) } -
defer关键字
遇见defer关键字,延迟调用, 当前函数所有代码都执行完了,再执行defer的内容,先注册,后调用 ,先写的defer后来执行.
defer fmt.Printf("今天周三了\n") defer fmt.Printf("明天周四\n") fmt.Printf("今天周三了\n") fmt.Printf("明天周四\n") """ 结果 今天周三了 明天周四 明天周四 今天周三了 """
包的使用
python模块和包
模块就是一个py文件,包是一个文件夹,包下有__init__.py文件
go包
-
包是在文件夹下,这个文件夹下所有go文件的第一行要声明一堆go文件的组合
-
在go中,一个包就是一个作用域. 新建文件夹,写多个go文件,包名必须要一致.
-
go中有个 go path 模式,已经弃用了,它的包导入了,不是从项目下开始导入,而是从 go path 的src路径下开始导入
注意点
-
包内部,大写开头,表示导出 变量 函数 .....
-
包内部的变量函数,只能定义一次
-
包内部的所有东西,在包内部直接使用
-
包下可以根文件夹名不一样,但是一个文件夹下只能有一个包main包
-
导入包,按路径导入,如果不重复名,就是文件夹必须根包名一样
如果文件夹跟包名不一样,可以命名成任意的,但要重命名导入的包名 ,后续使用重命名的包名.
import kiki "GoProjectday03/tools"
可以命名成任意的 import rose "GoProjectday03/tools"
以后使用包名,调用即可 ---tools.Test()
-
包内的init函数,可以定义多次,只要导入包,就会依次执行init函数
func init() {
fmt.Printf("我是第一个init函数") }func init() {
fmt.Printf("我是第二个init函数") } -
导入包,必须使用,不使用就报错
如果只想执行init函数
import _ "GoProjectday03/tools"
-
一个文件夹下可以再建文件夹建新的包,各个文件夹直接没有必然联系,只是文件夹层级关系
-
使用的go mod 模式,从1.11后都是这种模式,项目根路径下会有一个go.mod
//tools.go文件
package tools
import "fmt"
func add(a, b int) int { //小写的函数名是包内使用的函数,可以直接导入
return a + b
}
//导出的函数名要大写
func Test() {
fmt.Printf("我是tools中的函数")
res := add(1, 2)
fmt.Println(res)
}
//GoProjectday03下的s2.go文件
package main
import "GoProjectday03/tools"
func main() {
tools.Test()
}
goSDK内置包---Gin
自定义包和第三方包
gin: 开了一个web服务
gin的使用
-
安装
//配置代理:代理是七牛云 1.局部配置: 直接在配置中的goland中配置 GOPROXY=https://goproxy.cn,direct 2.全局配置: 终端命令:go env ---修改set GOPROXY //下载github的包 go get github.com/gin-gonic/gin -
使用gin
package main import kiki "GoProjectday03/tools" import "github.com/gin-gonic/gin" func main() { kiki.Test() r := gin.Default() //r.LoadHTMLFiles("templates/index.html") //LoadHTMLFiles指定单个页面 r.LoadHTMLGlob("templates/*") //LoadHTMLGlob指定templates下所有的页面 r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "code": "100", "message": "成功22", }) }) //GET请求路由 r.GET("/index", func(c *gin.Context) { c.HTML(200, "index.html", gin.H{"name": "kimi", "age": 18}) }) r.Run() //listen and serve on 127.0.0.1:8080 //运行 } // html页面的展示 <h1>{{.name}}---{{.age}}</h1>
if-else
if-else基本格式
if 条件{
}else if 条件 {
}else {
}
代码展示
package main
import "fmt"
func main() {
score := 96
if score >= 90 {
fmt.Printf("优秀")
} else if score >= 80 && score < 90 {
fmt.Printf("良好")
} else if score >= 70 && score < 80 {
fmt.Println("中等")
} else if score >= 60 && score < 70 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
}
循环
python while for
go 只有for循环
java while\for\do while
//go中的for循环能实现while循环的功能
-
for基本语法, for i 中的 i的作用域范围只在for内部有效
func main() { // 1 基本语法 for关键字 定义变量i=0;i<10;i++{} 三部分都可以省略,但是一般会保留第二部分,第二部分是条件 // 2 循环打印0--9 for i := 0; i < 10; i++ { fmt.Println(i) } //fmt.Println(i) i的作用域范围只在for内部有效 } -
省略第一部分,分号不能少
i := 0 for ; i < 10; i++ { fmt.Println(i) } fmt.Println(i) //10 -
省略第三部分,分号不能少
for i := 0; i < 10; { fmt.Println(i) i++ } } -
省略第一部分和第三部分,分号能省略
i := 0 for i < 10 { fmt.Println(i) i++ } -
for条件 {} while 循环
for true { fmt.Println("1111") } -
死循环
i := 0 for { fmt.Println(i) i++ } -
基于迭代的循环
上面都是基于索引的循环
s := "kimi" for i, v := range s { fmt.Println(i) //0 1 fmt.Println(v) //107 105 fmt.Println(string(v)) //k i } l := "中国" for i := 0; i < len(l); i++ { //fmt.Println(string(l[i])) // k i m i fmt.Println(l[i]) //228 184 173 229 155 189 ACKII中国两个字所占字符对应的位置 //fmt.Println(string(l[i])) //乱码 }
switch
switch是一个条件语句,用于将表达式的值与可能匹配的选项列表进行比较,并根据匹配情况执行响应的代码块,优雅的替换调else-if
-
switch基本使用
// 1 switch 基本使用 score := 78 //条件没有就没有结果,有结果就输对应的结果 switch score { case 90: fmt.Println("我是90") case 80: fmt.Println("我是80") case 70: fmt.Println("我是70") } -
default的使用
// 2.default的使用 score := 99 //条件没有就没有结果,有结果就输对应的结果 switch score { case 90: fmt.Println("我是90") case 80: fmt.Println("我是80") case 70: fmt.Println("我是70") default: fmt.Println("其他分数") } -
多表达式判断
score := 80 switch score { case 90, 91, 92, 93, 94: fmt.Println("我是90,91,92,93,94之一") case 80, 89: fmt.Println("我是80/89") case 70: fmt.Println("我是70") default: fmt.Println("其他分数") } -
无表达式
score := 81 switch { case score > 90: fmt.Println("大于90") case score > 80 && score < 90: fmt.Println("我是80-90") case score >= 60: fmt.Println("大于60") default: fmt.Println("其他分数") } -
Fallthrougth
//默认情况下,每个条件之间完,默认加break,但是也不用加,其他语言要加,其他语言去掉break,会无条件执行下一个case // 要无条件执行下一个case,需要使用fallthrough score := 86 switch { case score > 90: fmt.Println("大于90") case score > 80 && score < 90: fmt.Println("我是80-90") fallthrough case score >= 60: fmt.Println("大于60") fallthrough //无条件执行下一个case default: fmt.Println("其他分数") } }
数组
数组式同一类型元素的集合. 例如 整数集合 5,8,9,79,76 形成一个数组。
go语言中不允许混合不同类型的元素,例如包含字符串和整数的数组,数组式连续存储的,存储同一个类型的数据结构.
数组定义长度就固定了,数组不能扩容,切片才能扩容。
值类型有默认类型,字符串-->"" ,列表--->[],nill
引用类型没有默认值
1. 数组初始化,数组是值类型
数组是值类型,go语言中函数传参是 copy传递,复制一份参数,传入 当参数传递,在函数中修改,不会影响原来的
func main() {
var a [3]int = [3]int{2, 3, 4} //初始化
test(a)
fmt.Println(a) //[2 3 4]
}
func test(a [3]int) {
a[0] = 89
fmt.Println(a, "被调用") //[89 3 4] 被调用
}
2.数组长度
// 2 数组长度
//var a = [3]int{1, 2, 3}
//a := [3]int{1, 2, 3}
a := [...]int{1, 34, 5, 67, 87, 78} //可变长
fmt.Println(a) //[1 34 5 67 87 78]
fmt.Printf("%T\n", a) //[6]int 数组
fmt.Println(len(a)) // 6
3. 循环打印数组
a := [...]int{1, 34, 5, 67, 87, 78}
//基于索引的
//for i := 0; i < len(a); i++ {
// fmt.Println(a[i])
//}
//range循环,基于迭代的
for i, v := range a { // i是索引,v是值
fmt.Println(i)
fmt.Println(v)
}
4.多维数组
//var a [3][2]int //只是定义,没有初始化, [[0 0] [0 0] [0 0]]
var a [3][2]int = [3][2]int{{2, 3}, {2, 5}} //[[2 3] [2 5] [0 0]]
fmt.Println(a)
fmt.Println(a[0])
fmt.Println(a[0][1])
//循环多维数组
for i := 0; i < len(a); i++ {
for j := 0; j < len(a[i]); j++ {
fmt.Println(a[i][j])
}
}
for _, v := range a { //一般我们不拿索引值,只拿索引对应的值
for _, v1 := range v {
fmt.Println(v1)
}
}
5. 数组定义并赋值初值
var a [100]int8 = [100]int8{86: 1, 66: 67}
fmt.Println(a)
切片
slice:切片是由数组建立的一种方便 灵活且功能强大的包装.切片本身不拥有任何数据.他们只能对现有数组的[引用]
1. 切片的定义
//1 切片的定义 中括号中不放任何东西,是切片类型,放个数字,就是数组类型
var a []int //只定义,没有初始化,是引用类型 是nil类型,等同于python中的None
fmt.Printf("%T", a) // []int
fmt.Println(a[1]) // index out of range [1] with length 0
if a == nil {
fmt.Println("我是空数组") //[]int我是空数组
}
2. 切片定义并初始化
// 定义并初始化
var b [10]int //值类型,有默认值[0 0 0 0 0 0 0 0 0 0]
var a []int = b[:] /// 表示a这个切片,对数组b进行引用,引用了从0到最后
fmt.Println(b) //[0 0 0 0 0 0 0 0 0 0]
fmt.Println(a) //[0 0 0 0 0 0 0 0 0 0]
a[8] = 99
fmt.Println(a[8]) //99
fmt.Println(a) //[0 0 0 0 0 0 0 0 99 0
fmt.Println(b) //[0 0 0 0 0 0 0 0 99 0
3.切片的取值赋值
var b [10]int //值类型,有默认值[0 0 0 0 0 0 0 0 0 0]
var a []int = b[:] /// 表示a这个切片,对数组b进行引用,引用了从0到最后
a[8] = 99
fmt.Println(a[8]) //99
4.切片的修改,会影响底层数组,数据的修改也会影响切片
b[0] = 11
fmt.Println(b) //[11 0 0 0 0 0 0 0 99 0
fmt.Println(a) //[11 0 0 0 0 0 0 0 99 0
5.基于数组,获得切片
var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var a []int = b[0:3] // 引用,引用了从0到最后
fmt.Println(a) //[1 2 3]
a[0] = 99
fmt.Println(a) //[99 2 3]
fmt.Println(b) //[99 2 3 4 5 6 7 8 9 10]
var c []int = b[6:9]
fmt.Println(c) //[7 8 9]
c[0] = 1000
fmt.Println(c) //[99 8 9]
fmt.Println(b) //[99 2 3 4 5 6 1000 8 9 10]
b[0] = 88
fmt.Println(b) //[88 2 3 4 5 6 1000 8 9 10]
//切片追加值
fmt.Println(a) //[1 2 3]
a = append(a, 67)
fmt.Println(a) //[1 2 3 67]
fmt.Println(b) //[1 2 3 67 5 6 7 8 9 10]
6.切片的长度和容量
var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
var a []int = b[6:9]
fmt.Println(a) //[7 8 9]
fmt.Println(len(a)) //3
fmt.Println(cap(a)) //4 //容量,能存多少 4 ,不是底层数组的大小,取决于切片切数组的位置
7.切片如果追加值超过了底层数组长度,会自动扩容
var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
var a []int = b[6:9]
a[0] = 100
fmt.Println(b) //[1 2 3 4 5 6 100 8 9 0]
fmt.Println(a) //[100 8 9]
a = append(a, 66)
fmt.Println(a) //[100 8 9 66]
fmt.Println(b) //[1 2 3 4 5 6 100 8 9 66]
fmt.Println(len(a)) //4
fmt.Println(cap(a)) //4
// 超过底层数组,追加值
a = append(a, 88)
fmt.Println(a) // [100 8 9 66 88]
fmt.Println(len(a)) // 5
fmt.Println(cap(a)) // 8 超过容量,翻倍扩容(容量在1024内)
a = append(a, 12, 34, 45, 67)
fmt.Println(a) // [100 8 9 66 88 12 34 45 67]
fmt.Println(len(a)) // 9
fmt.Println(cap(a)) // 16 超过容量,翻倍扩容(容量在1024内)
// 改底层数组
b[9] = 999
fmt.Println(b) //[1 2 3 4 5 6 100 8 9 999]
fmt.Println(a) //[100 8 9 66 88 12 34 45 67]

8.切片定义并初始化,使用数组初始化
// 切片定义并初始化 使用数组初始化
//使用make初始化
//var a []int // 是nil ,没有初始化
//var a []int = make([]int, 3, 4) // 要初始化 ,长度为3,容量为4
//fmt.Println(a)
//fmt.Println(len(a)) //3
//fmt.Println(cap(a)) //4
var a []int = make([]int, 0, 4) // 要初始化 ,长度为0,容量为4
fmt.Println(a) //[] 有可能是nil,有可能已经初始化完成了,只有初始化完成了才能用
fmt.Println(len(a)) //0
fmt.Println(cap(a)) //4
a = append(a, 5)
fmt.Println(a) //[5]
fmt.Println(len(a)) //1
fmt.Println(cap(a)) //4
9.切片的参数传递是引用类型,函数中修改值会影响原来的
函数中修改和新增会改变原来的数组值,但是a的长度只有3,所以取出来就是[88 99 0]
var a []int = make([]int, 3, 5)
a[1] = 99 ////[0 99 0]
test(a)
fmt.Println(a) //[88 99 0]
}
func test(a []int) {
a[0] = 88
fmt.Println(a) //[88 99 0]
a = append(a, 33)
fmt.Println(a) //[88 99 0 33] //超过长度会翻倍扩容
fmt.Println(cap(a)) //5
}
10.多维切片,切片定义并初始化
//var a []int = []int{2, 3, 4, 45}
//fmt.Println(a) //[2 3 4 45]
//fmt.Println(len(a)) //4
//fmt.Println(cap(a)) //4
//a = append(a, 55)
//fmt.Println(len(a)) //5
//fmt.Println(cap(a)) //8 扩容翻倍
var a [][]string = [][]string{{"1", "3"}, {"0"}, {"4", "6", "34"}}
fmt.Println(a) //[[1 3] [0] [4 6 34]]
var b [][]string = make([][]string, 3, 3)
fmt.Println(b[2]) //[]
可变长参数
func main() {
test("1", "kimi")
var a []string = []string{"skh", "nihao"}
test(a...) //相对于打散了传入
}
func test(a ...string) {
fmt.Println(a) //[1 kimi]
fmt.Printf("%T", a) //[]string
fmt.Println(cap(a)) //2 可变长的容器就是放入的数组个数
}
maps
对于go来说 : key必须是值类型
对于panpython的key必须是不可变类型
以后所有的引用类型,都需要初始化才能用,值类型不需要,有默认值
func main() {
//1 key -value 形式存储 定义一个map
//var userInfo map[int]string // 没有初始化,它是nil,但是打印出来不是nil
//fmt.Println(userInfo) //map[]
//if userInfo == nil {
// fmt.Println("asdfasdf")
//}
// 2 map 初始化 方式一
//var userInfo map[int]string = map[int]string{1: "kimi", 3: "rose"}
//fmt.Println(userInfo) //map[1:kimi 3:rose]
//if userInfo == nil {
// fmt.Println("asdfasdf")
//}
// 2 map 初始化 方式二 make初始化
//var userInfo map[int]string = make(map[int]string)
//fmt.Println(userInfo) //map[]
//if userInfo == nil {
// fmt.Println("asdfasdf") //不走
//}
// 3 初始化后才能取值赋值
//var userInfo map[int]string
//var userInfo map[int]string = make(map[int]string)
//fmt.Println(userInfo[1]) //报错
//userInfo[1] = "kiki"
//fmt.Println(userInfo) //map[1:kiki]
// 以后所有的引用类型,都需要初始化才能用,值类型不需要,有默认值
//4 取值赋值
//var userInfo map[string]string = make(map[string]string)
//userInfo["age"] = "19"
//userInfo["name"] = "kimi"
//fmt.Println(userInfo) //map[age:19 name:kimi]
//
//fmt.Println(userInfo["age"]) //19
//// 取不存在的----》显示value值的默认值
//fmt.Println("--", userInfo["hobby"]) //空字符串
//
//// 如何判断一个key在不在map中 按key取值,能返回一个布尔值,根据布尔值判断
//v, ok := userInfo["name"]
//fmt.Println(v) //kimi
//fmt.Println(ok) //true就存在,false就不存在
//
//if v, ok := userInfo["name"]; ok {
// fmt.Println(v) //kimi
//}
// 5 删除map元素
//var userInfo map[string]string = make(map[string]string)
//userInfo["age"] = "19"
//userInfo["name"] = "kimi"
//delete(userInfo, "name")
//fmt.Println(userInfo) //map[age:19 name:kimi]
// 6 map的长度
//var userInfo map[string]string = make(map[string]string)
//fmt.Println(len(userInfo)) //0
//userInfo["age"] = "19"
//userInfo["name"] = "kimi"
//fmt.Println(len(userInfo)) //2
// 7 引用类型
var userInfo map[string]string = make(map[string]string)
fmt.Println(len(userInfo))
userInfo["age"] = "19"
userInfo["name"] = "kimi"
test6(userInfo) //map[age:19 name:rose]
fmt.Println(userInfo) //map[age:19 name:rose]
}
func test6(u map[string]string) {
u["name"] = "rose"
fmt.Println(u) //0
}

浙公网安备 33010602011771号