后端GOLang笔记1|青训营

100 阅读6分钟

记录了mac上go的下载方式和语言特点,并主要讨论了go的基本语法

参考材料:青训营课件,刘丹冰课件(补充),阅读贴:空值零值空结构(juejin.cn/post/689523…

Installation (for Mac):

GO pkg path == /usr/local/go/bin

检查是否下载完成 -> go version 检查版本

go env (检查go环境)

echo $GOPATH (得出 GOPATH="/Users/annadai/go")

cd $GOPATH (进入gopath)里存了src, bin, pkg

Doc: go.dev/doc/install

语言特点

  1. 直接编译成机器码
  2. 不依赖其他库,直接运行
  3. 是静态类型语言: 编译时通过go build main.go检查隐藏问题
  4. 能够充分利用多核,通过go关键词高效实现协程
  5. 强大标准库:
  • runtime 系统调度机制: 垃圾回收,优化
  • 高效GC垃圾回收
  • 丰富的标准库包
  1. 内嵌C语法,但目前没办法取代c
  2. 面向对象程序特征(继承,多态,封装)
  3. 跨平台运行

Best Applicable Tasks

  1. 云计算基础设施 e.g. docker
  2. 后端服务(数据相关的?

基本语法

运行程序

  1. go build hello.go -> 编译; 这里很像c里的gcc hello.c -o hello
  2. ./hello -> 运行; 这里和c一样,用./executable 运行程序

OR go run hello.go -> 编译并运行

一个简单的main

package main //这里跟java程序有点像
//import "fmt"
import (
    "fmt"
    "time"
)

func main(){ //函数名必须和{在同行
    fmt.Println("hi!您好!我不好") //;可加可不加
    time.Sleep(1 * time.Second) 
}

变量Declaration

单个变量

var a int //使用default值

var a int = 100 //设定初始值和dtype

var a = 200 //设定初始值,自动识别dtype

a := 500 //:= 既初始化又赋值,自动匹配dtype
fmt.printf("Type is %T", a) //should be int

BUT! 注意,除了用:=的声明方式,其他都能用在函数外定义全局var

多个变量

var b,c int = 1,2 //如果同一个dtype的话可以加上dtype
var d,e = 5, "hehe" //自动识别dtype

//多行定义
var (
    f bool = True
    g int = 123
)

constant var

package main
import "fmt"

//枚举
const (
    aa = iota * 5 //aa = 0
    bb // = 5
    cc // = 10
)
const (
    a, b = iota+1, iota+2
    c, d
    e, f
    
    g, h = iota*2, iota*3 //可以换formula,但iota照常累计,所以这里iota = 3
)

func main(){
    const x int = 10
    //a = iota 这样的写法是不允许的
}

const()中可以添加iota关键词:第一行默认值为0且每行+1 (iota有点像内置的一个++的for loop)

不同dtype以及零值空值空结构

printf, sprintf 等%v参考:pkg.go.dev/fmt

static type := int, str..

concrete type := interface指向的具体dtype,或者系统看得见的

default值:

值类型: bool -> false; 数值 -> 0; str -> ""; array等 -> 递归初始化元素

引用类型ptr, function, interface, slice, channel, map) -> nil

nil: 可以用来返回错误,但要注意 nil != nil 问题 (使用error接口+不要初始化从函数返回的空错误变量以避免这个问题)

** a := nil 是错误的,因为nil没有type

空结构:如type Q struct{}

特点:0 byte (即使互相套用空结构)

由于不占空间,所以两个不同大小的变量可能有一样的地址

用途:

  1. 代替chan bool将传递goroutines的信号表达得更清晰
  2. 在struct里添加 _ struct{} 以防止unkeyed 初始化结构

error

有内置的error package,专门处理error信息

通过errors.New(<error message>)来初始化

func和func返回值的不同写法

func命名方式:大写开头 -> public;小写开头 -> private

parameter: 先名字再dtype

func boo(a string, b int) (int, int){ //匿名return
    r1 := 1
    r2 := 2
    return r1, r2
}

func boo2(a string, b int) (r1 int, r2 int){ //有名称的return,直接return即可
    r1 = 100 //已经initialize了dtype,所以不能用:=
    r2 = 200
    return 
    //也可以写成return 100, 200
}
//OR
func boo3(a string, b int) (r1, r2 int){
    r1 = 100
    r2 = 200
    return 
}

关于import

init函数和import流程

新建folder和相应go程序来创建package

main里import一个package时,go会先import package里的package (so on if there are more),再执行package里的init -> init()的执行优先级比main()高

*注意import时的路径是基于gopath的

别名和匿名别名

匿名 _ "package1": 无法使用包里的方法,但由于import流程的因素会执行包里的init()

别名 pkg2 "package2": 通过别名.func来调用func (类似python里的import package2 as pkg2)

. "package3" -> 将package3的全部func导入当前程序,调用pkg3里的函数不需要package3作为前缀 (不建议使用,容易引起混淆)

因此,go语言中,如果有import但不使用的package,是会报错的 -> 可以通过起匿名来解决报错

pointer, double pointer (C学过所以不多做笔记)

pass-by-value vs pass-by-reference

& *

defer

defer := func结束之前出发的机制

  • 次数不限制
  • 触发顺序类似于stack (last in first out)
func main(){
    defer func1()
    defer func2()
    defer func3() //运行顺序为3->2->1
}
  • return的执行优先级 > defer

make和new

  • make用于map, slice, 和channel;但new适用于各种类型的内存分配
  • new会在分配内存之后返回一个ptr
  • map, slice, 和channel在被make初始化之前,为nil值(之后也会反复强调)

slice

array vs slice

array :=

  • 固定长度
  • 不同长度的array是不同的dtype (ex. [10]int[3]int是不一样的)
  • 作为parameter的时候是pass-by-value
var array1 [10]int
array2 := [10]int{1,2,3,4} //除了前四个值都是0
fmt.printf("Type is %T", array1) //should be [10]int

slice :=

  • 类似于c里的dynamic array
  • 不同长度的slice是同一个dtype
  • 作为parameter的时候是pass-by-reference(不是完全的pass-by-value但是会复制ptr的address,所以还是能改变原有值的?)
  • 空值为nil
//四种slice的declaration方式
slice1 := []int{1,2,3,4}

var slice2 []int //空值,不占内存
//slice2[0] = 1 这样写不对,因为slice此时没有任何数
slice2 = make([]int,3) //往slice2里塞三个0

var slice3 []int = make([]int, 3) //初始化并declare slice3有3个空间,都为0

slice4 := make([]int, 3)

Note: make有点类似c里的malloccalloc,但make仅仅是填充

cap

当目前的已用的空间(长度)不等于容量时,slice内部有个ptr指向已用空间的结束处

var num = make([]int, 3, 5) //5指的是当前最大容量
fmt.Printf("len = %d, cap = %d, slice = %v\n",len(num),cap(num),num) //should be "len = 3, cap = 5, slice = [0,0,0]"

append

注意:当使用append对slice进行追加值的时候,如果长度超过容量,则容量自动*2;但不会对原来的数值或者引用的数值造成影响

num = append(num,1) //往num里面insert一个int1

slicing

跟python的slicing有点像:

  • s[int a, int b]等同于s[a,b)
  • :截取全部char
  • 默认a为0,默认b为len(s)
  • slicing后的var和被slice的var指向同一个address -> 如果改了一个值,两个都会变
  • copy函数可以拷贝值(不同address的新var)
s := []int{1,2,3}
s1 := s[:,2]
s1[0] = 555 //如果s1变了值,s也会跟着变

//copy函数
s2 := make([]int,3)
copy(s2,s) //将s的值拷贝到s2

map

  • 类似于dictionary
  • default值为nil
  • 使用前必须通过make分配使用空间,不然报错

3种declaration

var map1 map[string]int //key是str, value是int
map1 = make(map[string]int, 10) //使用前需要给map分配使用空间

map2 := make(map[string]int)

map3 := map[string]int"me":1,
    "you":2,
    "she":3,
)

常见func

map := make(map[string]int)

//add & edit
map["me"] = 1
map["you"] = 2

//delete
delete(map,"me")

//for loop
for key,value := range map{
}