第一课 GO语言基础
这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记。
1. go 语言程序的示例
package main
import (
"fmt"
)
func main() {
fmt.Println("hello world")
}
- package main 表示这个文件输入main的一部分,main包是程序的入口包
- 第三行import导入了标准库的fmt包,用于和屏幕交互,格式化输入输出。
- 在vscode中,鼠标悬浮在函数上可以看到函数说明。
2. Go语言的变量
- Go是强类型语言,每一个变量由自己的类型。
2.1 GO的内置变量(待补充TBD)
-
字符串 string
- 比如可以用等于号比较两个字符串
- 用加号进行拼接
-
整数
-
浮点数
-
布尔类型
2.2 变量的声明
有两种方式:
- var 变量名 类型名称 (= 值)
- 变量名 := 值
// 使用var
var a = 'initial' // 程序自动推导变量类型
var b, c int = 1, 2 //显式的声明为int
//使用 := 符号进行变量的声明
g:= a + ' go'
2.3 常量的声明
- 使用 const 去声明一个东西为常量
- 常量是没有特定的类型的,编译器自动确定
3. 条件语句
3.1 if-else
和C++类似,区别在于:
- 条件不需要括号
- 条件之后的内容一定要有大括号
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")
}
}
3.2 switch语句
与C++不同的不同点:
- switch后面的变量不需要括号
- 不需要break也会自动终止
- switch甚至不需要变量,在case中写条件分支
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a { //与C++不同,不需要括号
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")
}//与C++不同,不需要break, 不会运行到最后的
t := time.Now()
switch { //与C++不同,go可以使用switch来取代if else
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
4. 循环语句(for)
- go语句只有一种循环语句就是for
- 没有while, do while
- for和C++一样的三段式
- 区别在于,三段式需要括号,但是语句需要大括号
- 三段式的每一段都是可选的(待确定TBD)
package main
import "fmt"
func main() {
i := 1
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)
}
for i <= 3 {
fmt.Println(i)
i = i + 1
}//形成了while
}
5. 数组
5.1 数组的定义
- 数组是一个 有编号 且 长度固定 的元素序列
- 优点:根据索引存储值
- 缺点:长度固定
package main
import "fmt"
func main() {
var a [5]int
a[4] = 100
fmt.Println("get:", a[2])
fmt.Println("len:", 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("2d: ", twoD)
}
5.2 数组的赋值(TBD)
- 一维数组的赋值方式
- 二维数组的赋值方式
- 未被赋值的数组,默认值是什么
- 什么类型的才支持数组?
6. Slice
6.1 Slice的定义
- 使用make来定义一个slice
- make函数的多种用法?(TBD)
package main
import "fmt"
func main() {
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) // c
fmt.Println("len:", len(s)) // 3
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) // [g o o d]
}
// 还可以使用 make([]int, 3, 10)来定义一个函数。这里定义的是容量为10但是当前长度只有3的slice。
6.2 Slice支持的操作
- 切片的追加:Slice = append()
- 切片存储了:长度、容量、指向数组的指针
- 因此如果超过容量,会扩容并且返回新的Slice
- 切片式取值(和python一样)不支持负索引
- copy(dst_slice, src_dlice) 从一个slice中向另一个slice拷贝
7.map
- map可以理解为哈希表或者是字典
- map是 无序的, 遍历的时候是随机顺序的
7.1 map的定义
- 可以使用花括号进行列表初始化
package main
import "fmt"
func main() {
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
fmt.Println(m["unknow"]) // 0
r, ok := m["unknow"]
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)
}
7.2 map支持的操作
- 直接获取键值对;使用 "r, ok := m["unknow"]" 两个变量,如果键不存在与map中,r=0, ok=false
- 删除键值对:delet(字典名称, 键值)
8. range操作
- range可以用来遍历slice以及map
- 返回两个值,一个是索引,另一个是值
- 如果不需要某个值,可以用下划线
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num) // index: 0 num: 2
}
}
fmt.Println(sum) // 9
m := map[string]string{"a": "A", "b": "B"}
for k, v := range m {
fmt.Println(k, v) // b 8; a A
}
for k := range m {
fmt.Println("key", k) // key a; key b
}
}
9. 函数
- 返回值后置(正如变量的类型名后置)
- 支持返回多个值
- 常用的业务代码一般都是返回两个值,第一个是真正需要的值,第二个是错误信息
package main
import "fmt"
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
}
func main() {
res := add(1, 2)
fmt.Println(res) // 3
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok) // A True
}
10. 指针
- 指针和C++一样的
- 但是操作非常有限,用于指针传参
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)
fmt.Println(n) // 7
}
11. 结构体
11.1 结构体初步
- 结构体是带类型的字段的集合
- 结构体在作为函数的参数时,支持指针,这样可以支持在函数中修改结构体的值,以及减少拷贝大结构体的开销
初始化:
- 键值对初始化
- 列表初始化
- 部分初始化的话,别的字段会默认初始化
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
}
11.2 结构体方法
- 实现结构体方法只需要在func之后,函数名之前,用括号传入结构体。
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
}
12.错误处理(使用errors包)
- 在函数返回时,习惯使用一个单独的返回值来返回错误信息
- 使用错误返回时,需要在函数的参数加上一个error的类型变量来作为参数
- 在返回时:
- 如果没有错,则在应该返回错误变量的地方返回nil
- 如果出错了,则在原本返回值中返回nil,而返回错误变量的地方使用error对应的参数的方法
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)
}
}
小练习:抓包
1. 找到响应 并且获取 curl
- 打开彩云翻译 fanyi.caiyunapp.com
- 然后输入一个单词
- 然后右键、检查、网络
- 在网络的包中找到POST,对api.interpreter.caiyunai.com/v1/dict的
- 可以在有边找到响应
- 如果我们要仿照这个东西去发数据,可能比较麻烦。
- 所以我们复制curl命令
- 然后打开网站curlconverter.com/#go,把东西转换为g…
2.把生成的东西写进代码
但是为什么我的是bad request?
3. json与go的转换
- 网址: oktools.net/json2go