1、基础学习
(1)声明变量
var identifier type
var identifier1, identifier2 type
第一种,指定变量类型,如果没有初始化
,则变量默认为零值
。
第二种,根据值自行判定变量
类型。
第三种,省略 var
, 注意 := 左侧如果没有声明新的变量
,就产生编译错误。
// 声明一个变量并初始化
var identifier [type] = 值
var a string = "Runoob"
var a = "RUNOOB"
(2)多变量声明
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
var (
Debug = false
DebugLog = NewLog(os.Stdout)
DefaultRowsLimit = 1000
DefaultRelsDepth = 2
DefaultTimeLoc = time.Local
ErrTxHasBegan = errors.New("<Ormer.Begin> transaction already begin")
ErrTxDone = errors.New("<Ormer.Commit/Rollback> transaction not begin")
ErrMultiRows = errors.New("<QuerySeter> return multi rows")
ErrNoRows = errors.New("<QuerySeter> no row found")
ErrStmtClosed = errors.New("<QuerySeter> stmt already closed")
ErrArgs = errors.New("<Ormer> args error may be empty")
ErrNotImplement = errors.New("have not implement")
)
(3)go语言常量
const identifier [type] = value
显式类型定义: const b string = "abc"
隐式类型定义: const b = "abc"
常量还可以用作枚举:
const (
Unknown = 0
Female = 1
Male = 2
)
(4)iota
iota,特殊常量
,可以认为是一个可以被编译器修改
的常量。
iota 在 const关键字出现时将被重置为 0
(const 内部的第一行之前),const 中每新增一行
常量声明将使 iota 计数一次
(iota 可理解为 const 语句块中的行索引
)。
iota 可以被用作枚举值
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
const (
a = iota
b
c
)
等价于
const (
a = iota
b = iota
c = iota
)
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
运行结果:0 1 2 ha ha 100 100 7 8
⚠️注:iota简写会记录上一行的表达式,并且iota自增
package main
import "fmt"
const (
i=1<<iota
j=3<<iota
k
l
)
func main() {
fmt.Println("i=",i)
fmt.Println("j=",j)
fmt.Println("k=",k)
fmt.Println("l=",l)
}
iota 表示从 0 开始自动加 1,所以 i=1<<0, j=3<<1(<< 表示左移的意思),即:i=1, j=6,这没问题,关键在 k 和 l,从输出结果看 k=3<<2,l=3<<3。
(5)go切片
切片是可索引
的,并且可以由 len()
方法获取长度。
切片提供了计算容量的方法 cap()
可以测量切片最长可以达到多少。
(6)切片截取
package main
import "fmt"
func main() {
/* 创建切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}
printSlice(numbers)
/* 打印原始切片 */
fmt.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int,0,5)
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
(7)切片append() 和 copy() 函数
如果想增加切片的容量
,我们必须创建一个新的更大的切片
并把原分片的内容都拷贝
过来。
2、进阶
(1)在定义常量组时,如果不提供初始值,则表示将使用上行的表达式。
package main
import "fmt"
const (
a = 1
b
c
d
)
func main() {
fmt.Println(a)
// b、c、d没有初始化,使用上一行(即a)的值
fmt.Println(b) // 输出1
fmt.Println(c) // 输出1
fmt.Println(d) // 输出1
}
(2)字符串去除空格和换行符
package main
import (
"fmt"
"strings"
)
func main() {
str := "这里是 www\n.runoob\n.com"
fmt.Println("-------- 原字符串 ----------")
fmt.Println(str)
// 去除空格
str = strings.Replace(str, " ", "", -1)
// 去除换行符
str = strings.Replace(str, "\n", "", -1)
fmt.Println("-------- 去除空格与换行后 ----------")
fmt.Println(str)
}
(3)Golang sizeof 占用空间大小
package main
import (
"fmt"
"unsafe"
)
type Man struct {
Name string
Money int
Age int32
}
func main() {
m := Man{Name:"james", Money:1000000, Age:30}
fmt.Println("man size:", unsafe.Sizeof(m))
fmt.Println("name size:", unsafe.Sizeof(m.Name))
fmt.Println("money size:", unsafe.Sizeof(m.Money))
fmt.Println("age size:", unsafe.Sizeof(m.Age))
}
运行结果:
man size: 32
name size: 16
money size: 8
age size: 4
⚠️注:
go语言的string
是一种数据类型
,这个数据类型占用16字节空间
,前8字节是一个指针
,指向字符串值的地址
,后八个字节是一个整数
,标识字符串的长度;注意go语言的字符串内部并不以'\0'作为结尾
,而是通过一个长度域来表示字符串的长度。
type mystr struct {
strbuf uintptr;
strlen uint64;
}
(4)iota 只是在同一个 const 常量组内递增,每当有新的 const 关键字时,iota 计数会重新开始。
package main
import "fmt"
const (
a = 1
b
c
d
)
func main() {
fmt.Println(a)
// b、c、d没有初始化,使用上一行(即a)的值
fmt.Println(b) // 输出1
fmt.Println(c) // 输出1
fmt.Println(d) // 输出1
}
(5)Go 的自增,自减只能作为表达式使用,而不能用于赋值语句。
a++ // 这是允许的,类似 a = a + 1,结果与 a++ 相同
a-- //与 a++ 相似
a = a++ // 这是不允许的,会出现变异错误 syntax error: unexpected ++ at end of statement
(6)切片初始化
s :=[] int {1,2,3 } 直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3。其 cap=len=3。
s := arr[:] 初始化切片 s,是数组 arr 的引用。
s := arr[startIndex:endIndex] 将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。
s := arr[startIndex:] 缺省 endIndex 时将表示一直到 arr 的最后一个元素。
s := arr[:endIndex] 缺省 startIndex 时将表示从 arr 的第一个元素开始。
s1 := s[startIndex:endIndex] 通过切片 s 初始化切片 s1
s :=make([]int,len,cap) 通过内置函数 make() 初始化切片 s,[]int 标识为其元素类型为 int 的切片。
(7)创建结构体
// 创建一个新的结构体
Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407}
// 也可以使用 key => value 格式
Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407}
(8)go语言递归函数
func recursion() {
recursion() /* 函数调用自身 */
}
func main() {
recursion()
}
(9)类型转换
type_name(expression)
3、高阶
(1)panic 、recover 、defer
1.引发panic有两种情况,一是程序主动调用,二是程序产生运行时错误,由运行时检测并退出。
2.发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到被recover捕获或运行到最外层函数。
3.panic不但可以在函数正常流程中抛出,在defer逻辑里也可以再次调用panic或抛出panic。defer里面的panic能够被后续执行的defer捕获。
4.recover用来捕获panic,阻止panic继续向上传递。recover()和defer一起使用,但是defer只有在后面的函数体内直接被掉用才能捕获panic来终止异常,否则返回nil,异常继续向外传递。
(2)panic使用场景
一般情况下有两种情况用到:
1.程序遇到无法执行下去的错误时,抛出错误,主动结束运行。
2.在调试程序时,通过 panic 来打印堆栈,方便定位错误。
(3)panic总结
1、panic 在没有用 recover 前以及在 recover 捕获那一级函数栈,panic 之后的代码均不会执行;一旦被 recover 捕获后,外层的函数栈代码恢复正常,所有代码均会得到执行;
2、panic 后,不再执行后面的代码,立即按照逆序执行 defer,并逐级往外层函数栈扩散;defer 就类似 finally;
3、利用 recover 捕获 panic 时,defer 需要在 panic 之前声明,否则由于 panic 之后的代码得不到执行,因此也无法 recover;
(4)关闭通道
并不会丢失
里面的数据
,只是让读取通道数据
的时候不会
读完之后一直阻塞等待
新数据写入
(5)通道无缓冲和有缓冲的区别无缓冲是同步的
,例如 make(chan int),就是一个送信人去你家门口送信,你不在家他不走,你一定要接下信,他才会走,无缓冲保证信能到你手上。
有缓冲是异步的
,例如 make(chan int, 1),就是一个送信人去你家仍到你家的信箱,转身就走,除非你的信箱满了,他必须等信箱空下来,有缓冲的保证信能进你家的邮箱。
学习链接:
https://studygolang.com/articles/10835
https://www.runoob.com/go/go-basic-syntax.html