Go语言快速上手 | 青训营笔记

266 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

本堂课重点内容

  • Go语言简介
  • Go语言基础语法
  • Go语言实战

1. Go语言简介

1.1 Go语言的优势

  • 高性能、高并发
  • 语法简单、学习曲线平缓
  • 丰富的标准库:功能丰富且兼容性强并支持高并发的标准库
  • 完善的工具链:代码检查、编译、包管理、内置完善的单元测试框架(性能测试及优化)
  • 静态链接:仅需要保留一个可执行文件,无需像C那样添加大量的.so文件或者Java那样附加一个大体积的jre
  • 快速编译:大量微服务可实现时间小于1分钟的增量编译
  • 跨平台:Linux,Windows,Macos,路由器,树莓派
  • 垃圾回收:让用户专注于业务开发

2. Go语言基础语法

2.1 部署开发环境

a. 安装Golang

b. IDE

  • VScode + Go插件
  • Goland

c. 基于云的开发环境

2.2 基础语法

2.2.1 常量与变量

三种声明方式

var a = 1
var b int = 2
c := 3  // 第一和第三种自动推断类型

const country = "China"  // 自行通过上下文推断

2.2.2 if-else

if condition {
    ...
}else if{
    ...
}else{
    ...
}

注意点:

  • 条件无需加括号
  • if及后续代码块不能在一行
  • else if 与第一个代码块的右花括号要在同一行,else同理

2.2.3 循环

Go只有for一种循环

for i:=1; i < n; i++{
    ...
}

Go也具有continue和break关键字

当循环与fmt.Scanf("%d", &i)一起使用时,需要额外吃掉一个换行符,否则后续循环会出现
unexpected newline,原因是换行符留在了缓冲区,如果不清理掉,下次程序将尝试将换行符赋予整型变量导致出错

2.2.4 switch

除了像C或C++那样的用法,Go中的switch还支持不写变量,直接在case处写条件表达式,有点像sql中的case when

switch variable {
    case value1:
        ...
    case value2:
        ...
    ...
    default:
        ...
}

switch {
    case condition 1:
        ...
    case condition 2:
        ...
    ...
    default:
        ...
}

2.2.5 数组

import "fmt"

var a[5]int

a[1] = 10

b := [3]int{1, 2, 3}

// 二维数组
var twoD [3][4]float

// 遍历
for i:=1; i < len(a); i++{
    ...
}

// 打印
fmt.Println("arr: ", a)

2.2.6 切片

// 分配了长度为3,容量为5的切片空间
s := make([]int, 3, 5)

// append操作
s = append(s, 1)

// 索引操作
fmt.Println(s[1:])  // 打印从第一个位置往后的所有元素
fmt.Println(s[:4])  // 打印第四个位置前的所有元素
fmt.Println(s[1:4])  // 打印从第一个位置到第四个位置前的所有元素

// 初始化
well := []string{"w", "e", "l", "l"}

注意点:

  • 切片原理是存储了一个数组(指针)、当前长度与容量,当容量不足的时候,此时使用append操作会进行扩容并返回新的切片对象,因此需要使用原始切片对象接收append函数的返回值
  • 不像Python,Go中的切片不支持负数索引
  • 切片索引规则:左闭右开,如arr[1: 4],实际上是获取[1, 4)的元素

2.2.7 map

Python中称为dict

// 初始化
m := make(map[string]int)  // map[键类型]值类型{初始化值}
mm := map[string]int{"third": 3, "forth": 4}

// 赋值
m["first"] = 1

// 访问元素
res, ok := m["second"]  // 0 false(map中不存在该k-v对)
res, ok = m["first"]  // 1 truy(map中存在该k-v对)

// 删除元素
delete(m, "first")

注意点:

  • Go中的map是完全无序的,既不会按字母表顺序输出,也不会按插入顺序输出,而是随机顺序

2.2.8 range

快速遍历slice或map

nums := []int{1, 2, 3, 4}
sum := 0
for idx, item := range nums{
    sum += item
}

m := map[string]int{"a": 1, "b": 2}
for k, v := range m{
    ...
}

注意点:

  • 遍历切片是两个返回值的索引,元素值
  • 遍历字典是则是键值对

2.2.9 函数

func main(){
    res, err := divide(4, 2) // 2, nil
    res, err = divide(4, 0) // 0, error(""denominator can not be 0")
}

func divide(numerator int, denominator int)(res int, err, error){
    if denominator == 0{
        return 0, errors.New("denominator can not be 0")
    }
    return numerator / denominator, nil
}

注意点:

  • Go中没有try catch概念, 通常由函数的第一个值作为返回值,第二个值作为错误信息

2.2.10 指针

相比于C与C++中的指针,Go中的指针支持的操作有限,其中一个主要作用是对传入的参数进行修改

2.2.11 结构体

image.png

type user struct{
    name string
    password string
}

// 声明结构体的方法,类似类成员方法
func (u user) checkPassword(password string){
    return u.password == password
}

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

func main(){
    u := user{name: "keith", password: "0512"}
    u.resrtPassword("1024")
    u.checkPassword("1024")  // true
}

注意点:

  • Go中的结构体可以进行json格式化,下面会进行说明
  • Go中的结构体可以使用中括号进行初始化,没有初始化的字段值,会以该字段的默认值进行初始化
  • 函数中传入结构体指针,可以避免大体积结构体的拷贝开销
  • 结构体方法如果声明了传入的对象为指针类型,则说明该方法可以对结构体进行修改,反之则不能

2.2.12 错误处理

Go中没有try catch概念, 通常由函数的第一个值作为返回值,第二个值作为错误信息
--from 2.2.9

由于该特性,Go中的错误可以通过if-else结构来处理,

func main(){
    res, err := divide(4, 2) // 2, nil
    if err != nil {
        log.Printf("divide function happened error: %w", err)
        return
    }
    log.Printf("divide success, res = %f", res)
    res, err = divide(4, 0) // 0, error(""denominator can not be 0")
    if err != nil {
        log.Printf("divide function happened error: %w", err)
        return 
    }
    log.Printf("divide success, res = %f", res)
}

2.2.13 字符串处理

常用的字符串处理函数包括:

  • Contains(str string, target string) bool: 判断字符串str是否包含目标字符串
  • Count(str string, target string) int: 统计字符串str中目标字符串出现的次数
  • HasPrefix(str string, target string) bool: 判断字符串str是否以目标字符串开头
  • HasSuffix(str string, target string) bool: 判断字符串str是否以目标字符串结尾
  • Index(str string, target string) int: 返回目标字符串在字符串str第一次出现的索引,不存在返回-1
  • Join(strs []string, connection string) string: 使用connection连接strs里的所有字符串
  • Repeat(str string, times int) string: 将str字符串重复times次,并返回
  • Replace(source string, old string, new string, n int) string: 将字符串source中的old替换成new,n指定次数,n=-1时不限制修改次数
  • Split(str string, connection string) []string: 将str字符串以connection为分隔符切分
  • ToLower(str string) string: 转换成小写
  • ToUpper(str string) string: 转换成大写
  • len(str string) int: 返回字符串长度

image.png

字符串格式化
常用标识:

  • %v:打印任意类型的变量
  • %+v: 打印详细结果
  • %#v:打印进一步详细的结果 image.png

2.2.14 JSON处理

import (
    "encoding/json"
    "fmt"
    "strings"
)

type user struct{
    Name string `json: "name"`
    Password string `json: "password"`
    StudentId string `json: "student_id"`
}

func main(){
    u1 := user{Name: "keith", Password: "1024", StudentId: "2048"}
    buf, err := json.Marshal(u1)
    if err != nil {
        panic(err)  // 抛出错误
    }
    fmt.Println(string(buf))  // {"name":"keith","password":"1024","student_id":"2048"}
    
    buf = strings.Replace(buf, "1024", "4096", -1)
    var u2 user
    err = json.Unmarshal(buf, &u2)  // 注意传入指针,需要修改属性
    if err != nil {
        panic(err)  // 抛出错误
    }
    fmt.Printf("%v\b", u2)  // {Name:"keith",Password:"4096",StudentId:"2048"}
}

注意点:

  • 将需要json序列化的结构体中的属性命名成公开字段,即第一个字母大写
  • 可以为每个字段指定json tag,表示该字段在序列化后的字段名

2.2.15 时间处理

标准库:time
常见用法:

image.png

2.2.16 数字分析

标准库:strconv

常用函数

  • ParseInt(s string, base int, bits int) int :将字符串解析成bits位的base进制的整型。base=0时由编译器自动推断,其他的ParseXXX函数作用类似
  • Atoi(s string)(int, error):将一个十进制字符串转换为整型,当输入不合法时,返回一个错误
  • itoA(num int)string:将整型转换为字符串

2.2.17 进程信息

标准库:os, os/exec

基础用法

image.png

3.实战

3.1 猜数字游戏

注意点:

  • 当循环与fmt.Scanf("%d", &i)一起使用时,需要额外吃掉一个换行符,否则后续循环会出现 unexpected newline,原因是换行符留在了缓冲区,如果不清理掉,下次程序将尝试将换行符赋予整型变量导致出错 --from 2.2.3

  • 每次执行程序前需要修改随机数种子,否则每次生成的随机数不变。涉及代码:rand.Seed(time.Now().UnixNano())

3.2 在线词典

使用到的工具:

  • curlconvertor(curlconverter.com/go/):用于从浏览器…
  • json2go(https:/oktools.netoo/json2go):用于将复杂的json响应字符串解析成固定结构的Go结构体,配合上Go标准库的json.Marshal,能够轻松地解析http响应体

注意点:

  • 请求结构体DictRequest,需要为每个字段标注json tag,使其转换成json后字段名与API匹配
  • 获得响应时,应该先判断状态码,再继续下一步操作
  • 某些API返回的中文是unicode编码的,json.Marshal还帮我们将其编码成UTF-8

3.3 代理服务器

这个程序说明会在下一篇笔记中