这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
1 GO语言上手
1.1 什么是go语言
-
高性能、高并发
- 内建高并发编程
- 不需要第三方库,只需要标准库或者基于标准库
-
语法简单、学习曲线平缓
- 类似C语言,但是大量简化
-
丰富的标准库
- 和python一样,有很高稳定性、兼容性
-
完善的工具链
- 编译、代码格式化、错误检查、帮助文档、包管理、代码补充提示
- 内置完整单元测试框架,单元测试、性能测试、代码覆盖率、数值竞争检测、性能优化
-
静态链接
- 所有编译结构默认静态链接、拷贝编译之后唯一一个可执行文件,不需要附加
- 线上容器环境下运行,镜像控制的非常小、部署方便快捷
-
快速编译
-
跨平台
- 交叉编译,无需配置环境
-
垃圾回收
- 无需考虑内存的分配释放,专注业务逻辑
2.2 基础语法
helloworld
package main
import (
"fmt"
)
func main() {
fmt.Println("hello world")
}
- package main:文件属于main的一部分,main包也就是程序入口包
- import:导入包
- fmt:屏幕输入输出字符串、格式化字符串
变量
package main
import (
"fmt"
"math"
)
func main() {
var a = "initial"
var b, c int = 1, 2 //自己写变量类型
var d = true
var e float64
f := float32(e) //一种
g := a + "foo"
fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
fmt.Println(g) // initialapple
const s string = "constant"
const h = 500000000
const i = 3e20 / h
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
- 强类型语言,跟python一样,可以先声明类型
- 字符串可以加号拼接、=比较
- const为常量,没有确定的类型(自己决定),不可更改
if-else
package main
import "fmt"
func main() {
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
- if没有括号
- num:=9 等于var num=9
循环
- 只有for循环,空为死循环
- 经典:
for i:=1 ; i<10; i++
switch
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
- switch变量不需要括号,可以使用任意一种类型变量
- 不需要加break也可以自己跳出去
数组
- 数组长度固定、切片更实用
b := [5]int{1, 2, 3, 4, 5}var twoD [2][3]intvar a [5]int
切片
- 可变长度
s := make([]string, 3),长度可变append(s,”s“)追加元素copy复制切片len(s)长度- 和python一样的切片操作
map
m := make(map[string]int)r, ok := m["unknow"]fmt.Println(r, ok) // 0 falsemap是不是有key存在- 随机顺序
range
- 数组快速索引,第一个数组为索引,不想用的话可以和python一样用下划线
for i, num := range nums {for k, v := range m {- 遍历map第一个值为key,第二个为value
函数
- 天生支持返回多个值
func add(a int, b int) int {- 括号内为局部变量参数、后面一个是返回数值类型,支持多个
func exists(m map[string]string, k string) (v string, ok bool) {
指针
- 作用:对传入的参数进行修改
package main
import "fmt"
func add2(n int) { //没有意义,传入的参数是一个拷贝
n += 2
}
func add2ptr(n *int) { //加*
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n) //C语言
fmt.Println(n) // 7
}
结构体
- 带类型的字段的集合
- 没有初始化的为空值
- 指针可以对结构体进行修改
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
结构体方法
package main
import "fmt"
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: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // true
}
- func后面加(变量名,结构体类型)
- 需要修改的话,结构体类型要变为指针(前面加*)
错误处理
package main
import (
"errors"
"fmt"
)
type user struct {
name string
password string
}
func 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{{"wang", "1024"}}, "wang")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) // not found
return
} else {
fmt.Println(u.name)
}
}
- 返回值加上error
- 没错返回nil,错误返回
errors.New("not found") u, err := findUser([]user{{"wang", "1024"}}, "wang")
字符串操作
strings.Contains(a, "ll")字符串是否包括llstrings.Count(a, "l")字符串l的数量strings.HasPrefix(a, "he")判断字符串s是否以prefix开头strings.HasSuffix(a, "llo")判断字符串s是否以suffix结尾strings.Index(a, "ll")字符串ll所在的起始位置strings.Join([]string{"he", "llo"}, "-")字符串用-组合strings.Repeat(a, 2)字符串重复strings.Replace(a, "e", "E", -1)字符串前n个e替换为E,-1代表没有限制strings.Split("a-b-c", "-")字符串按照-分割strings.ToLower(a)字符串全小写strings.ToUpper(a)字符串全大写len(a)字符串长度ReverseStr(str1)字符串反转
字符串格式化
- 可以用%v任意打印
- +v可以更详细的打印字段的名字,值
- #v可以打印出字段的名称和值(包括结构体的结构和名称)
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}fmt.Printf("%.2f\n", f) // 3.14.f保留小数点两位浮点数
JSON处理
-
结构体需要字段名的第一个字母大写
-
buf, err := json.Marshal(a)对结构体json序列化,会变成一个字符串数组(string类型转换) -
buf, err = json.MarshalIndent(a, "", "\t")Marshal的基础上****前缀不为空,****Indent()对读的结果做了一些处理,简单说就是对Json 多了一些格式处理。 -
err = json.Unmarshal(buf, &b)json反序列化到结构体(注意结构体变量要取地址)
时间处理
time.Now()快速获取当前时间t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)构造时间t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()取参数t.Format("2006-01-02 15:04:05")按照所需格式进行标准化t2.Sub(t)返回两个时间相差的时间段*。*time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")now.Unix()时间戳time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")字符串解析成时间
数字解析
strconv.ParseInt(字符串,进制(0为10),返回的数值精度)f, _ := strconv.ParseFloat(字符串, 返回的数值精度)strconv.Atoi("123")快速把十进制字符串转成数字- _为error信息
进程信息
os.Args命令行参数os.Getenv("PATH")获得环境变量os.Setenv("AA", "BB")buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()快速启动子进程获取输入输出
实战
猜数游戏
secretNumber := rand.Intn(maxNum)系统随机数(),不设置随机数种子,不然默认种子都是一样的rand.Seed(time.Now().UnixNano())加上一个随机数种子reader := bufio.NewReader(os.Stdin)读取用户输入input, err := reader.ReadString('\n')获取输入字符串input = strings.TrimSuffix(input, "\n")TrimSuffix取空格_, err := fmt.Scanf("%d", &guess)一行解决取数字,去除上面所有的烦恼