0%

go学习笔记-基础与特性总结

特性

  • Go 在用一个目录下不能有多个 package
  • Go 中立即返回函数中局部变量的地址也是安全的
  • Go 中 new 只是一个预定义的函数,不是一个关键字,因此可以将 new 重新定义为别的类型使用,这样将无法使用 new 函数
  • Go 的早期版本 GOMAXPROCS 可用核数被设置为 1 因此无法真正做到并行 而 go1.6 版本后 GOMAXPROCS 被设置为可用核心数目
  • Go 中++ —是语句 不是表达式,并且只有后缀形式,因此以下的代码是错的
1
2
a := 5
b := a++ //不能当做表达式使用
  • Go 不支持隐式类型转换
  • Go 不支持三元操作符
  • Go 只有值传递
  • Go 指针不能进行运算
  • 特殊只写变量_用于忽略值占位

数据类型

  • Go 的基本类型有 Basic types
    • bool
    • string golang 是没有 char 的 string 在 go 中属于基本数据类型 不可变类型
    • int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr
    • byte 是 uint8 的别名
    • rune 是 int32 的别名 代表一个 Unicode 码
    • float32 float64
    • complex64 complex128 Go 原生支持复数类型
  • 16 进制的转义形式是\xhh。8 进制的转移形式是\ooo,o 代表一个八进制数字,但是不能超过\377(对应十进制的 255)
  • 无符号数往往只有在位运算或其他特殊的运算场景才会使用
  • %q 打印带有单引号的字符
  • 函数外的每个语句都必须以关键字开始(varfunc、等等),:= 结构不能使用在函数外。
  • 字符串和字节 slice 可以相互转换 一个[]bytes 转换分配了一个新的字节数组用于保存字符串数据的拷贝,然后引用这个底层的字节数组
  • 将一个整数转为字符串有两种方法,一种是用 fmt.Sprintf 返回一个格式化的字符串,另一种方法是用 strconv.Itoa
  • FormatInt(FormatUint) func(i int64, base int) string 可以用不同的进制格式化数字
  • strconv.ParseInt 或者 strconv.Atoi 可以用来解析一个字符串为证书,还有用于解析无符号整数的 ParseUint
1
2
3
s := "abc"
b := []byte(s)
s2 := string(b)
  • 可以通过自定义类型来实现枚举类型限制
  • math.NaN 与任何数都不相等
  • Go 语言中的许多常量并没有一个明确的基础类型,编译器为这些没有明确的基础类型的数字常量提供比基础类型更高精度的算术运算,可认为至少有 256bit 的运算精度。
1
2
3
4
5
6
7
8
9
10
11
12
13
type Color int
const (
Black Color = iota //iota的初始值为0
Red
)
func test(c Color) {}
func main(){
c := Black
test(c)
x:=1
test(x) //报错
test(1) // 常量会被自动转换
}
  • go 作为编译型语言,不允许重复声明,但有一种特殊情况,即有新的变量被定义,如下
1
2
a := "hellp"
a,b := "hel","world"

选择 循环结构

  • 跟 for 一样,if 语句可以在条件之前执行一个简单的语句 由这个语句定义的变量的作用域仅在 if 范围之内。
  • go range 只有一个接收参数的时候遍历的是 index
  • go 中 range 会复制对象,k,v 都是从复制的对象中取出的,所以 a[k]和 v 实际是两个变量
1
2
3
4
5
6
a := [4]int{1, 2, 3, 4}
for k, v := range a {
a[k] = 4
a[k] = v + 1
fmt.Print(a[k])
}

输出:
2345

  • 控制流 if 支持初始化语句,定义代码块局部变量
1
2
3
if a := 5; a > 4 {
fmt.Println(a)
}
  • 带有 flag 的双重循环,可以实现准层数确的 continue 和 break
1
2
3
4
5
6
7
8
9
10
11
12
13
14
L1:
for x := 0; x < 3; x++ {
L2:
for y := 0; y < 5; y++ {
if y > 3 {
continue L2
}
if x > 1 {
break L1
}
print(x, ":", y, " ")
}
println()
}

数组&&切片

  • make([]T, length, capacity)使用 make 创建切片
  • 切片的 cap 为切片起始位置到底层数组末尾的长度
  • golan 数组传值默认使用值拷贝,但是会造成性能问题,通常会建议使用 slice 或者数组指针
  • 内置函数 len 和 cap 都能返回数组长度
  • reslice 是在 slice 的基础上创建新的 slice 对象,新的对象依旧指向原先底层数组
  • append 是向 slice 尾部添加数据,返回新的 slice 对象,一旦超市原 slice 的 cap 就会重新分配底层数组
  • copy(dst,src)复制 slice,两个 slice 可以指向不同的数组

Map

  • map 查找,类型断言或者通道接受出现在赋值语句右边不一定是产生两个结果,也可能只产生一个结果
  • 支持== !=等操作符 可用作 map 键类型 相等的判断依据是值相等而非指针相等
  • 结构体可作为 map 的 key 和 value
1
2
3
4
5
6
7
8
type people struct {
name string
age int
}
m := map[int]people{
1: {"user1", 10},
2: {"user2", 20},
}
  • golang 结构体作为 map 的元素时,不能够直接赋值给结构体的某个字段。因为在 Go 里面所有的都是值 copy,map[“name”]取值的时候实际上是里面的 struct 的值完整的一个 copy,如果允许了这样的操作 list[“name”].XXX 这样的操作,那么实际上你修改的东西没有复制回原来的 struct,所以你可以采用 map[key]*struct 的定义来进行操作,如下所示是可以正确操作的。
1
2
m := map[string]*A{"a": &A{"hello"}}
m["a"].Name = "Asta"
  • 通过双赋值检测某个键存在:
1
elem, ok = m[key]
  • *[]int 指向数组的指针 []*int 指针数组
  • make 函数可以用于创建 map,预先给 make 函数一个合理的数量参数,有助于提升性能
1
2
3
if v,ok :=m["a"];ok{
println(v)
}
  • 使用 range 迭代 map 不能保证迭代次序

Struct

  • go 结构体以%v 形式打印,直接输出,指针以%v 形式打印,会在结构体{}之前加上一个&
  • go 结构体的几种初始化方式
1
2
3
4
5
6
7
8
type Rect struct {
x, y float64
width, height float64
}
rect1 := new(Rect)
rect2 := &Rect{}
rect3 := &Rect{0, 0, 100, 200}
rect4 := &Rect{width:100, height:200}
  • 注意这几个变量全部为指向 Rect 结构的指针(指针变量),因为使用了 new()函数和&操作符。而如果使用方法,那么则表示这个是一个 Rect{}类型
1
a := Rect{}
  • 内置函数 new 计算类型大小,返回指针,make 会被编译器翻译成具体的创建函数,返回对象
  • 使用 `` 定义不做转义处理的原始字符串,支持跨行
  • 使用 ‘’ 表示 Unicode Code Point 字符常量 如 ‘u6211’ 要修改字符串 可以先转换成 []rune 或者[]byte

Function

  • 命名返回参数可以被同名局部变量遮蔽,此时需要显式返回
1
2
3
4
5
6
func add(x,y int) (z int){
{
var z = x + y
return z //必须显式返回
}
}
  • defer 与 return 执行顺序
1
2
3
4
5
6
7
func add (x,y int)(z int){
defer func(){
println(z)
}()
z = x + y
return z + 200 //执行顺序 (z = z + 200) -> call defer -> ret
}
  • 延迟调用参数在注册时求值或复制,可用指针或闭包延迟读取
1
2
3
4
5
6
7
8
9
func test(){
x, y := 10,20
defer func(i int) {
println("defer",i, y)
}(x)
x += 10
y += 100
println("x=",x,"y=",y)
}

输出:
x=20 y=120
defer: 10 120

  • go 学习笔记的一个错误,method value 并不会复制 receiver,而是指向同一个结构体
  • 延迟调用中引发的错误,可以被后续延迟调用捕获,但是只有最后一个错误可以被捕获
1
2
3
4
5
6
7
8
9
func test() {
defer func() {
fmt.Println(recover())
}()
defer func() {
panic("defer panic")
}()
panic("test panic")
}

interface

  • 空接口没有任何方法签名,这意味着任何类型都实现了空借口,其作用类似面向对象语言中的根对象 object
  • 匿名接口可以用作变量类型,或者结构成员

错误处理

  • go 的错误处理有 panic 和 error 两种方式,当关键流程出现不可修复性错误使用 panic,其他情况使用 error

基本输入输出

  • fmt.Sprintf 返回一个格式化的字符串,但是不输出,fmt.Println 直接输出
  • 命令行程序,必须在使用标志参数对应的变量前调用 flag.Parse(),用于更新每个标识变量的值(之前是默认值)

HTTP

  • HandleFunc func(pattern string, handler func(ResponseWriter, *Request)) 如果你的 pattern 是”/“,那么请求的 URL 都是匹配该函数,如果你请求根目录”/“,那么处理函数将会执行两次

init

当只想执行包的 init 函数的时候,在其在 import 的包名设置为 _,这将会使 go 编译器忽略未引用的错误,但是仍然会执行 init 函数