后端与 GO | 青训营笔记

139 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天,因为我之前从来没接触过goalng,所以我边做笔记边学习,效果还不错,字节老师讲的也很好,下面便是我今天的收获。

1 Go语言的基础语法

1.1 什么是Go语言

  1. 高性能
  2. 语法简单、学习曲线平缓
  3. 丰富的标准库
  4. 完整的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

1.2 基础语法

1.2.1 变量

  • 自动推导变量类型

var name = value

  • 定义变量类型

var a, b int = 1, 2

var d = true

  • 使用:=

name := value

  • 常量(没有确定的类型。会根据上下文自动确定类型)

const s string = “constant”

const a = 30000

1.2.3 if else

if 7%2 == 0 {
    fmt.Println("7 is even")
}else {
    fmt.Println("7 is odd")
}

注意:不能在同一行,必须加大括号

1.2.4 循环

一个for循环可以实现其他语言所有循环功能,很强大

for {
    fmt.Println("loop")
    break
}

for j := 7; j < 9; j++ {
    fmt.Println(j)
}

for n := 0; n <5; n++ {
    if n % 2 == 0 {
        continue
    }
    fmt.Println(n)
}

i := 1
for i <= 3 {
    fmt.Println(i)
    i = i + 1
}

1.2.5 switch

不需要break,但可以实现break的功能;此外可以替代if else语句,规则是在witch后面直接加大括号;最重要的是变量可以使用任意的类型,例如:字符串,结构体

a := 2
switch a {
    case 1:
        fmt.Println("one")
    case 1:
        fmt.Println("two")
    default:
        fmt.Println("other")
}


t := time.Now()
switch {
    case t.Hour() < 12:
        fmt.Println("before noon")
    default:
        fmt.Println("after noon")
}

1.2.6 数组

数组的操作和其他语言基本类似

var a [5]int
a[4] = 100
fmt.Println(a[4], len(a))

b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b)


var twoD [2][3]int
for i := 0; i < 2; i++ {
    for j:= 0; j < 3; j++ {
        twoD[i][j] = i + j
    }
}
fmt.Println(twoD)

1.2.7 切片

可变长度的数组,类似python的切片,但是没有负数索引

s := make([]string, 3) 
s[0] = "a"
s[0] = "b"
s[0] = "c"
fmt.Println(s[0])
fmt.Println(len(s))

s = append(s, "d")
s = append(s, "e", "f")
fmt.Println(s) //[a b c d e f]


c := make([]string, len(s))
copy(c, s)
fmt.Println(c)//[a b c d e f]

fmt.Println(s[2:5]) //[c d e]
fmt.Println(s[:5]) //[a b c d e]
fmt.Println(s[2:]) //[c d e f]

good := []string{"g", "o", "o", "d"}
fmt.Println(good)

1.2.8 map

3种声明初始化方式

m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m)//map[one:1 two:2]
fmt.Println(len(m)) //2
fmt.Println(m["one"]) //1

r, ok := m["unkone]
fmt.Println(r, ok) //0 false

delete(m, "one")

m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
fmt.Println(m2, m3)

注意:是无序的

1.2.9 range

用来遍历的,会返回两个值,第一个是索引,第二个是值,不想使用索引可以用下划线代替

nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
    sum += num
    if num == 2 {
        fmt.Println(i, num) //0 2    
    }
}
fmt.Println(sum)

m := map[string]string{"a": A, "b": B}
for k, v := range m {
    fmt.Println(k, v) //b B a A 
}
for k := range m {
    fmt.Println(k) //a b
}

1.2.10 函数

返回值类型是后置的,并且可以返回多个值,也说明了为什么很多内置函数,需要两个值来接收,其中一个包含错误信息,这样设计我觉得很好,不用像Java异常那么繁琐

func add(a int, b int) int {
    return a + b
}

func add2(a, b int) int {
    return a + b
}

func exists(m map[string]string, k string) (v string, ok bool) {
    v, ok = m[k]
    return v, ok
} //第一个是value, 第二个是错误信息

func main() {
    res := add(1, 2)
    fmt.Println(res)

    v, ok := exists(map[string]string{"a": A}, "a")
    fmt.Println(v, ok) //A, true
}                                        
    

1.2.11 指针

指针也是golang所具有的,但是老师说在golang里面对指针做了一系列的限制,我还需要挖掘挖掘

func add(n int) {
    n += 2
} //不能完成自增2的需求,因为是函数的内部变量

func add2ptr(n *int) {
    *n += 2
}

func main() {
    n := 5
    add2(n)
    fmt.Println(n) //5
    add2ptr(&n)
    fmt.Println(n) //7
}

1.2.11 结构体

带类型的字段的集合,和类这种概念很类似

type user struct {
    name string
    password string
}

func main() {
    a := user{name: "zheng", password: "123"}
    b := user{"zheng", "123"}
    c := user{name: "zheng"}
    c.password = "123"
    var d user
    d.name = "zheng"
    d.password = "123"
    fmt.Println(a, b, c ,d) //{zheng 123}
    fmt.Println(checkPassword(a, "nihao")) //false
    fmt.Println(checkPassword(&a, "nihao")) //false
}

func checkPassword(u user, password string) bool {
    return u.password == password
}

func checkPassword(u *user, password string) bool {
    return u.password == password
}

1.2.12 结构体方法

相当于Java的成员方法,可以接可以接收指针参数,此外对于方法接收者是值类型时,可以直接用指针类型的变量调用,反过来同样可以;不管接收者是值类型还是引用类型都可以直接使用实例变量调用方法

type user struct {
    name string
    password string
}

func (u user) checkPassword(password string) bool {
    return u.password == password
}

func (u *user) resetPassword(password string) {
    u.password = password
}

func main() {
    a := user{name: "zheng", password: "123"}
    a.resetPassword("456")
    fmt.Println(a.checkPassword("456")) //true
}

1.2.13 错误处理

错误处理是我学过所有语言里面最舒服的一种方式,因为函数返回值可以是多个,所以很nice

type user struct {
    name string
    password string
}

fuc findUser(users []user, name string) (v *user, err error) {
    for _, u := range users {
        if u.name == name {
            return &u, nil
        }
    }
    return nil, errors.New("not found")
}

func main() {
    u, err := findUser([]user{{"zheng", "123"}}, "zheng")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(u.name) //zheng
}    

1.2.14 字符串操作

字符串操作很多,该有的golang都有

a := "hello"
strings.Contains(a, "zheng") //true
strings.Count(a, "l") //2
strings.HasPrefix(a, "he") //true
strings.HasSuffix(a, "llo") //true
strings.Index(a, "ll") //2
strings.Join([]string{"he", "llo"}, "-") //he-llo
strings.Repeat(a, 2) //hellohello
strings.Replace(a, "e", "E", -1) //func Replace(s, old, new string, n int)
strings.Split("a-b-c", "-") //[a b c]
strings.ToLower(a) //hello
strings.ToUpper(a) //HELLO
len(a) //5

1.2.15 字符串格式化

格式化需要注意的是%v可以代替所有,使用起来极其方便,%+v和%#v可以打印出更详细的信息,这个真的很方便

1.2.16 JSON处理

json的处理golang用起来也很顺手,首先只需要定义一个满足格式的结构体,然后进行转化为json,此外json还可以通过解码操作转换回结构体

type userInfo struct {
    Name string
    Age int `json:"age"`
    Hobby []string
}

func main() {
    a := userInfo{Name: "zheng", Age = 21, Hobby = "golang"}
    buf, err := json.Marshal(a)
    if err != nil {
        panic(err) //panic为抛出异常    
    }
    fmt.Println(buf) //[unicode编码]
    fmt.Println(string(buf)) 
    //{"Name": "zheng", "age": 21, "Hobby": "golang"}
}
    //func MarshalIndent(v any, prefix, indent string)([]byte, error)
    buf, err := json.MarshalIndent(a, "", "\t")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(buf))
    
    var b userInfo
    err = json.Unmarshal(buf, &b)
    if err != nil {
        panic(err)    
    }
    fmt.Println("%#v", b)
    //main.userInfo{"Name": "zheng", "age": 21, "Hobby": "golang"}

1.2.17 时间处理

时间处理可太方便了,尤其是格式化转化为时间字符串,其他语言yydd一堆东西,golang只需要它的生日

now := time.Now()
t := time.Date(2023, 1, 15, 17, 52, 20, 0, time.UTC)
t2 := time.Date(2023, 1, 16, 17, 52, 20, 0, time.UTC)
fmt.Println(t) //2023-01-15 17:52:20 +0000 UTC
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute())
//2023 1 15 17 52
fmt.Println(t.Format("2006-01-02 15:04:05")) //按照格式转化为时间字符串
diff := t2.Sub(t)
fmt.Println(diff) //1h
fmt.Println(diff.Minutes(), diff.Seconds()) //60 3600
t3, err := time.Parse("2006-01-02 15:04:05", "2023-01-15 17:25:20")
//字符串按格式转化为时间,前者为格式
if err != nil {
    panic(err)
}
fmt.Println(t3 == t) //true
fmt.Println(now.Unix())时间戳

1.2.18 数字解析

字符串和数字之间的转换,用起来太方便了,直接用函数转化,不像Java那样需要弄一堆函数换来换去

f, _ := strconv.ParseFloat("1.234", 64) //1.234
n, _ := strconv.ParseInt("111", 10, 64) //111
//func ParseInt(s string, base int, bitSize int) (i int64, err error)
//base为进制,0则从字符串前置判断,0x为16 0为8 其他为10
n, _ = strconv.ParseInt("0x1000", 0, 64) //4096
n2, _ := strconv.Atoi("123") //123
//func Atoi(s string) (i int, err error)
//func Itoa(i int) string 字符串转换为整型
n2, err := Atoi("AAA") //报错

1.2.19 进程信息

用os可以获取进行的一系类参数,比如os.Arg就可以用来获取命令行的参数,在打印结果时第一个是自身的路径等信息,第二个就是自己输入的参数

总结

我在基础篇花费的很多时间,因为很多我不熟悉的API和知识点我会去查询官方API文档或者GO圣经,所以效率很慢,一下午才学完第一个视频,但是我收获的也很多,golang和我之前学习的计算机语言有些不同,例如只有for循环、类型名在后等等,是一门我认为很有意思的计算机语言,在基础语法里面的切片中,类似python但是没有负数索引,这一点我有些不明白为什么没有负数索引,后面我思想想想,可能是因为除了数组,其他都是不定长的,所以没必要。 golang最吸引我的地方,就是方法可以有多个返回值,一般都会加上错误信息,这点真的太棒了,使用起来极其舒适,其他地方我还需要慢慢挖掘。

引用文献

golang标准库文档