golang学习笔记

golang学习笔记

  1. go version 查看go的版本号
  2. go env 查看go的环境
  3. 先go build xx.go 然后再./xx就可以输出 先编译在运行 或者go run xx.go 直接运行
  4. 先编译再运行:优点:编译出的可执行文件可以再各个终端上运行,即使没有安装go环境,依然可以运行。原因:编译后,将代码依靠的库文件一起打包到编译好的文件里,所以可以直接运行,并且代码的容量也会变大
  5. go build -o xxx xx.go 将编译出的文件文件名变为xxx
  6. 从main()开始执行,导入的包和使用的变量,如果不使用就会报错不能编译
  7. gofmt -w xx.go 格式化演示 类似cat
  8. golang全部使用utf-8编码,不会有中文乱码,常量用const修饰,定义的时候就必须赋值,a=iota则a为0 ,每使用一个iota,iota的值就会加1
  9. golang 英文字母1个字节(byte可以用来表示一个字节),中文三个字节
  10. bool类型只能用false和true 不能用0和1表示
  11. golang中字符串是不可改变的!一旦赋值就不可变
  12. ``反引号,可以输出一段话类似python的””” “””
  13. 字符串拼接 加号不能开头,要放在末尾(如果一行太多),原因默认会在一行末尾加上;
  14. map返回两个参数 第一个是value,第二个是 是否存在所找key if _,ok := HashMap[target-v];ok 如果true的话就执行
  15. 数据类型:int、float、string、bool、数组、slice切片、map、指针、结构体、channel管道。
  16. Print输出无换行、Println输出有换行、Printf是格式化输出(默认无换行,如果有需要,需要自己加)、Sprintf是格式化传入的数据并赋给变量,终端不显示。Scanf是格式化输入,Scanln直接输入,fmt.Scanln(&nm)获取一行的 fmt.Scanf()格式化获取内容(空格隔开) 获取键盘输入。
  17. golang所有数据类型的转换必须是强制转换,不能自动转换
  18. strconv 函数 可以将其他数据类型转为string类型,Formatint将整数转为其他进制数字,Formatfloat,Formatbool,ParseBool字符串转为bool,ParseXX => ParseInt,ParseFloat
  19. var ptr *int = &num ptr存的是num的地址
  20. 变量、函数名,首字母大写才能被其他包访问 首字母大写是共有的,首字母小写是私有的
  21. %b 二进制输出 0开头 是8进制 0x开头是16进制
  22. math下有一个rand包 rand.Intn(n)生成一个0-n的伪随机数,需要先设置一个随机种子,rand.Seed(time.Now().Unix())根据秒数变化 1970年0:0 Seed(time.Now().UnixNano)根据纳秒变化。
  23. lable2:外面循环 内部循环 lable1: (break lable2) 退出最外层 continue也可以跳到指定标签。
  24. goto lablex ,label :xxx 可以使用但不建议,因为会导致程序执行顺序混乱 跳转执行 中间的代码执行不了
  25. 每个包对应一个文件夹,引用的时候是包名加文件 package 包名 (打包包名)
  26. 包的引用从GOPATH下的src开始找 所以包名路径要从src下开始
  27. 别名 xxx(引入包名) 重命名 将xxx命名为其他别名,原来包名不能再使用
  28. go build -o 重命名 xx/xx/xx/xx/main.go 编译 成重命名 可以直接运行 在不同设备上 只有当package main时才可以打包 这时他会把其他需要的文件一起打包到里面
  29. go函数不支持函数重载 不能同一个函数名有不同参数 生成不同的函数 即函数名不能重复 跟返回值和形参类型无关
  30. 函数是一种变量 可以将函数赋值给变量 可以通过变量名使用 函数本身也可以作为形参传入
  31. 函数支持对返回值命名 func xx (num int,sun int) (adnm int,subnm int) return 就不用再加其他东西
  32. golang 支持可变参数 func sum (nm int, arg… int) int arg是切片(名字可以是其他,…三个点是关键),可以通过下标访问对应的数据例如:arg[0],arg[1]
  33. switch 后的数据类型和case的数据类型需要一样 case后面可以有多个表达式和switch后的表达式匹配 在case语句中 输入fallthrough 可以穿透下一层 x.type()和x = interface{}和switch配合可以看到x空接口指向的类型
  34. init函数在main函数前执行,每个函数都有一个init函数(初始化函数) 变量定义->init->main函数
  35. 匿名函数,一般调用一次,可以使用匿名函数,1、定义时,直接使用,只能调用一次 res := func (a int,b int) int{xxxx}(a,b)。2、匿名函数赋给变量,调用变量可以使用函数
  36. (在一个函数内)闭包是由函数和引用到的变量构成的,在这个闭包里,变量只声明一次,不会重复声明,重复调用闭包会重复更新数据 闭包相当于类
  37. 函数中的defer(延时机制) 在函数执行完毕后,快速释放所有资源。 defer 后面的语句,会进入一个单独的栈(先入后出)同时会将对应的变量数据同时拷贝进栈(深拷贝) 待函数内其他语句执行完后(return 后)【即函数执行完毕后再执行】,再从后往前一个个出栈,弹出对应语句。创建资源后,后面加上defer用于关闭资源, 可以在函数结束后再释放资源,函数内部的资源依然可以继续使用资源
  38. 值传递(深拷贝)基本数据类型:int,float,bool,string,数组,结构体,引用传递(浅拷贝)更快:指针,切片slice、map、管道chan,interface等都是引用类型
  39. 内置函数 len、nm = new(“指针类型”)分配类型,主要分配值类型,nm默认为0,new的两个作用,分配一个空间用来存储值,再分配一个空间用来存储指向值的指针
  40. go中错误处理机制:defer、recover、panic defer:=func(){err = recover() if err!=nil{ fmt.Println(err)}} ()定义一个匿名函数,recover()会接受报错
  41. err = errors.New(“自定义错误”) panic(err) 如果出错,则终止返回自定义错误并退出
  42. 数组是相同类型的集合,大小确定后不能超出范围,不能动态变化 切片:动态变化的数组,slice是数组的引用。var 切片名 []类型 []里不需要写大小,(引用类型,由三部分组成,头指针指向地址,长度,容量)
  43. cap()查看容量,能存储的最大范围
  44. 切片的使用方式:1、定义一个切片,让切片引用指定数组。make创建切片,var xx []类型 = make([]类型,大小,容量),对于切片,必须make后才能使用3、定义切片时,直接赋值,var xx []类型 = []类型 {数据值}
  45. 使用make来创建slice,map,chanel、指针
  46. 切片可以用append进行动态追加,slice2 = append(slice2,待追加的数据)append后必须重新赋值,它并不改变自身数据,(先创建新的数组【将待添加的数据和原来的数据都加到新数组里】,然后再用新的切片引用) ,追加切片:name = append(name,name1…),只能追加切片,不能是数组,必须加上…
  47. 切片是引用类型,在函数实参传给形参时,对数据进行改变,原切片也会改变,因为是引用类型
  48. copy(切片1,切片2),将切片2的数据拷贝给切片1,他们都是独立的空间,拷贝后互不影响,必须都是切片,切片2大于小于切片1的大小都不会报错,大的话,就只取切片1本身的长度得切片2
  49. 字符串底层也是切片,但是和切片不同的是,str不可变,不能用str[0]=”s”改变,切片可以改变,若想改变字符串,可以先转换为切片,然后改变数据后再转为字符串,arr = []byte(str),str = string(arr)【不能处理中文,byte按字节处理数据,而一个汉字占三个字节,所以不能处理中文】或者arr1 = []rune(str),str = string(arr1)【常用】
  50. 二维数组 var name [][]类型,先初始化再赋值,二维数组再内存里是每一行的头代表一个指针,指向这个数组;var name [][]类型 = [][]类型{数组xx1,数组xx2} 二维数组的遍历也可以用for xxxx和for range
  51. map是key-value类型,var name map[key类型]val类型 slice、map、function不可以作为key,不能用==比较。声明map是不会分配内存,只有make后才能赋值和使用 a = make(map[key类型]val类型,大小),key一样的话会后来的会覆盖前面的,key不能重复,是map是无序排列的
  52. map的增加和修改,name[key] = value 即可 map的删除,delete(name,key) 删除,无论有无都不会报错;查找 val,ft = name[key],如果key存在,则ft为true,val为对应key的值;如果key不存在,则ft为false
  53. map切片,map个数动态增加;name := []map[string]string
  54. sort.Ints(切片)对切片进行递增排序,map是引用类型数据,可以自动扩容,map的value经常用结构体表示会很方便
  55. struct可以加个tag标签 序列化struct 用来方便不同语音的交流、反引号json:字段名反引号
  56. 方法作用在数据类型上的,自定义类型都有方法
  57. func (a A) test{} A是结构体,表明test绑定为A结构体的一个方法 test方法只能通过A类型变量调用,其他不能调用 func (xx type) method (形参类型) (返回值) {}
  58. 如果一个方法定义了string方法,在调用fmt.Println时会默认自动调用这个string方法
  59. 只有函数或方法是指针时才能调用指针类型,调用者会发生改变,否则结构体和函数都还是值拷贝。函数时,函数是值类型就只能传递值类型,是指针类型就只能传递指针,方法时,函数是值类型,无论传值或者地址都是值拷贝,传递的值类型,函数是指针类型,无论传值或地址都是引用拷贝,传递的指针类型。
  60. 结构体淡化了指针和普通值,定义后,传递值和指针,编译器默认会加上指针的符号
  61. 封装:设置个Set和Get方法来存储和读取私有信息在不想对外公开的包中,给其他想引用的包流出接口
  62. 继承是通过在子结构体中加入 匿名的父结构体即可
  63. 接口:type xxx interface{ 方法…. } 接口里声明的方法不需要实现,不能使用变量,在其他结构体需要使用时,写出接口的方法,实现接口是指实现全部接口的方法。接口的实现是基于方法的,不需要指定实现哪个接口,只需要实现接口里的方法即可 interface是一个指针,是引用,使用时必须先初始化否则为nil interface{}空接口的话 可以指向任意变量类型
  64. 继承解决代码的复用和可维护性,接口实现代码的解耦,比继承更加灵活
  65. 类型断言,c = a.(b) c是b类型的,a也是b类型的,a是空接口,转为具体类型
  66. os.args 可以获得对应的命令行的内容,以空格隔开,遍历os.orgs 每一个对应的都是以空格隔开的内容 而flag包更常用于解析命令行参数
  67. json:encoding/json包 data,err = json.Marshal(待编码的数据) 返回的是byte切片 json序列化。json反序列化:err = json.unmarshal([]byte(待转换的json字符串),&接受转换的数据类型变量) 反序列化slice、map类型不需要make,因为底层已经make过了
  68. 对于结构体的序列化 结构体标签tag 反引号 json:”name”反引号 相当于重命名 便于接受和传递使用 在声明时使用
  69. 序列化:将数据类型json序列化转为json字符串,反序列化:奖json字符串转为对应的数据类型
  70. 单元测试:确认一个模块结果是否正确 testing测试框架 go test: 定义一个包必须以_test.go结尾,里面写入func TestXxxxx(name *testing T) { t.Fatalf(错误的信息) t.Logf(正确的信息)} go test -v运行测试用例 一个测试文件中可以有多个测试函数 go test命令:正确不输出,错误输出,go test -v命令:正确错误都输出。测试单个文件:go test -v 测试文件名 测试文件函数即可 ,测试单个方法:go test -v -test.run 测试方法名
  71. goroutine:线程,主线程可以有多个协程,协程是轻量的线程 ,go协程特点:有独立的栈空间,共享程序堆空间,调度由用户控制,协程是轻量级的线程。在程序中 调度函数时前面加个go即可(开启了一个协程)
  72. MPG:M是操作系统的主线程(物理线程),P是程序执行时的上下文环境,G是协程。golang设置cpu的个数 导入runtime包,runtime.NumCPU()获取当前逻辑cpu的数量(不一定是实际的),GOMAXPROCS(n)设置最大执行cpu的数量
  73. go build -race 可以查看程序竞争使用的资源数,声明全局互斥锁,lock sync.Mutex 全局的互斥锁,lock.Lock()执行前上锁,lock.Unlock()解锁,管道:channel是线程安全,多个协程操作同一个管道时,不会发生资源竞争,但是管道是有类型的,除非设置空接口,需要使用类型断言,name = 接受变量.(类型),否则不能使用不同类型数据。var 变量名 chan 数据类型,是channel的声明,必须make后才能使用。变量名<- 数据,这是向管道中写入数据,容量确定后是不能改变的,存入的数据不能超过管道的容量,但是可以存入一个取一个,name = <-变量名 取出第一个数据。写入和取出时都会堵塞,有写入就必须要有读。
  74. channel管道关闭后,不能向里面写入数据,但可以继续读取数据,使用close(name)可以关闭。 channel 支持for-range ,for data :=range 管道{},遍历时若channel已经关闭,会正常遍历数据,遍历时若channel没有关闭,会返回deadlock错误。如果容量不够,管道一直写的话,会报死锁错误,可以慢点读,但不能不读。
  75. 管道可以声明为只读或只写,var name chan<- 数据类型,name只能写入,不能读取。var name <-chan 数据类型,name只能读取不能写入。select可以解决不关闭管道中读取数据问题,for{ select { case v := <-name: xxxx case x := <-xxx default:xxxx } } 如果取不到就向下一个case,都取不到就进入default。退出的时候可以使用break label:或者使用return。err :=recover() 可以接收panic错误,这样协程发生错误时,其他线程及主线程可以继续运行不会被迫终止。
  76. 反射 reflect:reflect.Type类型,reflect.TypeOf(变量)获取变量,通过Type可以调用很多方法,操作变量。reflect.Value。reflect.Value和interface{}和变量相互转换。将interface{}转为Value类型,rVal := reflect.ValueOf(b),Value转为interface{}, iVal :=rVal.interface() Kind是类型,Type是类别,类型大于类别,附地址的时候,要用.Elem().Setxxx() .Elem是指向对应地址里对应的值,
  77. redis:远程字典服务器,默认16个数据库。数据类型:字符串、hash、列表、集合、有序集合 go链接redis,导入包后,con = redis.Dial(tcp,ip:端口) 链接目标数据库,con.Do(“set”,xxxx)存,con.Do(“get”,xxx)取:返回的是空接口,redis.String(con.Do(“get”,xxx))可以将对应的数据转换为字符串显示。
  78. 环形链表,数组模拟的花可用取模来存取,(tail+1)%maxsize == head时加满
  79. 冒泡排序: 元素两两比较,只要前一个元素大于后一个元素就对换位置。从首元素到最后元素逐个两两比较进行一轮一轮的排序。

    // [冒泡排序]传入切片,
    func BubbleSort(nums []int, length int) {
        // 外层循环用来控制次数
        for i := 0; i < length-1; i++ {
        // 内层循环用来比较相邻的,并将最大的交换到最后
            for j := 0; j < length-1; j++ {
                if nums[j] > nums[j+1] {
                    nums[j], nums[j+1] = nums[j+1], nums[j]
                }
            }
        }
    }
    
  80. 选择排序:每次选择未进行排序的序列中最小的元素,逐个放在序列的前面。

    // [选择排序]传入切片 切片是引用传递,原地排序
    func SelectSort(nums []int, length int) {
        // 外层循环用来控制次数和确定每个待交换的数
        for i := 0; i < length-1; i++ {
            // 记录假设的最小值 下标和值
            Min_num := i
            Min_size := nums[i]
            // 内层循环找到比假设值小的,则更新记录
            for j := i + 1; j < length; j++ {
                if nums[j] < Min_size {
                    Min_num = j
                    Min_size = nums[j]
                }
            }
            // 将更新的记录和外层待交换的数进行交换
            nums[i], nums[Min_num] = Min_size, nums[i]
        }
    }
    
  81. 插入排序:从序列的第二个元素开始依次遍历整个序列,固定当前被遍历的对象,比较其与其位置左边所有的对象,若有对象比当前固定的对象大,则将该对象后移,直到被固定的对象左边的所有元素都比所固定的对象小为止。

    // [插入排序]传入数组切片 切片是引用传递,原地排序
    func InsertSort(nums []int, length int) {
        // 外层循环,用来控制从第二个元素到最后
        for i := 1; i < length; i++ {
            // 记录当前需要插入的元素
            temp := nums[i]
            j := i - 1
            // 从前面已经排好序的数组找到合适的位置
            for j >= 0 && nums[j] > temp {
                nums[j+1] = nums[j]
                j--
            }
            // 将待插入的元素插入即可
            nums[j+1] = temp
        }
    }
    
  82. 快速排序:首先我们找一个基准数固定,将序列中所有比该基准数小的数都放到该基准数的左侧,将序列中所有比该基准数大的数都放在该基准数的右侧。然后不断左侧递归前述操作,不断右侧递归前述操作。

    // [快速排序]传入切片 原地修改切片数据
    func QuickSort(nums []int, left int, right int) {
        // 存储原来left和right的位置
        l, r := left, right
        // 如果left和right指针指向同一位置或者交叉返回
        if left >= right {
            return
        }
        // 假设待比较的值,在每个数组的第一个
        temp := left
        // 左指针小于右指针的时候,继续比较和交换
        for left < right {
            // 先从右往左找到第一个小于 nums[temp]假定值 的值
            for left < right && nums[right] >= nums[temp] {
                right--
            }
            // 再从左往右找到第一个大于 nums[temp]假定值 的值
            for left < right && nums[left] <= nums[temp] {
                left++
            }
            // 交换左右这两个值
            nums[left], nums[right] = nums[right], nums[left]
        }
        // 此时left和right指向同一个数,将他们指向的数和最开始假定的值交换即可
        nums[temp], nums[left] = nums[left], nums[temp]
        // 然后递归遍历排序左边的数据
        QuickSort(nums, l, left-1)
        // 递归遍历排序右边的数据
        QuickSort(nums, right+1, r)
    }
    
  83. golang二进制bit位的常用操作

    1、golang二进制bit位的常用操作,biu是一个转换二进制显示的库: "github.com/imroc/biu"      
    	
    2、1Byte = 8bit  
    	  
    3、1<<7 相当于 0000 0001 -> 1000 0000 相当于从右往左移动7位(最右边是第一位,最左边是8位,假设1是uint8)  
    	
    4、假设a = (30)00011110 则b := a|(1<<7)b的结果为 (158)1001 1110,使用biu.ToBinaryString(b) 得到1001 1110   
    	
    5、同理 可以左移 << 右移>> ,&与|或^非(a^b)异或,使用他们可以得到某一位想要的值或者得到新值,&1、|0 和他们操作会得到原来自己的数   
    
  84. 向layui传入json数据时,需要满足以下格式,Data传入的数据必须是列表,否则显示错误

    {
    Code: 0,
    Msg: “成功”,
    Count: 10,
    Data: [],
    }

  85. 列表中加入字典的方法,

    // 定义一个空接口列表
    var role_data []interface{}
    	
    // 向接口列表加入数据时,加入字典形式的即可
    role_data = append(role_data, map[string]interface{}{
        "id":       1,
        "username": "超级管理员",
        "status":   true,
        "sort":     5,
        "created":  time.Now().Year(),
        "updated":  time.Now().Month(),
    })
    
  86. iris返回json数据的方法,[ctx iris.context] ctx.json(待返回的数据)函数帮你处理,自己不用额外处理

  87. golang的defer是遵循后进先出原则,即后定义的先执行

  88. 如果defer传入的函数时含参函数,则defer(存在一个列表,定义函数时会传入defer的列表)在定义的时候的同时,会将形参传入,当时就已传入,不会受后进先出再影响,如果是闭包(没有参数传入,直接使用全局或局部变量)是遵循后进先出原则,是在函数结束后再继续执行

  89. defer在return后执行,如果return中有地址的引用,且defer中有对该值的更改,则返回后的,用指针接收的值会变为更改后的值

  90. defer 里的recover可以捕获异常,恢复协程,外部函数执行到panic后会立即跳到defer,不会继续执行panic,而是执行defer里的信息,defer 里 err:=recover(),如果err不为空,则err里的内容为捕获到的异常消息,并且同时不会再报panic错误,但是并不会继续往下执行

  91. go test目的:功能、模糊、性能测试,测试方法:本地模式(指定),列表模式(多目录)

  92. 文件_test.go 功能测试:func TestParseSize(t *testing.T),模糊测试:func FuzzParseSize(t *testing.F), 性能测试:func BenchmarkParseSize(b *testing.B),主函数入口 func TestMain(m *testing.M)

  93. 加锁:加锁的范围越小越好,尽量不要再for循环里加入锁,sync函数,里的Mutex和RWMutex,解决协程安全和数据竞争,尽量避免使用锁(会堵塞,增加运行时间)sync.Map适合读多写少的情况下,通过内部的原子访问和锁机制实现结合的线程安全

  94. 开启协程:wg := sync.WaitGroup{} xxxxx wg.Add(1) go func(){} wg.Wait(); 加入互斥锁 local := sync.Mutex{} local.lock()xxxx local.Unlock();读写互斥锁,读写互斥,写写互斥,local := sync.RWMutex{} 读锁:RLock()RUnlock(),写锁Lock(),ULock();

  95. sync.Pool 创建临时连接池,减少内存分配和降低GC压力,pool长度默认为cpu线程数,在pool中的对象随时可能在不被通知的情况下回收,sync.Once 初始化单例资源,适用于加载一次的场景

  96. Sort函数的使用(sort.Sort())
	sort包中实现了3种基本的排序算法插入排序快排和堆排序和其他语言中一样这三种方式都是不公开的他们只在sort包内部使用所以用户在使用sort包进行排序时无需考虑使用那种排序方式  
	sort.Interface定义的三个方法
	1. 获取数据集合长度的Len()方法
	2. 比较两个元素大小的Less()方法
	3. 交换两个元素位置的Swap()方法
	实现上述三种方法就可以顺利对数据集合进行排序sort包会根据实际数据自动选择高效的排序算法
	type Interface interface {
	    // 返回要排序的数据长度
	    Len() int
	    //比较下标为i和j对应的数据大小,可自己控制升序和降序        
	    Less(i, j int) bool
	    // 交换下标为i,j对应的数据
	    Swap(i, j int)
	}
	任何实现了 sort.Interface 的类型一般为集合),均可使用该包中的方法进行排序这些方法要求集合内列出元素的索引为整数
	
	示例

	type Person struct {
	    Name string
	    Age  int
	}
	
	type ByAge []Person
	//实现了sort接口中的三个方法,则可以使用排序方法了
	func (a ByAge) Len() int           { return len(a) }
	func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
	func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
	
	func Example() {
	    people := []Person{
	        {"Bob", 31},
	        {"John", 42},
	        {"Michael", 17},
	        {"Jenny", 26},
	    }
	
	    fmt.Println(people)
	    sort.Sort(ByAge(people)) //此处调用了sort包中的Sort()方法,我们看一下这个方法
	    fmt.Println(people)
	
	    // Output:
	    // [Bob: 31 John: 42 Michael: 17 Jenny: 26]
	    // [Michael: 17 Jenny: 26 Bob: 31 John: 42]
	}