【VScode F5默认调试】
文件->打开文件夹 (选择该文件夹)
运行->添加配置,选GO,此时会在文件夹下建立一个.vscode文件夹,里面有个launch.json文件
在vscode里面打开launch.json文件,删除configurations下的配置,点右下角“添加配置”,选Go:launch file。切回.go文件,F5,完成
【sql语句过长换行】
stmtOut, err = dbConn.Prepare("这里写sql语句")
stmtOut, err := dbConn.Prepare(`这里
写sql
语句`)
【跨平台编译】
build
env GOOS=linux GOARCH=amd64 go build
【编译】
install
常用于本地打包编译的命令:go install
$GOPATH下文件夹go install
$GOPATH外单文件go install 文件名
打包之后在$GOPATH/bin下
【打印】
fmt.println(变量名),会换行
fmt.printf("%d",变量名),不会换行
有关fmt包的printf占位符 https://www.cnblogs.com/xiao-xue-di/p/11521823.html
【定义变量】
下划线变量
_ = append(s1, 6)
如果有声明了不想用又不得不用的变量可以赋值给它,这个特殊变量不可读取。
var s int = 42(可读性最强,用于重要变量)
var s = 42 (介于两者之间)
s := 42 (最方便,用于很随意的变量)
多个变量一起声明
var(
a = "123"
b = "456"
)
其中for语句里面不能用var声明,直接用 := 就好
for i:=0; i<10; i++ {
}
如果定义一个变量不赋值,那么默认会赋予一个[零值]
string类型的[零值]是一个空字符串,长度为0
int类型的[零值]是0
bool类型的零值]是false
指针类型的[零值]是nil
全局变量:
首字母大写 - 外部全局变量
首字母小写 - 内部全局变量
内部全局变量只有当前包的代码可以访问
常量分全局常量和局部常量,常量必须初始化并赋值,不可二次赋值
指针变量和取地址符


【条件语句】
循环for range,没有while,可用for{}(不写条件)代替
分支if swtich
条件都不需要加括号
【数组】
数组的声明
var a = [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
c := [8]int{1, 2, 3, 4, 5, 6, 7, 8}
声明之后,不可改变类型,不可改变长度
同类型&同长度数组之间才可以相互赋值
数组之间赋值之后修改任意一个数组元素的值,另一个数组相对应下标的值不会随之改变

【切片】
切片的创建
//创建一个容量cap为8,长度len为5的零值切片,值为[0 0 0 0 0]
var s1 []int = make([]int, 5, 8)
//创建一个容量和长度都为8的零值切片,值为[0 0 0 0 0 0 0 0]
var s2 []int = make([]int, 8)
//也可以这样创建零值切片,参数从左到右依次是类型,Len,cap
var s1 = make([]int, 5, 8)
s2 := make([]int, 8)
//这样创建的切片是满容的
var s []int = []int{1,2,3,4,5} // cap和len都为5
//创建空切片,他们值均为[] cap为0 len也为0
var s1 []int
var s2 []int = []int{}
var s3 []int = make([]int, 0)
切片之间直接赋值之后,修改其中一个切片的元素的值,另一个切片相对应元素的值会随之改变
切片之间用copy()赋值后,修改其中一个切片元素的值,另一个切片相对应元素的值不会发生改变
切片的基础操作
package main
func main() {
var s1 []int
s2 := []int{14,15}
for i := 0; i < 10; i++ {
s1 = append(s1, i)
}
// 添加一个元素元素
s1 = append(s1, 10)
// 添加多个元素元素
s1 = append(s1, 11, 12, 13)
// 添加另一个切片的元素(后面加…)
s1 = append(s1, s2...)
// 删除索引最小的元素
_ = s1[1:]
// 删除索引最大的元素
_ = s1[:1]
// 删除 (非最大最小 && 索引为index)的元素
index := 4
_ = append(s1[:index], s1[index+1:]...)
}
切片append之后必须使用,切片append追加之后如果超出了原切片的容量,会变成一个新的切片,不再和原切片共享同一个底层数组,原切片改变元素新切片对应元素不变。
切片的扩容要用append,不能用赋值的方法扩容,否则会触发panic中断程序
func main() {
var s1 []int
s1[0] = 1 // 此处会触发panic
}
当比较短的切片扩容时,系统会多分配 100% 的空间
但切片长度超过1024时,扩容策略调整为多分配 25% 的空间
切片之后再切片

package main import ( "fmt" ) func main() { a := [5]int{1, 2, 3, 4, 5} s := a[1:3] // s := a[low:high] //s:[2 3] fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s)) s2 := s[3:4] // 索引的上限是cap(s)而不是len(s) fmt.Printf("s2:%v len(s2):%v cap(s2):%v\n", s2, len(s2), cap(s2)) }
输出
s:[2 3] len(s):2 cap(s):4
s2:[5] len(s2):1 cap(s2):1
第一次对切片执行切片表达式时
a:[1,2,3,4,5]
s:[2,3] //a[1:3] 取切片 a 的索引 1 到 2 (a[1],a[2])得出 [2,3],实际容量为从a的索引1开始一直到最后,也就是容量为4
当对切片再次执行切片表达式时
s2[5] //此时又把切片a拿出来,只不过此时的索引是从被s切过的地方算起,也就是[2,3,4,5] 。对[2,3,4,5]执行切片表达式[3:4]就是取第4个元素,值为5
【字典】有点类似于PHP的key->value数组
创建字典,使用 make 函数创建的字典是空的,长度为零,内部没有任何元素。
var m map[int]string = make(map[int]string)
字典初始化赋值
var m = map[int]string{
90: "优秀",
80: "良好",
60: "及格",
}
字典检查Key是否存在
var score, ok = fruits["durin"]
if ok {
fmt.Println(score)
} else {
fmt.Println("durin not exists")
}
【字符串】
对中文字符串通过下标按字节遍历
package main
import "fmt"
func main() {
var s = "嘻哈china"
for i:=0;i<len(s);i++ {
fmt.Printf("%v ", s[i])
}
}
结果
229 152 187 229 147 136 99 104 105 110 97
对字符串通过rune遍历
package main
import (
"fmt"
)
func main() {
v6 := []rune("嘻哈china")
for k, v := range v6 {
fmt.Printf("%c", v)
}
}
结果
//%c,该值对应的unicode码 嘻哈china //%v,该值的十进制表示 22075 21704 99 104 105 110 97
字符串不可按下标赋值,编译器会直接在语法上拒绝
字符串切割
func main() {
var s1 = "hello world"
var s2 = s1[3:8] //s2类型依然是string
fmt.Println(s2)
}
------------------------
lo wo
字符串和字节切片相互转换
var b = []byte(s1) // 字符串转字节切片 var s2 = string(b) // 字节切片转字符串
【结构体】
创建
package main
import "fmt"
type Circle struct {
x int
y int
Radius int
}
func main() {
var c Circle = Circle {
x: 100,
y: 100,
Radius: 50, // 注意这里的逗号不能少
}
fmt.Printf("%+v\n", c)//%+v输出结构体时会加字段名
}
----------
{x:100 y:100 Radius:50}
如果赋值的时候缺省了,则用该类型[零值]代替
var c1 Circle = Circle {
Radius: 50,
}
-----------------
{x:0 y:0 Radius:50}
还可以这样赋值
var c Circle = Circle {100, 100, 50}
但要所有的字段都赋值不能缺
使用new返回的是指针,同时所有字段均为[零值]
var c *Circle = new(Circle)
三种零值结构体初始化方法:
var c1 Circle = Circle{}
var c2 Circle
var c3 *Circle = new(Circle)
值类型的结构体之间的拷贝是不共享底层数据的
指针类型的结构体之间的拷贝是共享底层数据的
Go 语言的方法名称也分首字母大小写,它的权限规则和字段一样,首字母大写就是公开方法,首字母小写就是内部方法,只能归属于同一个包的代码才可以访问内部方法
结构体方法使用
func (c Circle) expandByValue() int {
return c.Radius//go里,函数里要return的地方必须声明返回变量类型,也就是上面那个int
}
//调用
c.expandByValue()
---------------------------------------------
func expandByPointer(c Circle) int{
return c.Radius
}
//调用
expandByPointer(c)
结构体的 指针方法 和 值方法 的区别
package main
import "fmt"
type Circle struct {
Radius int
}
func (c Circle) expandbyvalue() {
c.Radius *= 2
}
func (c *Circle) expandbypoint() {
c.Radius *= 2
}
func main() {
var c = Circle{Radius: 50}
c.expandbyvalue()
fmt.Println(c.Radius)
// 指针变量调用方法形式上是一样的
var pc = &c
pc.expandbypoint()
fmt.Println(pc.Radius)
}
------------------------------------------
50
100
通过指针访问内部的字段需要 2 次内存读取操作,第一步是取得指针地址,第二部是读取地址的内容。结构体进行值拷贝时,如果结构体很大,那拷贝的就很慢,如果此时就拷贝个指针,就很快
内嵌结构体
type Point struct {
x int
y int
}
func (p Point) show() {
...
}
type Circle struct {
loc Point
Radius int
}
func main() {
var c = Circle {
loc: Point {
x: 100,
y: 100,
},
Radius: 50,
}
//结构体
fmt.Printf("%+v\n", c)
//内嵌结构体
fmt.Printf("%+v\n", c.loc)
//内嵌结构体的变量
fmt.Printf("%d %d\n", c.loc.x, c.loc.y)
//内嵌结构体的方法
c.loc.show()
}
匿名内嵌结构体
type Point struct {
x int
y int
}
func (p Point) show() {
...
}
type Circle struct {
Point // 匿名内嵌结构体
Radius int
}
func main() {
var c = Circle {
Point: Point {
x: 100,
y: 100,
},
Radius: 50,
}
fmt.Printf("%+v\n", c)
fmt.Printf("%+v\n", c.Point)
fmt.Printf("%d %d\n", c.x, c.y) // 继承了字段
fmt.Printf("%d %d\n", c.Point.x, c.Point.y)
c.show() // 继承了方法
c.Point.show()
}
多态:子类可以重新定义父类的方法
GO语言结构体不支持多态
【defer 和 return 】
func c() (i int) {
defer func()
return 1
}
defer 会延迟执行函数,但会在他所在的函数销毁之前被执行
先defer的后执行,后defer的先执行
参考来源 https://www.cnblogs.com/ricklz/p/9574645.html
【map】
多协程并发操作下用sync.Map,单协程下用 map
在并发下操作 map 可能会panic报错,fatal error: concurrent map writes
定义map
make(map[KeyType]ValueType, [cap])
// 例:
scoreMap := make(map[string]int, 8)
添加元素
scoreMap["张三"] = 90
判断某个键是否存在
v, ok := scoreMap["张三"]
if ok {
fmt.Println(v)
} else {
fmt.Println("查无此人")
}
遍历map
for k, v := range scoreMap {
fmt.Println(k, v)
}
删除键值对
scoreMap := make(map[string]int) scoreMap["小明"] = 100 delete(scoreMap, "小明") // 将小明:100从map中删除
【sync.Map】
需要保证线程安全且读多写少的情况下用sync.Map,此方法在性能上存在一些问题,如果是大量并发的Read,则问题不明显
package main
import (
"fmt"
"sync"
)
var sm = new(sync.Map) // var sm *sync.Map = new(sync.Map)
func main() {
// store 方法,添加元素
sm.Store(1, "a")
// Load 方法,获得value
if v, ok := sm.Load(1); ok {
fmt.Println(v)
}
// Delete 方法,删除元素
sm.Delete(1)
// 遍历该map,参数是个函数,该函数参的两个参数是遍历获得的key和value,返回一个bool值,当返回false时,遍历立刻结束。
sm.Range(func(k, v interface{}) bool {
fmt.Print(k)
fmt.Print(":")
fmt.Print(v)
fmt.Println()
return true
})
fmt.Println(sm.Load(1))
}
【接口 interface】
空接口的应用,一般遇到不确定参数类型的地方就用空接口
// 定义一个空接口x
var x interface{}
// 字符串string
s := "Hello 沙河"
x = s
fmt.Printf("type:%T value:%v\n", x, x)
// int
i := 100
x = i
fmt.Printf("type:%T value:%v\n", x, x)
// bool
b := true
x = b
fmt.Printf("type:%T value:%v\n", x, x)
空接口作为函数参数
func show(a interface{}) {
fmt.Printf("type:%T value:%v\n", a, a)
}
空接口作为map值
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "沙河娜扎"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)
类型转换
func test(a interface{}) int {
return a.(int)
}
浙公网安备 33010602011771号