Golang 入门基础知识
快速入门 go 语言笔记,参考了各种大佬的 blog 。
一、下载安装
国内站点 :https://golang.google.cn
IDE : Goland
(2020.12月以后破解有些困难 见 zhile.io, 无法试用的话可以搜一个注册码,失效的也可以,验证成功后断网进入软件,再使用 zhile.io)
自行配置 go sdk ,go root 、go path新建项目, 创建 main.go
package mainimport "fmt"func main() {fmt.Println("hello world")
}
注:
右键新建文件会遇到如下错误 :
https://blog.csdn.net/my_miuye/article/details/103706162
执行命令 :go run XXX.go
编译命令 : go build XXX.go
形成可执行文件 exe
二、基本语法
1、go 风格 :
一个问题只有一个解决方案
例如 : 逐行编译(末尾自动加分号)、 严格格式化 gofmt -w XXX.go
(帮助格式化代码)…
建议每行不要超过 80 个字符。
2、文档:
https://golang.google.cn/pkg/
3、 数据类型 :
(以下开始和 java 产生严重分歧)
1)基本数据类型 : 数值型 、 非数值型
数值型 : 整数数值型 (int、int8、int16…uint、uint8…byte)、 浮点数值型(float32 、 float64)
非数值型 : 字符型 (用 byte 保存单个字母)、布尔(bool)、字符串(string !!区别于java)
2)复杂数据类型 : (派生数据类型)
指针 (Pointer)
数组
结构体(struct)
管道 (Channel)
函数
切片(slice)
接口(interface)
map
注 : 如何查看一个变量的数据类型 ?
var unknow = 123// 查看类型 %Tfmt.Printf("类型 : %T", unknow)fmt.Println()// 查看字节 %dfmt.Printf("字节 :%d",unsafe.Sizeof(unknow))
go 在保证程序正常运行的前提下,应使用尽可能小空间变量类型
3、浮点数
跟 java 一样,分为单精度、双精度, 支持 e 表示科学计数法 , 也会损失精度。
因为 , 存储方式是将小数拆分成 “符号位 + 指数位 + 尾数位” (即科学计数法)存储在计算机中
golang 小数默认类型 float64, 开发中推荐使用 float64
(与 java 相同, java 默认 double)
4、字符如何保存?
golang 没有 char 为字符单独设计的类型,所以, 字符用 byte 保存。
golang 的字符串用固定长度的字符连接起来, 即用固定长度的 byte 序列表示 string。
golang 的字符串也是不可变数据类型。
var str string = "这是一句话 , 天生 utf-8 ,没有乱码 \n"fmt.Println(str)// 对于特殊格式字符串 , 反引号输出带格式 stringvar content string = `这里可以带任何格式 :换行!`fmt.Println(content)
注 : 字符串拼接 + 放在前一行末尾
4、基本数据类型的默认值
数值型均为 0 , bool 默认 false , 字符串 “”
5、基本数据类型之间的转换
必须强制类型转换, 不像 java 一样可以自动转型!!(低精度到高精度也不可以)
语法 :
//数据类型强制转换var convert float64 = float64(unknow)fmt.Printf("强制转换 %T -> %T ", unknow, convert)
注 : 变量 + 数字, 此时数字按默认值 即 int64 跟随OS位数 。 二次赋值可能报错。
6、基本数据类型 与 string 转换
fmt.Sprintf("", XX)
go 中只能通过 strconv 包中的函数进行转换!
var t string = strconv.FormatFloat(float64(price),'f',1,64)// 方法2fmt.Printf("%T : %s \n", t, t)// string 转基本数据类型var string2Float, _ = strconv.ParseFloat("12.23445", 64)fmt.Printf("%T : %f \n", string2Float, string2Float)
注:
Parse 返回两个值,下划线表示忽略其中一个返回值。
bit 位对应接收变量的 bit 位 。
其他类型同理, 需要查看官方文档。
string 转基本数据类型,如果不能转换, 会直接转为该基本类型的默认值。
7、指针
指针变量已经指向了一个内存空间,但存放的是后面变量的地址。
指针变量同样有自己的地址
var pointer *int8 = &i // 变量i 的内存地址fmt.Println(pointer)
如何取出指针指向的变量的值? 利用 *
8、值类型 与 引用数据类型
(值类型 “一般” 是在栈内存直接分配空间 , 引用类型 通常 在堆也会 gc, 与 java 相同, 但 string 是个例外)
值类型: 基本数据类型 、 数组、结构体
引用数据类型: 指针、切片、管道等
代码
package mainimport ("fmt""strconv""unsafe"
)func main() {// hello worldfmt.Println("hello world")// 有符号整数var i int8 = 100fmt.Println("i : ", i)// 无符号整数var j uint16 = 256fmt.Println("j : ", j)// 程序员需要根据 数据范围 对变量进行限制// 注意 1 : byte ~ uint8// 注意 2 : int 不限定位数,默认与系统位数相同 (多为 64 位)var k byte = 255 // 256 会 overflowfmt.Println("k : ", k)var unknow = 123// 查看类型 %Tfmt.Printf("类型 : %T \n", unknow)// 查看字节 %dfmt.Printf("字节 :%d \n",unsafe.Sizeof(unknow))// 浮点型var price float32 = 12.67fmt.Println("price : ", price)// 字符与字符串var char1 byte = 'a'fmt.Println(char1)var char2 byte = '3'fmt.Println(char2)fmt.Printf("char1 = %c , char2 = %c \n", char1, char2)// 溢出情况 : 用范围更大的数值变量,如intvar char3 int = '李'// 字符常量用单引号表示fmt.Printf("%c", char3)var str string = "这是一句话 , 天生 utf-8 ,没有乱码!"fmt.Println(str)// 对于特殊格式字符串 , 反引号输出带格式 stringvar content string = `这里可以带任何格式 :换行!`fmt.Println(content)//数据类型强制转换var convert float64 = float64(unknow)fmt.Printf("强制转换 %T -> %T \n", unknow, convert)// 基本数据类型转 stringvar float2String string = fmt.Sprintf("%f", price) // 方法1fmt.Printf("%T \n", float2String)var t string = strconv.FormatFloat(float64(price),'f',1,64)// 方法2fmt.Printf("%T : %s \n", t, t)// string 转基本数据类型var string2Float, _ = strconv.ParseFloat("12.23445", 64)fmt.Printf("%T : %f \n", string2Float, string2Float)// 指针var pointer *int8 = &i // 变量i 的内存地址fmt.Println(pointer)fmt.Println(*pointer)// 取出指针变量指向的地址的值fmt.Println()}
三 、算术运算符
基本与 java 相同
1、 自增,自减不能参与赋值运算。 只能作为独立运算
2、取模 本质 a % b = a - ( a / b ) * b
3、自增,自减 只能写在变量后面 , a++
4、运算符的优先级
单目运算、赋值运算符 从右到左 , 如 !、 sizeof ;
其余都从左到右!
注 :
,
也是运算符, 优先级最低;
&
返回变量的地址, 放在变量前 &a
给出实际地址;
*
表示指针变量
5、不支持 三目运算符 , 与 go 的单一设计理念相违背
6、:=
是声明并赋值,并且系统自动推断类型,不需要var关键字
四、 获取键盘输入
fmt.Scanln() 行输入
与 java 不同,是可以直接通过地址赋值给变量
// 键盘输入var name stringfmt.Println("请输入姓名:")fmt.Scanln(&name)fmt.Println("姓名:", name)
也可以通过占位符 配合 Scanf 作为格式化输入
占位符号 参考 :
https://www.cnblogs.com/junneyang/p/6069248.html
五、流程控制
1、 分支控制
常规 if else , 但是 golang 支持在 if 条件中定义一个变量,并且只在条件语句作用域中起作用。
if age := 20; age > 18{fmt.Println("你的年龄是 :",age)}
注 :
即使只有一条语句也不能省略括号。
也有 switch , 注意不需要 break !case 后面只要是一个表达式, 只要有返回值即可。
switch key{case XX, XX, XX... :...........default .....
}
switch 也可以不带表达式, 当 if else 使用
注意:
fallthrough 关键字 , 表示 switch 穿透 , 走到这个关键字跳入下一个 case 条件。
type switch ,等学过 interface 后补充 ,x interface x.(type)
。
2、循环控制
第一种 ,for 常规三条件使用。第二种是单一条件, 第三种无条件即死循环。
最后两种使用方式并不习惯 , 结合 for 内部使用 if 实现代替 while。
s := "abcdefg"for i := 0; i < len(s); i++ {fmt.Printf("%c", s[i]) }
注: 此方式遍历 按照字节逐个遍历,汉字 utf - 8 对应 3 个字节,会出现乱码!需要使用 []rune
转换 string。 newS = []rune(s)
for-range 遍历不会出现问题,默认按照字符的方式遍历 。 但是 index 会出现不连续!
// for ranges = "我是谁"for index, val := range s {fmt.Printf("index=%d , val=%c \n", index, val)}
output :
index=0 , val=我
index=3 , val=是
index=6 , val=谁
注 : TMD !golang 中没有 while 和 do while 。
3、 break 与 continue
golang 可以指定跳出位置,配合 label 使用。
// break 默认跳出最近循环, golang 通过 label 指定跳出位置labelName :for i := 0; i < 5; i++{for j := 0; j < 10; j++{fmt.Printf("i = %d :: j = %d \n", i, j)if (i == 3 && j == 7) {//breakbreak labelName}}}
注 : continue 同理也可以配合标签
4、goto
golang 支持 goto , 还是不推荐使用,造成流程混乱。
一般 配合 if 和 label 使用 。
六、函数 和 包
1、golang 中函数和方法是有区别的
2、函数与变量的作用域(是否包外可见)是通过名称首字母大小写来确定的,大写即包外可见
3、值传递与引用传递 : 与 java 类似基本数据类型都是值传递, 即复制一份值到栈空间, 然后改变栈空间的值。但是!!! go 中的数组是引用传递,与 java 不同!因为 go中有指针,如果希望函数内修改函数外部值,可以传递指针变量。不需要像 java 一样包成引用数据类型。
4、为了符合 go 的设计初衷, 一种事物有一种解。go 是不支持函数重载的。(go 有可变参数,所以可以达到调用相同函数,传递不同参数)
5、go 中函数也是一种数据类型,可以赋值给一个变量
6、函数本身可以作为形参,传递给其他函数 func myfun(param func(int,int) int, p1 int, p2 int) {...}
7、自定义数据类型关键字 type , 像对原有的数据类型起别名(相当于,但不是等同于同一个数据类型), type myTypeName string
可以创建 myTypeName 类型的 string 变量,声明在调用之前。
.......// 函数sv := sumfmt.Println(sv(10,5))
.......func sum(a int, b int) int {return a + b
}
8、很方便区别, go 支持对返回值命名
func sumAndSub(a int, b int) (sum int, sub int) {sum = a + bsub = a - breturn //对返回值赋值后,不需要返回sum sub
}
// 调用
.....p1 := 13p2 := 11res1, res2 := sumAndSub(p1, p2)fmt.Printf("变量1 : %d, 变量2 : %d , sum : %v, sub : %v" , p1, p2, res1, res2)
......
注 : 下划线 占位符可以忽略返回值, day1 提到过。
9、可变参数 参数名后加 ...
func testFunc(p1 int, args... int) int {....}
表示 1 到多个参数
注 :
args 是 slice 切片, 理解为可变数组。
可变参数需要放在形参列表最后。
代码
package mainimport "fmt"func main() {// ++ --var a int8 = 56// var b int8 = a-- // golang 禁止a--var b int8 = a - 1fmt.Printf(" b : %d \n", b)// 键盘输入var name stringfmt.Println("请输入姓名:")fmt.Scanln(&name)fmt.Println("姓名:", name)// 流程控制if age := 20; age > 18{fmt.Println("你的年龄是 :",age)}for i:= 0; i < 10; i++ {fmt.Printf("第 %d 次循环 \n", i )}s := "abcdefg"for i := 0; i < len(s); i++ {fmt.Printf("%c", s[i]) // 此方式遍历 按照字节逐个遍历,汉字 utf - 8 对应 3 个字节会出现乱码}fmt.Println()// for ranges = "我是谁"for index, val := range s {fmt.Printf("index=%d , val=%c \n", index, val)}// break 默认跳出最近循环, golang 通过 label 指定跳出位置labelName :for i := 0; i < 5; i++{for j := 0; j < 10; j++{fmt.Printf("i = %d :: j = %d \n", i, j)if (i == 3 && j == 7) {//breakbreak labelName}}}// 函数sv := sumfmt.Println(sv(10,5))p1 := 13p2 := 11res1, res2 := sumAndSub(p1, p2)fmt.Printf("变量1 : %d, 变量2 : %d , sum : %v, sub : %v" , p1, p2, res1, res2)}func sum(a int, b int) int {return a + b
}func sumAndSub(a int, b int) (sum int, sub int) {sum = a + bsub = a - breturn //对返回值赋值后,不需要返回sum sub
}
init 函数 和 go 文件执行顺序
1、全局变量初始化最先执行
2、init 函数 : 在 main 之前执行, 一般用于完成对只声明未初始化的变量的初始化工作。init 函数在引包的时候就已经被执行了了,即 被引入文件的 init 先于 引入文件的 init 函数。
3、main 函数 : 程序主入口
匿名函数
p1 := 100p2 := 20// 匿名函数myFun1 := func(a int, b int) int{return a * b} (p1, p2)// 调用位置fmt.Println(myFun1)myFun2 := func(a int, b int) int{return a / b}fmt.Println(myFun2(p1, p2))
注 : myFun1 、 myFun2 是函数类型变量
全局匿名函数
var (// 全局匿名函数Sum = func (a int,b int) int {return a + b}
)
注 : 匿名函数结尾括号 相当于调用这个函数的传参
。。。// 2.结果管道resultChan := make(chan *Result, 128)// 3.创建工作池createPool(64, jobChan, resultChan)// 4.开个打印的协程go func(resultChan chan *Result) {// 遍历结果管道打印for result := range resultChan {fmt.Printf("job id:%v randnum:%v result:%d\n", result.job.Id,result.job.RandNum, result.sum)}}(resultChan)
。。。
闭包
闭包不太好理解,函数式编程中,如果一个函数引用了外部变量,那么他们构成了一个整体。如果,操作闭包对象的方法改变闭包对象关联属性,相当于操作了同一个对象。
// 累加器
func AddUpper() func(int) int{// 下面的代码相当于构成了 java 中的一个 classvar base = 5return func(x int) int {base += x // 因为函数引用了函数外的变量 , 跟 base 一起形成了闭包return base}
}// 调用
// 闭包add := AddUpper() // 函数调用,此时 add 为累加器的返回函数 (如果不带形参列表,则函数主题作为变量,)fmt.Println(add(1))// 6fmt.Println(add(1))// 7 由于闭包所以构成了一个整体,相当于对于同一个对象的方法进行二次调用fmt.Println(add(1))// 8 闭包的概念相当于 java 中的 class ,构成了一个实例对象, 此时的 add 相当于对于累加方法的三次调用fmt.Println()add2 := AddUpper()// 相当于 java 中 new 了一个新对象fmt.Println(add2(1))// 6
闭包的好处 : 保存以前使用过的变量
defer
遇到defer 语句先不执行 , defer 存在于单独的 defer 栈中 。
函数执行完毕后将 defer 语句出栈。
用于函数的收尾工作, 例如函数结束时需要释放连接。
func testDefer(p1 int, p2 int) int {defer fmt.Println("defer1 p1: ", p1)// step 3defer fmt.Println("defer2 p2: ", p2)// step 2sum := p1 + p2fmt.Println("sum :", sum) // step 1return sum
}
....
// 调用fmt.Println("call test defer : ",testDefer(14, 27)) // step 4
....
注 :
如果 defer 语句涉及到 变量值的话, 入 defer 栈的时也将"变量当时的值"拷贝一份入栈。
最终 defer 语句出栈时,还原的是defer 语句入栈时的变量值。
变量作用域补充
1、只要在函数外面定义的变量就是全局变量, 作用域整个包有效
2、如果全局变量首字母大写, 那么该变量整个程序有效 (其他包通过包名调用)
常用内置函数 (转)
常用内置函数
close: 用于发送方关闭chan,仅适用于双向或发送通道。
len、cap: 用于获取数组、Slice、map、string、chan类型数据的长度或数量,len返回长度、cap返回容量;
new、make: new用于值类型、用户定义类型的内存分配,new(T)将分配T类型零值返回其指向T类型的指针;make用于引用类型(Slice、map、chan)内存分配返回初始化的值,不同类型使用有所区别。make(chan int) 创建无缓冲区的通道
make(chan int,10) 创建缓冲区为10的通道
make([]int,1,5) 创建长度为1,容量为5的slice
make([]int,5) 创建容量长度都为5的slice
make(map[int] int) 创建不指定容量的map
make(map[int] int,2) 创建容量为2的map
copy、apped: 用于复制slice与为slice追加元素;
print、println: 用于打印输出;
panic、recover: 用于错误处理;
delete: 用于删除map中的指定key
代码
package mainimport "fmt"var (// 全局匿名函数Sum = func (a int,b int) int {return a + b}
)func main() {p1 := 100p2 := 20// 匿名函数myFun1 := func(a int, b int) int{return a * b} (p1, p2)// 调用位置fmt.Println("乘 : " , myFun1)myFun2 := func(a int, b int) int{return a / b}fmt.Println("除 :" , myFun2(p1, p2))// 调用全局匿名函数fmt.Println("加 :" , Sum(p1, p2))// 闭包add := AddUpper() // 函数调用,此时 add 为累加器的返回函数 (如果不带形参列表,则函数主题作为变量,)fmt.Println(add(1))// 6fmt.Println(add(1))// 7 由于闭包所以构成了一个整体,相当于对于同一个对象的方法进行二次调用fmt.Println(add(1))// 8 闭包的概念相当于 java 中的 class ,构成了一个实例对象, 此时的 add 相当于对于累加方法的三次调用fmt.Println()add2 := AddUpper()// 相当于 java 中 new 了一个新对象fmt.Println(add2(1))// 6// defer 存在于单独的 defer 栈中 , 函数执行完毕后将 defer 语句出栈, 用于函数的收尾工作fmt.Println("call test defer : ",testDefer(14, 27)) // step 4}
// 累加器
func AddUpper() func(int) int{// 下面的代码相当于构成了 java 中的一个 classvar base = 5return func(x int) int {base += x // 因为函数引用了函数外的变量 , 跟 base 一起形成了闭包return base}
}func testDefer(p1 int, p2 int) int {defer fmt.Println("defer1 p1: ", p1)// step 3defer fmt.Println("defer2 p2: ", p2)// step 2sum := p1 + p2fmt.Println("sum :", sum) // step 1return sum
}
七、 错误
golang 中错误处理比较繁琐,没有 try catch 和 throw 。 经常需要逐层判断 err 是否为 nil 特别啰嗦,并且需要显示传递 err 。
一般成熟的工程对 err 都有统一的封装方式、代码风格和 recover 机制,这里不赘述 。
panic :异常, 程序直接会挂掉,需代码 recover。
error :错误, 不会影响程序的运行。release 程序需要有将 panic 转成 err 的机制。
区别 java 名称, java 异常 exception 是可以 try catch 的;错误 error 一般都不可恢复 (死锁、爆栈)。
注意 : golang 中的 error 一般放在返回值的最后一个参数。
八、容器
数组
这数组的定义可真奇怪。
var name [len] type
名称 长度 类型
注 :形参是 parameter,实参是 argument
go 中, 数组在参数传递中是 形参传递, 函数运行时会复制数组的值, 值传递。
(区别 java!)
func main() {// 随机生成三个数 ,放入数组, 并反转打印var arr [3] intrand.Seed(time.Now().UnixNano()) // 当前纳秒值作为随机数种子for i := 0; i < len(arr); i++ {arr[i] = rand.Intn(50) // n 为 [ 0, n) 随机范围}fmt.Println(arr)// 换位for i := 0; i < len(arr)>>1; i++ {swap(&arr, i, len(arr) - 1 - i)}fmt.Println(arr)
}
func swap(arr *[3]int, a int, b int) {t := (*arr)[a] // 指针取用数组实际值(*arr)[a] = (*arr)[b](*arr)[b] = t
}
注意 :
[]int 与 [n]int 是不同类型,不限制数组长度则为切片类型!
切片 slice
**切片是数组的一个引用, 遵循引用传递。**使用方式与数组一致, 但是长度是可变的,可以简单理解为一个动态数组。
1、数组得到切片
myslice := arr2[1:4]fmt.Println("元素 = ", myslice)fmt.Println("容量 = ", cap(myslice))
注意 : 算头不算尾 。 arr[:]
从头切到尾。
2、切片的本质
一个结构体(stuct), 包含三个部分 : 指向被引用数组第一个元素地址, 切片长度, 容量。指向被切位置的同一个地址,而不是拷贝一份。slice 本身还有一个地址,指向上面的结构体。
注: 切片的容量 cap
切片的容量是可变一般是元素个数的两倍
3、make 创建切片
var myslice2 []int = make([]int, 4, 5)myslice2[1] = 123//myslice2[4] = 46 // 越界! cap 有啥用呢 ?fmt.Println(myslice2)
注 : 程序员不可见,但 make 底层会创建一个数组, 由 slice 维护 创建引用。
4、创建时赋值
var myslice3 []int = []int{6,7,8}fmt.Println(myslice3)
5、 切片还可以再次切片!
6、扩容函数 append
var myslice2 []int = make([]int, 4, 5)myslice2[1] = 123//myslice2[4] = 46 // 越界! cap 有啥用呢 ?myslice2 = append(myslice2, 46) //超越长度 [0 123 0 0 46]myslice2 = append(myslice2, 47) //超越长度 [0 123 0 0 46]fmt.Println(cap(myslice2))// 容量;10 追加超越长度后,容量自动扩容 2 倍myslice2[4] = 43 // 扩容后切片维护的数组长度发生变化,可以进行正常赋值fmt.Println(myslice2) // [0 123 0 0 43 47]
注: 切片也可以追加切片。
数组都是底层维护,程序员不可见。
7、 切片的拷贝问题copy
// 切片的拷贝问题var myslice4 = make([]int, 2, 2)copy(myslice4, myslice)fmt.Println(myslice4) // 由多到少的拷贝会被截断
8、string 与切片
string 底层是 byte[] 也可以进行 切片操作。
map 集合
1、简介
kv 数据结构 , 通常 key 是 string 或 int。
var mapName map[k-type]v-type
注 : map 声明是不会分配内存的,需要 make 分配内存! (数组声明完分配内存)
var myMap map[string]stringmyMap = make(map[string]string, 5)myMap["姓名"] = "alex"myMap["年龄"] = "26"fmt.Println(myMap)
注意 : golang 中的 key 是完全无序的!
2、 重置 map
golang 没有清空 map 的接口 , 用 myMap = make(map[string]string)// 给一个空 map,原来的内存变为垃圾被 gc
清空 map。
判断值是否存在 :
// 判断值是否存在val,ok := myMap["姓名"]fmt.Println(ok , val)
3、遍历
只能用 for-range
//遍历for key,val := range myMap {fmt.Println(key, "::", val)}
4、map 切片
指的是 slice 的数据类型是 map 。
5、 排序 map
将 key 放入切片 的, 对切片排序 (利用 sort 包
) 。
代码
package mainimport ("fmt""math/rand""time"
)func main() {// 随机生成三个数 ,放入数组, 并反转打印var arr [3] intvar arr2 = [...]int{1,2,3,4,5} // ... 代替数组的长度// for range 遍历for _,value := range arr2{fmt.Println(value)}rand.Seed(time.Now().UnixNano()) // 当前纳秒值作为随机数种子for i := 0; i < len(arr); i++ {arr[i] = rand.Intn(50) // n 为 [ 0, n) 随机范围}fmt.Println(arr)// 换位 与 遍历for i := 0; i < len(arr)>>1; i++ {swap(&arr, i, len(arr) - 1 - i)}fmt.Println(arr)// 切片 : 动态数组// 定义与初始化myslice := arr2[1:4]fmt.Println("元素 = ", myslice)fmt.Println("容量 = ", cap(myslice))var myslice2 []int = make([]int, 4, 5)myslice2[1] = 123//myslice2[4] = 46 // 越界! cap 有啥用呢 ?myslice2 = append(myslice2, 46) //超越长度 [0 123 0 0 46]myslice2 = append(myslice2, 47) //超越长度 [0 123 0 0 46]fmt.Println(cap(myslice2))// 容量;10 追加超越长度后,容量自动扩容 2 倍myslice2[4] = 43 // 扩容后切片维护的数组长度发生变化,可以进行正常赋值fmt.Println(myslice2) // [0 123 0 0 43 47]var myslice3 []int = []int{6,7,8}fmt.Println(myslice3)// 切片的拷贝问题var myslice4 = make([]int, 2, 2)copy(myslice4, myslice)fmt.Println(myslice4) // 由多到少的拷贝会被截断// map 集合var myMap map[string]stringmyMap = make(map[string]string, 5)myMap["姓名"] = "alex"myMap["年龄"] = "26"fmt.Println(myMap)// curddelete(myMap, "年龄")fmt.Println(myMap)// myMap = make(map[string]string)// 给一个空 map,原来的内存变为垃圾被 gc// 判断值是否存在val,ok := myMap["姓名"]fmt.Println(ok , val)myMap["年龄"] = "26"myMap["身高"] = "180"//遍历for key,val := range myMap {fmt.Println(key, "::", val)}}
func swap(arr *[3]int, a int, b int) {t := (*arr)[a] // 指针取用数组实际值(*arr)[a] = (*arr)[b](*arr)[b] = t
}
go 中对面向对象编程 与 传统面向对象编程有很大区别, 因为没有类的概念, 需要使用结构体代替。
注 : 面向对象是一种思想, go 实现了面向对象功能。
九、 结构体 struct
1、 定义
type name struct {fieldName type....
}
注 : 结构体在 go 中是值传递! name 直接指向内存空间。如果field 中有 slice 或 map 需要先 make 再使用结构体。
type Mystruct struct {name stringmySlice []float64ptr *intmyMap map[string]string
} // 未分配空间的引用类型是 nil , 无法赋值
...
var v1 MyStruct
v1.mySlice = make([]float64, 5)
v1.mySlice[0] = 5.5
2、 结构体是值类型,这里与 java 不同!
如果 struct1 := struct2 , 那么stuct1 和 struct2 不指向同一地址。(其实是一个copy操作) 如果想像 java 一样对对象进行引用传递, 需要借助指针。
var p *Person = &Person{XX,"XX"} // 创建时为结构体赋初始值
(*p).XXX = "XX" // 标注使用方式,等效于直接 点属性
3、结构体属性的地址是连续的
4、tag
给结构体的字段 加一个 tag , 序列化 json 的时候,给字段别名。
type Mystruct struct {Name string `json:"name"`
}
十、 方法
自定义类型都有方法,方法与类型绑定。 (type 创建的自定义类型,不只局限于结构体)
1、方法与函数的区别
函数没有绑定数据类型, 方法需要指定绑定的数据类型 。
type Person struct {Name string
}func (p Person) hello() {fmt.Printf("my name is %s", p.Name)
}func main() {var man *Person = &Person{"Alex"}man.hello()
}
注意 :
go 中自定义类型很奇怪, 可以对基本类型进行自定义。
谁调用方法 ,调用者被当作实参传递给方法。
2、String() 方法实现
如果一个类型,实现了 String() 方法, fmt.Println 会调用 String() ,类比 java 的
注 : 结构体创建方式要改变
// 重写了 Person 的 Stringvar man *Person = &Person{"Alex"}man.hello()fmt.Println(man)fmt.Println(&man) // man 是指针变量赋初始值, & 还显示地址var woman Personwoman.Name = "fei"fmt.Println(woman)fmt.Println(&woman) // woman 的 & 显示的事 String() ????
十一、面向对象
嵌套结构体(继承)
继承的目的是为了归类和基本的代码复用, 虽然说 java 的继承在维护复杂度较高的工程时,让人头大,但是有总比没有好。
golang 通过组合实现继承,其实就是子类的属性包含父类的引用或者父类 struct。(这里的父类和子类就是被继承者和继承者)
1 、不管首字母大小写, 嵌套结构体可以使用继承的全部方法和属性
type blackMan struct {Person // 继承skill string
}
2、就近原则,先找自身方法和属性,(实现继承的 overwrite)
3、多继承,(多嵌套几个匿名结构体)如果发生重名问题,调用需要指定匿名结构体名称
接口 interface (多态)
golang 的核心就是面向接口编程。 我理解 golang 的设计思想,一种方式只有一种结果,本身是与多态相违背的。如果想要使用多态特性要依赖接口实现。空接口 可以表示任意类型,go 中可以传空接口。但是空接口 interface 的传递会引发一些列调试的问题, 以及难以预料的 panic,这些都需要在代码编写的时候做考虑 。
注 :
golang 的空接口不存值。 golang 中空接口的传递,其实发生了类型转换。多态即编译时不能确定接口类型,运行时决定调用哪个实现方法。
类型断言 :
v := i.(T) // 会panic
// 这种方式不会引发 panic
v,ok := i.(T)
“鸭子模式” :
“一个东西看来长得像鸭子, 叫声像鸭子, 走路像鸭子,我们就认为它是鸭子。”
golang 的接口实现不像 java 一样需要显式实现, 认为一个struct 实现了 interface 的所有方法就证明实现了这个接口。因为这种松散耦合会让 struct 多实现接口,导致代码阅读不流畅。 需要 coder 对代码进行有效的目录组织
© 著作权归作者所有,转载或内容合作请联系作者
喜欢的朋友记得点赞、收藏、关注哦!!!
相关文章:
Golang 入门基础知识
快速入门 go 语言笔记,参考了各种大佬的 blog 。 一、下载安装 国内站点 :https://golang.google.cn IDE : Goland (2020.12月以后破解有些困难 见 zhile.io, 无法试用的话可以搜一个注册码,失效的也可以&…...
SQL把字符串按逗号分割成记录
在 SQL 中,可以通过以下方法将字符串按逗号分割,并将每个分割的值作为单独的记录插入到结果集中。以下是针对不同数据库系统的实现方法: 1. 使用 STRING_SPLIT(SQL Server 2016) STRING_SPLIT 是 SQL Server 提供的内置…...
HTML——26.像素单位
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>像素</title></head><body><!--像素:1.指设备屏幕上的一个点,单位px,如led屏上的小灯朱2.当屏幕分辨率固定时&…...
TIM的输出比较(PWM)
OC(Output Compare)输出比较 输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道 初始化 1.开启RCC RCC_…...
李宏毅机器学习笔记-Transformer
目录 1. Seq2seq 2. encoder Transformer 中的 Block 结构 3. Decoder 4.Encoder和Decoder间的信息传递 5.Training 6.Tips 1. Seq2seq Transformer 是一个seq2seq的model。Seq2seq指的是input是一个序列,输出也是一个序列,输出的长度是由机器自己…...
【机器学习:三、常见的代价函数】
代价函数的选择与任务类型(回归、分类等)以及模型的具体目标密切相关。 回归任务中的代价函数 均方误差(Mean Squared Error, MSE) 公式: J ( θ ) 1 m ∑ i 1 m ( y i − y ^ i ) 2 J(\theta)\frac{1}{m}\sum_{i1}^m(y_i-\…...
React 数据是怎样传递的
写在前面 在 React 应用程序中,数据传递是非常重要的。它允许我们在组件之间共享信息和状态,从而构建出复杂的用户界面。本文将深入探讨 React 中的数据传递机制,包括 props、state 和 context API。我们还将通过实际例子来演示如何在项目中…...
Python入门教程 —— 高阶函数和装饰器
1.递归函数 什么是递归函数 如果一个函数在内部不调用其它的函数,而是自己本身的话,这个函数就是递归函数。 递归函数的作用 举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n 解决办法1:使用循环来完成 def cal(num):result,i = 1,1while i <= num:result *= ii…...
RSA密码的安全性分析(简化版本)
RSA的安全性是基于分解大整数的困难性假定,之所以认为是假定是因为至今还未能证明大整数就是NP问题,也许有尚未发现的多项式时间分解算法。随着人类计算能力的不断提高,原来被认为是不可能分解的大数已经被成功分解。对于大整数的威胁除了人类…...
解决Vue中设置el-select的高度不生效问题
el-select是Element UI框架中的一个选择器组件,它允许用户从多个选项中选择一个或多个项目。但这里确存在一个小坑,我们可以看到直接修改el-select的高度是无法生效的 <template><div id"login"><el-select v-model"role…...
解决openpyxl操纵带公式的excel或者csv之后,pandas无法读取数值的问题
1 功能特点 openpyxl: 这是一个专门用于操作Excel文件(.xlsx/.xlsm)的库。它提供了丰富的功能来读取、写入和修改Excel文件的各个元素,如单元格、行、列、工作表等。例如,可以通过openpyxl轻松地创建一个新的Excel工作…...
高等数学学习笔记 ☞ 无穷小比较与等价无穷小替换
1. 无穷小比较 1. 本质:就是函数的极限趋于0时的速度,谁快谁慢的问题。 2. 定义:若是在同一自变量的变化过程中的无穷小,且,则: ①:若,则称是比的高阶无穷小,记作&…...
计算队列中的‘捣乱分子’对数:一种量化无序程度的方法
计算队列中的‘捣乱分子’对数:一种量化无序程度的方法 前言解题思路关键点实现代码时间复杂度分析前言 在日常生活中,我们经常会遇到需要排队的场景,比如买票、候车、就餐等。在理想的排队情况下,人们会按照某种顺序(如先到先服务)整齐地排成一列。然而,总有一些人不遵…...
如何在不丢失数据的情况下从 IOS 14 回滚到 IOS 13
您是否后悔在 iPhone、iPad 或 iPod touch 上安装 iOS 14?如果你这样做,你并不孤单。许多升级到 iOS 14 beta 的 iPhone、iPad 和 iPod touch 用户不再适应它。 如果您在正式发布日期之前升级到 iOS 14 以享受其功能,但您不再适应 iOS 14&am…...
提升汽车金融租赁系统的效率与风险管理策略探讨
内容概要 在汽车金融租赁系统这个复杂的生态中,提升整体效率是每个企业都渴望达成的目标。首先,优化业务流程是实现高效运行的基础。通过分析目前的流程,找出冗余环节并进行简化,能够帮助企业缩短审批时间,提高客户满…...
Git 入门(一)
git 工作流如下: 命令如下: clone(克隆): 从远程仓库中克隆代码到本地仓库checkout (检出):从本地仓库中检出一个仓库分支然后进行修订add(添加): 在提交前先将代码提交到暂存区com…...
pyinstaller冻结打包多进程程序的bug:无限创建进程直至系统崩溃
前面写过两篇相关的文章: PyQt应用程序打包Python自动按键 这两篇文章都没有提到下面的这个重要问题: 采用Pyinstaller冻结打包多进程程序时,必须非常小心。这个技术线在Windows上会有一个非常严重的Bug。直接运行打包后的程序会造成无限创…...
OSI模型的网络层中产生拥塞的主要原因?
( 1 )缓冲区容量有限;( 1.5 分) ( 2 )传输线路的带宽有限;( 1.5 分) ( 3 )网络结点的处理能力有限;( 1 分…...
服务器开发 的设计模式(Design Patterns)核心知识
服务器开发的设计模式(Design Patterns)核心知识 在现代软件开发中,设计模式是一组为解决特定问题而重用的解决方案。它们是经过时间考验的最佳实践,为开发人员提供了一种有效的解决方案来应对常见的软件设计问题。特别是在服务器…...
智能工厂的设计软件 应用场景的一个例子: 为AI聊天工具添加一个知识系统 之20 再次重建 之5 项目文件三大部 整“拼”项目文档总述
本文要点 ]本项目的项目文件中的三个Part终于“拼”出来的(个别用词可能还需斟酌,但样子、格式和意思 差不多): Part1【凡间-AI众生】 人性化&去中心化-个体人生观 语言有关(语言交流-经验常识实践 信条 行为主义 随机数-本质偶然的 想…...
Lucas-Kanade光流法详解
简介:个人学习分享,如有错误,欢迎批评指正。 光流(Optical Flow)描述的是图像序列中各像素点随时间的运动情况,是计算机视觉中的基本问题之一。光流问题涉及尝试找出一幅图像中的许多点在第二幅图像中移动的…...
基于YOLO5的机械臂视觉抓取实现
前言: 机器人视觉系统标定是保证机器人精确运动和控制的关键环节之一。通过对机器人的运动学进行分析,可以精确计算出机器人末端执行器的位姿信息,从而实现对目标的精准定位和控制。相机标定是计算机视觉和图像处理中的重要步骤,标…...
Git 仓库与文件管理笔记
Git 的三种仓库概念 本地仓库 (Local Repository) 位于本地 .git 文件夹中通过 git init 或 git clone 创建存储完整的项目历史和分支信息 远程仓库 (Remote Repository) 位于 GitHub、GitLab 等平台服务器使用 git remote -v 查看所有远程仓库默认远程仓库名通常为 origin 工…...
Qt 5.14.2 学习记录 —— 오 信号与槽机制(2)
文章目录 1、信号与槽的参数2、为什么要有信号槽机制?3、断开并重新连接4、槽函数lambda写法 1、信号与槽的参数 信号和槽可以带参数,当信号带有参数时,槽的参数必须和信号的一致,此时发送信号就可以给信号函数传递实参ÿ…...
043_小驰私房菜_MTK Camera,Hal层将camera型号写到property属性中
【问题背景】 app层需要知道当前设备的摄像头型号,然后做一些差异化处理。底下如何上报这个摄像头型号? 【分析】 在kernel和hal层,都是有地方能获取到当前摄像头的型号,就看在哪里添加方便。获取到摄像头硬件型号后,将其写入到property属性, 然后app就可以通过读取该…...
Java线程
目录 一、线程入门 二、线程同步 三、死锁 四、线程的方法 五、线程的流程图 六、线程池 一、线程入门 1.进程:每一个软件都是一个进程。 2.线程:进程是由多个线程组成的。 3.进程和线程的关系:一个进程是对应一个或者是多个线程的。…...
JMeter 的 If Controller:开启性能测试的智能大门
嘿,宝子们!今天咱们就来聊聊 JMeter 里超级厉害的 If Controller,它就像是一把神奇的钥匙,能帮我们打开灵活测试的大门,让你的测试计划变得更加智能和高效。 一、If Controller 初印象 想象一下,你正在指…...
node内置模块之---os 模块
os 模块的作用 os 模块是 Node.js 的一个核心模块,提供了与操作系统交互的一些功能。它使得 Node.js 应用可以访问操作系统的底层信息,并执行一些系统级的操作,比如文件系统操作、环境变量、进程管理等。 os 模块的相关api 文件系统路径操…...
PgSQL如何用cmd命令行备份和还原数据库
一、备份 备份为压缩的二进制格式(通常更快且占用空间更少) pg_dump -U username -Fc -h hostname -p port -d dbname -F p -f backup.sql-U username:指定连接数据库的用户名(默认是 postgres)。-Fc:备…...
neo4j学习笔记
图数据库 图数据库是基于图论实现的一种NoSQL数据库,其数据存储结构和数据查询方式都是图论为基础的,图数据库主要用于存储更多的连接数据。 图论(GraphTheory)是数学的一个分支。图论以图为研究对象,图论的图是由若干…...
Halcon 显示异常
//For Halcon System HOperatorSet.SetSystem("clip_region", "false"); set_system( clip_region, false) *旋转 hom_mat2d_identity (HomMat2DIdentity1) hom_mat2d_rotate (HomMat2DIdentity1, rad( 90), 0, 0, HomMat2DRotate) affine_trans_region …...
2021年3月多省联考《申论》B卷真题解析
2021年福建公务员考试申论试题(乡镇卷) 材料一 在传统乡镇布局中,部分乡镇面积小、人口少,但管理机构、干部职数、机构编制、财政投入均不少,行政运行成本较高。合理调整乡镇行政区划,有助于统筹设置乡镇服…...
Mac iTerm2集成DeepSeek AI
1. 去deepseek官网申请api key,DeepSeek 2. 安装iTerm2 AI Plugin插件,https://iterm2.com/ai-plugin.html,插件解压后直接放到和iTerms相同的位置,默认就在/Applications 下 3. 配置iTerm2 4. 重启iTerm2,使用快捷键呼出AI对话…...
主机A与主机B建立TCP连接的三次握手过程
( 1 )主机 A 的 TCP 向主机 B 发出连接请求 SYN 报文段(第一次握手)。( 1 分) ( 2 )一旦包含 SYN 报文段的 IP 数据报到达主机 B , SYN 报文段被从数据报…...
vue3 vite 动态加载路由遇到的问题
记录一下动态加载路由遇到的问题 正常使用import引入静态路由是没问题的 component: () > import(/components/ExampleComponent.vue)动态引入的时候写成import就不行了 由于后端给的路由格式比较反人类…我这边先递归把获取到的数据格式做了一个整合. const processedDa…...
仿生的群体智能算法总结之一(十种)
群体智能算法是一类通过模拟自然界中的群体行为来解决复杂优化问题的方法。以下是10种常见的群体智能算法: 编号 算法名称(英文) 算法名称(中文) 年份 作者 1 Ant Colony Optimization (ACO) 蚁群优化算法 1991 Marco Dorigo 2 Particle Swarm Optimization (PSO) 粒子群优…...
02pandas读取和保存数据的方法
pandas读取和保存数据的方法 一、pandas支持的数据格式二、pandas常用数据读取方法1. 准备工作2. 代码示例1.pandas数据读取常用的方法2. read_excel 方法(1)read_excel()方法:读取excel文件(2)head()方法:…...
【STM32项目】智能物联网驱动的生物样本培育与管理辅助系统(完整工程资料源码)
视频功能演示: 智能物联网驱动的生物样本培育与管理辅助系统 目录: 目录 视频功能演示: 目录:...
家教老师预约平台小程序系统开发方案
家教老师预约平台小程序系统将连接学生/家长与家教老师,提供一站式的家教服务预约体验。 一、用户需求分析1、家教老师:希望获得更多的学生资源,通过平台展示自己的教学特长和经验,管理个人日程,接收并确认预约请求&a…...
uniapp 自定义类微信支付键盘 (微信小程序)
效果图 代码: <view class"popups popupsB"><view class"appreciatePrice"><view class"appreciatePriceTitle">赞赏金额</view><view class"appreciatePriceInput flex ac">¥<input typ…...
推荐5款局域网IP扫描工具,支持电脑+Android!
在日常网络管理中,快速扫描局域网中的设备和IP地址是一项基本但非常重要的任务。无论是排查网络问题还是进行设备管理,一款好用的 IP 扫描工具都能让你事半功倍。 如何选择适合自己需求的局域网 IP 扫描工具?有哪些功能强大又易于上手的工具…...
第十一章 图论
/* * 题目名称:连通图 * 题目来源:吉林大学复试上机题 * 题目链接:http://t.cn/AiO77VoA * 代码作者:杨泽邦(炉灰) */#include <iostream> #include <cstdio>using namespace std;const int MAXN 1000 10;int fathe…...
算法学习(21)—— BFS解决FloodFill问题
关于FloodFill 关于FloodFill算法,我们之前在dfs章节已经介绍过了:算法学习(17)—— FloodFill算法-CSDN博客 下面是用bfs宽搜来解决的实例 部分OJ题详解 733. 图像渲染 733. 图像渲染 - 力扣(LeetCode…...
计算机网络基础(7)中科大郑铨老师笔记
应用层 目标: 网络应用的 原理:网络应用协议的概念和实现方面 传输层的服务模型 客户-服务器模式 对等模式(peerto-peer) 内容分发网络 网络应用的 实例:互联网流行的应用层协 议 HTTP FTP SMTP / POP3 / IMAP DNS…...
CSS 之 响应式设计 前世今生
CSS系列文章目录 CSS 之 display 布局属性详解 CSS 之 position 定位属性详解一文搞懂flex布局 【弹性盒布局】 文章目录 CSS系列文章目录一、前言二、历史上的网站布局三、响应式设计之前的灵活布局四、响应式设计五、媒介查询六、灵活网格七、现代布局技术7.1 多栏布局7.2 伸…...
前端,npm install安装依赖卡在sill idealTree buildDeps(设置淘宝依赖)
输入npm i后,一直卡在sill idealTree buildDeps,一动不动 cnpm可以安装成功,但使用cnpm不会生成package-lock.json文件 设置淘宝依赖,依然卡住,挂梯子也不行 解决方法: // 取消ssl验证 set strict-ssl …...
【Rust自学】9.2. Result枚举与可恢复的错误 Pt.1:match、expect和unwrap处理错误
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 9.2.1. Result枚举 通常情况下,错误都没有严重到需要停止整个程序的地步。某个函数之所以运行失败或者是遇到错误通常是由一些…...
[241231] CachyOS 2024 年终总结:性能飞跃与社区繁荣 | ScyllaDB 宣布转向开源可用许可证
目录 CachyOS 2024 年终总结:性能飞跃与社区繁荣ScyllaDB 宣布转向开源可用许可证 CachyOS 2024 年终总结:性能飞跃与社区繁荣 CachyOS 2024 年的最后一个版本 (也是第 13 个版本) 已经发布,同时也迎来了辞旧迎新之际。让我们一起回顾 Cachy…...
9.系统学习-卷积神经网络
9.系统学习-卷积神经网络 简介输入层卷积层感受野池化层全连接层代码实现 简介 卷积神经网络是一种用来处理局部和整体相关性的计算网络结构,被应用在图像识别、自然语言处理甚至是语音识别领域,因为图像数据具有显著的局部与整体关系,其在图…...
java并发之AQS
一、简介 AQS,全称:AbstractQueuedSynchronizer,是一个JDK提供的用于构建锁、同步器等线程协作工具类的框架,内部维护FIFO双向队列(双向链表实现)。 AQS重要属性: // 表示同步状态。它既可以表…...