Go学习笔记

1. byte与rune 字符串

byte 占用1个字节,8个bit,范围为0->255(uint8)
rune 占用4个字节,32个bit,范围为0-2^32(int32)(字符集大,中文必须使用这个)

func main() {
    var a byte = 'A'
    var b rune = 'B'
    fmt.Printf("a 占用 %d 个字节数\nb 占用 %d 个字节数", unsafe.Sizeof(a), unsafe.Sizeof(b))
}
//1 4

字符串
一个英文字符占1个字节,一个中文字符占3个字节

//表示 \r\n 这个字符串
//解释型表示法
var str string = "\\r\\n"
//原生型表示法(不会根据转义字符解析)
var str string = `\r\n`
//使用%q还原原生型表示法
fmt.Printf("的解释型字符串是: %q", str)

2. go指针

创建方法

  • 定义变量取内存地址
a:=1
p=&a
  • 创建指针分配内存后,再给指针指向的内存写入值
str:=new(string)
*str="test"
  • 声明指针变量,从其他变量取地址并赋值
a:=1
var b *int
b=&a

& 在普通变量中取内存地址
* 该符号在赋值操作符的右边时,表示在指针变量在取得变量值,在左边是,是指指针指向的变量
打印内存地址

fmt.Printf("%p", p)
fmt.Println(p)

指针具有类型,比如string,int,*float64
指针的零值为nil
指针和切片都是引用类型,改变一个数组的值时,有两个方法:

  • 传入数组切片(推荐使用)
func modify(nums []int) {
    nums[0] = 90
}
func main() {
    nums := [3]int{89, 90, 91}
    modify(nums[:])
}
  • 传入数组指针
func modify(nums *[3]int) {
    (*nums)[0] = 90
}

func main() {
    nums := [3]int{89, 90, 91}
    modify(&nums)
}

3. select用法

func main() {
    c1 := make(chan string, 1)
    c2 := make(chan string, 1)
    timeout := make(chan bool, 1)

    go makeTimeout(timeout, 2)

    select {
    case msg1 := <-c1:
        fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
        fmt.Println("c2 received: ", msg2)
    case <-timeout:
        fmt.Println("Timeout, exit.")
    }
}
//一直阻塞,导致超时
  1. select 只能用于 channel 的操作(写入/读出/关闭),而 switch 则更通用一些;
  2. select 的 case 是随机的,而 switch 里的 case 是顺序执行;
  3. select 要注意避免出现死锁,同时也可以自行实现超时机制;
  4. select 里没有类似 switch 里的 fallthrough 的用法;
  5. select 不能像 switch 一样接函数或其他表达式。

4. go异常机制

触发panic

panic("crash")

recover(),捕获panic信息并打印

defer func() {
        // recover() 可以将捕获到的panic信息打印
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

即使 panic 会导致整个程序退出,但在退出前,若有 defer 延迟函数,还是得执行完 defer 。
defer 在多个协程之间是没有效果,在子协程里触发 panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer 函数的。
panic:抛出异常,使程序崩溃
recover:捕获异常,恢复程序或做收尾工作

5. 结构体

结构体可以继承

type Person struct {
    Name string
}
type Man struct {
    person
    age string
}

结构体中,属性名大写为Public,小写为Private
实例化方法:

  • 正常实例化
    p:=Person{name:"xxx"}
    
  • 使用new
    p:=new(Person)
    p.name="xxx"
    
  • 使用&
    var p *Person=&Person{}
    p.name="xxx"
    //等同于(*p).name="xxx"
    

从一个结构体实例对象中获取字段的值,通常都是使用 . 这个操作符,该操作符叫做 选择器。
可以直接省去 * 取值的操作,选择器 . 会直接解引用

空结构体

type person struct{}

空结构体没有任何属性,不占用空间,size为0

6. 结构体tag

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    Addr string `json:"addr,omitempty"`
}

person结构体中Addr字段有omitempty属性,使用encoding/json转会为字符串时,如果该属性为空则会被忽略
获取Tag

field := reflect.TypeOf(obj).FieldByName("Name")
field := reflect.ValueOf(obj).Type().Field(i)  // i 表示第几个字段
field := reflect.ValueOf(&obj).Elem().Type().Field(i)  // i 表示第几个字段
tag:=field.Tag
// 获取键值对
labelValue := tag.Get("label")//个体是对lookup的封装,如果为空则返回空字符串
labelValue,ok := tag.Lookup("label")

7. 类型断言

  • 检查是否为nil
  • 检查是否为某个类型
    t:=i.(T)
    //断言失败会触发panic
    t,ok:=i.(T)
    //断言失败不会触发panic
    
    var k interface{} // nil
    t, ok := k.(interface{})
    //这里断言失败,但会继续执行
    
    Type Switch
    	switch x := i.(type) {
    	case int:
    		fmt.Println(x, "is int")
    	case string:
    		fmt.Println(x, "is string")
    	case nil:
    		fmt.Println(x, "is nil")
    	default:
    		fmt.Println(x, "not type matched")
    	}
    

8. 空接口interface{}

接口包含两个属性,值和类型,而空接口都为nil

var i interfacece{}
fmt.Printf("type: %T",value: %v",i,v)
//type: <nil>, value: <nil>

用法:

  • interface{}作为类型声明,可以承载任何类型的值
    var i interface{}
    i=1
    i=false
    
  • 让函数接收任何类型的参数
    func test(if intrface{}){}
    接收任意个任何类型的参数
    func test(ifs ...interface{}){}
    
  • 定义一个接受任何类型的array,slice,map,struct
    any:=make([]interface{},3)
    any[0]=1
    any[2]=false
    

空接口可以承载任何类型的值,但不代表任何类型的值可承接空接口的值
空接口承载数组和切片后,该对象无法再进行切片
使用空接口接收任意类型的参数时,静态类型是interface{},但动态类型并不知道,需要使用类型断言

9. golang反射

  • 反射可以将接口类型变量转换为反射类型对象
    reflect.TypeOf(i)//获得接口类型的值,类型为*reflect.rtype
    reflect.ValueOf(i)//获得接口值的值,类型为reflect.Value
    
  • 反射可以将反射类型对象转换为接口类型变量
    i := v.Interface()
    i := v.Interface().(int)
    
  • 如果要修改反射类型对象其类型必须是可写的
    要想具有可写性:
    • 创建反射对象传入变量的指针
    • 使用Elem()函数返回指针指向的数据
    func main() {
    var name string = "Go编程时光"
    v1 := reflect.ValueOf(&name)
    fmt.Println("v1 可写性为:", v1.CanSet())
    
    v2 := v1.Elem()
    fmt.Println("v2 可写性为:", v2.CanSet())
    }
    
  1. 通过反射获取类型Kind(),基础的分类,比如struct(类型为main.Profile)

  2. 类型转换

    var age int = 25
    v1 = reflect.ValueOf(age)
    v2 := v1.Int()
    

    Int() :转成 int
    Float():转成 float
    String():转成 string
    Bool():转成布尔值
    Pointer():转成指针
    Interface():转成接口类型

  3. 对切片的操作
    Slice(),对切片再切片,返回reflect.Value 反射对象
    Slice3():对切片再切片(三下标)
    Slice3():对切片再切片(三下标)

  4. 对属性的操作
    NumField() 属性数量和 Field(i)获得属性

  5. 对方法的操作
    NumMethod() 方法数量 和 Method()获得方法

  6. 动态调用参数

    type Person struct{}
    func (p Person)Say(){}
    func (p Person)SayHello(text string){}
    p:=&Person{}
    v:==reflect.ValueOf(p)
    v.MethodByName("Say").Call(nil)//无参调用
    v.MethodByName("SayHello").Call("hello")//有参调用
    

10. 静态类型和动态类型

静态类型:变量声明时的类型
动态类型:程序运行时系统中的类型
interface组成:
type+date
interface

  • iface 带有方法的接口
  • eface 不带方法的接口

11. make和new函数

  • make 用来创建slice,map,chan类型,make返回类型的本身而不是指针,返回值也依赖于具体传入的类型,因为是引用类型,所以没必要返回指针
  • new 分配内存 设置零值 返回指针

12. 包管理

//单行导入
import "fmt"
//多行导入
import (
	"fmt"
	"math"
)
//别名
import mrand "math/rand"
//点操作,将该包函数视为本包函数
import . "fmt"
//匿名导入
import _ "fmt"

包的初始化,调用包时,会执行该包的init函数,优先于main函数,包引用链中,会递归init执行
同一个文件,可以有多个init函数,init函数不能有入参和返回值,且所有init函数都会执行,但不能保证执行顺序
包的匿名导入:只会执行init函数,不会报错
相对导入:在\(GOPATH/src或\)GOROOT或$GOPATH/pkg/mod导入
相对导入:在当前目录搜索并导入
import导入的时路径不是包

import "../../module"

13. 包导入优先级

  • 使用govendor
    1. 从项目根目录的vendor查找
    2. 从$GOROOT/src中查找
    3. 从$GOPATH/src中查找
  • 使用gomodules
    1. 如果导入的带域名,在$GOPATH/pkg/mod中查找,找不到去该网站查找
    2. 不带域名则去$GOROOT查找
    3. vcendor文件夹,无论是否有域名,都只会在vendor中查找

14. 寻址

可寻址:变量,指针,数组元素索引,切片,切片元素索引,组合字面量
不可寻址:常量,字符串,函数,基本类型字面量,符合字面量,map中的元素,数组字面量

15. 内存分配

内存分配

16. 泛型

func myFunc[K comparable, V int64 | float64](m map[K]V) V {
    return res
}
  • K,V 泛型别名,作用于在函数内
  • comparable go预声明的类型,是可比较,可哈希的类型集合,通常用于定义map中的key
posted @ 2022-04-20 08:31  流光之中  阅读(94)  评论(0编辑  收藏  举报