其他分享
首页 > 其他分享> > Go 语言圣经笔记

Go 语言圣经笔记

作者:互联网

Go圣经

变量

基本类型:数值、字符串、布尔值

引用类型:指针、接口、切片、map、函数、chan

聚合类型:数组、结构体

引用类型的零值为nil

基本类型为其对应的零值

聚合类型的元素或字段为其对应的零值

res,err:=test()
res1,err:=test()//ok
res:=test()
res:=test()//error

整形

字符串

字符串的第ige索引值代表的是第i个字节,第i个字节不一定是第i个字符,因为UTF-8是一种变长的编码方式,对于非ASCII编码可能会使用两个或者更多字节进行编码,可以使用[]rune(string)将字符串转化为unicode码点后进行索引,一个unicode码点为4个字节

func main() {
s := "国hello, world"
fmt.Println(string(s[0]), string([]rune(s)[0])) // å 国
}


常量

golang也可以进行类似枚举的操作(只能是常量),使用iota,从0开始递增

var (
A int = 1 << iota
B
C
D
)


func main() {
// var a Vex
fmt.Println(A, B, C)
}


golang许多常量可能并没有一个明确的基础类型,在赋值时会进行尝试转换

import "fmt"


func main() {
var a float64 = 3 + 0i
fmt.Printf("类型:%T,值:%[1]v\n", a) // 类型:float64,值:3
a = 20
fmt.Printf("类型:%T,值:%[1]v\n", a) // 类型:float64,值:20
a = 'a'
fmt.Printf("类型:%T,值:%[1]v\n", a) // 类型:float64,值:97
}

复合数据类型

数组

func test(arr []int) {
arr = append(arr, 3)
arr[0] = 3
fmt.Println(len(arr), cap(arr)) // 3 4
}
func main() {
arr := make([]int, 2, 4)
test(arr)
fmt.Println(arr[:3])            //[3 0 3] 共享同一底层数组,扩容后可以看见添加的值
fmt.Println(len(arr), cap(arr)) // 2 4
}

所以平常可以这么使用,避免一些不必要的麻烦(覆盖原始数据之类的)

func test(arr []int) []int {
arr = append(arr, 3)
arr[0] = 3
return arr
}


func main() {
arr := make([]int, 2, 4)
arr = test(arr)
fmt.Println(arr) // 2 4
}

map

函数

可变参数

golang支持可变参数,类型必须匹配

func test(v ...int) {
for _, value := range v {
fmt.Println(value)
}
}
func main() {
test(12, 3, 3)
arr := []int{5, 6, 7}
test(arr...)
}

匿名函数

golang中也要注意循环闭包带来的引用同一变量的问题,类似JavaScript

Recover

golang异常可以使用recover()恢复,但是不要滥用,在该恢复的地方进行恢复

func main() {
defer func() {
if p := recover(); p != nil {
fmt.Println("调用recover可以恢复异常,注意应该只恢复应该恢复的panic")
}
}()
log.Panic("异常")
log.Println("啊啊啊啊啊")
}

接口

包和工具

包的重命名

import (
"crypto/rand"
"fmt"
mrand "math/rand"
)

测试

创建test目录,为file包创建file_test.go文件,test文件测试函数必须以Test开始。调用go test -v进行测试。demo:

func TestSum(t *testing.T) {
if Sum(2, 3) != 5 {
t.Error("错误:2+3!=5")
}
if Sum(4, 6) == 8 {
t.Error("错误:4+6=8")
}
}


go test ./...可以进行递归测试
测试覆盖率查看

  1. go test -v -coverprofile=cover.out
  2. go tool cover -html=c.out

第二部是转化为html查看

反射

反射可以帮助我们在运行时更新变量或者检查他们的值,调用方法和内在操作等,interface{}需要在运行时确定类型,但是如果不是确定其动态类型并且调用断言的话,是不能访问其内在的值和方法的,但是反射提供了这种机制。

package main


import (
"fmt"
"reflect"
)


func test(data interface{}) {
t := reflect.TypeOf(data) // 返回reflect.Type
fmt.Printf("%q\n", t)     // 打印时会调用String方法


v := reflect.ValueOf(data) // 返回reflect.Value类型
fmt.Printf("%q\n", v)
fmt.Println("---------", v.Kind() == reflect.String) // 判断类型,不能与string比较,因为其是无类型string


t = v.Type() // 调用Type 方法返回对应具体类型的reflect.Type
fmt.Printf("%q\n", t)


//  reflect.ValueOf(data)的逆操作,返回interface{}类型的具体值
// 与reflect.Value不同的是空接口隐藏了值的内部表现和方法(什么都
// 做不了),只有知道具体的动态类型时才能使用断言访问内部值。相反
// reflect。Value有很多方法检查其内容
i := v.Interface()
fmt.Printf("%q\n", i.(string))
}


func main() {
test("字符串")
}


通过反射修改值

package main


import (
"fmt"
"reflect"
)


func main() {
x := 3
a := reflect.ValueOf(&x).Elem() // 获取可取地址的Value,reflect.ValueOf()返回的是值的拷贝
fmt.Println(a.CanAddr())        // 可以使用该方法判断是否可取地址
/*
Addr() 返回reflect.Value 保存指向变量的指针
随后取Interface + 断言即可通过普通指针更新变量
*/
// a.Addr().Interface().(*int)


// 或者调用可取地址的reflect.Value的Set方法更新值
a.Set(reflect.ValueOf(4))
// 简便写法
a.SetInt(6)


y := struct{ field int }{10}
// 可以获取未导出字段,但是不能更改
field := reflect.ValueOf(&y).Elem().FieldByName("field")
// 判断是否可更改
fmt.Println(fd.CanAddr(), fd.CanSet()) // "true false"


}


获取结构体字段标记

type resp struct {
Code int    `http:"responseNo"`
Msg  string `http:"responseMsg"`
}




func main() {
res := resp{10000, "成功"}


v := reflect.ValueOf(&res).Elem()


fmt.Printf("字段有:%d个\n", v.NumField())          // 获取字段数
fmt.Println(v.Type().Field(1).Tag.Get("http")) // responseMsg
}

调用方法

reflect.Typereflect.Value 都实现了reflect.Mehtod,通过Call调用方法,传入参数为[]reflect.Value类型,返回值同样为[]reflect.Value类型

// 必须要是导出方法才能获取
func (r resp) Fn() int {
return 123
}
func (r resp) fn() int {
return 123
}
func main() {
res := resp{10000, "成功"}
v := reflect.ValueOf(res)             // 值类型的reflect.Value 则获取方法的时候获取接受者为值的方法
fmt.Printf("有%d个方法\n", v.NumMethod()) // 有1个方法


// reflect.Type 和reflect.Value 均实现了reflect.Method
t := v.Type()
fmt.Printf("有%d个方法\n", t.NumMethod()) // 有一方犯法


// Call 调用方法,调用方法传入的参数必须为[]reflect.Value 返回也是[]reflect.Value
// 调用Interface 返回interface{},值是具体值
fmt.Println(v.Method(0).Call(nil)[0].Interface().(int))
}

goroutines和channels

基于共享变量的并发

goroutine与线程的区别

  1. 具有动态栈
    线程运行时会分配一个大约2MB左右(一般语言中)的内存栈用于存储线程运行期间,函数内部的变量等,但是如果每个goroutine都分配这么大的内存的话有些浪费,goroutine一般分配内存大小为2kb左右,并且可以动态改变,最大可达1GB
  2. 调度开销小
    线程的调度会通过内核函数完成,golang具有自己调度器,使用内部实现的结构进行调度
  3. 没有特定标识(屏蔽了)
    避免滥用

标签:arr,圣经,int,fmt,笔记,reflect,test,func,Go
来源: https://www.cnblogs.com/cphacker/p/13878340.html