这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
1.Go语言特性
- 高性能、高并发
- 语法简单、学习曲线平缓
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
2.基础语法
2.2 Hello World
package main //main包
import(
"fmt" //导入标准库的format包
)
func main(){
fmt.Println("hello world")
}
2.3 变量
变量声明一般语法:
var 变量名字 (类型) (= 表达式)
其中“类型”或“= 表达式”两个部分可以省略其中的一个。如果省略的是类型信息,那么将根据初始化表达式来推导变量的类型信息。如果初始化表达式被省略,那么将用零值初始化该变量。 数值类型变量对应的零值是0,布尔类型变量对应的零值是false,字符串类型对应的零值是空字符串
var a string
fmt.Print(a) //""
也可以在一个声明语句中同时声明一组变量,或用一组初始化表达式声明并初始化一组变量。如果省略每个变量的类型,将可以声明多个类型不同的变量(类型由初始化表达式推导):
var a,b,c int //int,int,int
var e,d,f = true,2.3,"four" //bool,float,string
简短变量声明:
以“名字 := 表达式”形式声明变量,变量的类型根据表达式来自动推导。
a := 'aaa'
b := a + "bbb"
fmt.Print(b) //"aaabbb"
因为简洁和灵活的特点,简短变量声明被广泛用于大部分的局部变量的声明和初始化。var形式的声明语句往往是用于需要显式指定变量类型的地方,或者因为变量稍后会被重新赋值而初始值无关紧要的地方。
2.4 if else
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
与python类似,if的条件不用加(),且后面直接加{},不能把if和结果语句写在同一行
2.5 循环
Go中只有for循环,无其他循环
i := 1
for { //无限制条件——死循环
fmt.Println("loop")
break //break跳出
}
for j := 7; j < 9; j++ { //限制条件
fmt.Println(j)
}
for n := 0; n < 5; n++ {
if n%2 == 0 {
continue //continue跳过
}
fmt.Println(n)
}
for i <= 3 {
fmt.Println(i)
i = i + 1
}
/*
loop
7
8
1
3
1
2
3
*/
2.6 switch
与C++类似,不同的是,在搜索到对应的case后即使没有break,它也会直接跳出switch
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two") //找到case后跳出switch
case 3:
fmt.Println("three")
case 4:
fmt.Println("four")
default:
fmt.Println("other")
}
fmt.Println("end") //跳出switch执行此行
/*
two
end
*/
此外,case可用其他类型变量以及条件语句
a := 2
switch { //switch不加变量
case a < 5: //case后加条件语句
fmt.Println("a<5")
default:
fmt.Println("a>=5")
}
fmt.Println("end")
/*
a<5
end
*/
2.7 数组
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("2d:",twoD)
/*
100 5
[1 2 3 4 5]
2d: [[0 1 2] [1 2 3]]
*/
由于数组长度固定,一般比较少用,而多用切片
2.8 切片
make(a,b) :用来创建切片,参数a指定类型,参数b指定长度
s := make([]string, 3) //创建切片
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) //get: c
fmt.Println("len:",len(s)) //len: 3
append() :用来对切片进行添加操作,可一次添加多个元素
s = append(s, "d") //添加1个元素
s = append(s, "e", "f") //添加2个元素
fmt.Println(s) //[a b c d e f]
copy(a, b) :把切片b的内容复制到a
c := make([]string, len(s)) //创建空切片c
copy(c, s) //将s的内容复制到c中
fmt.Println(c) //[a b c d e f]
s[a : b] :获取切片s中下标从a到b的内容
fmt.Println(s[2:5]) //[c d e]
fmt.Println(s[:5]) //[a b c d e f]
fmt.Println(s[2:]) //[c d e f]
2.9 map
map有点类似于python的字典
make(map[a]b) :创建map,a表示key的类型,b表示value的类型
m := make(map[string]int) //创建map,key类型为string,value类型为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(未定义的key,值默认为0)
delete() :删除map的某个键值对
delete(m, "one") //删除m中key为“one”的键值对
fmt.Println(m) //map[two:2]
当某键值对的value为0时,可用两个变量判断其到底是真的值为0还是不存在此键值对
m["zero"] = 0
a, b := m["zero"] //a返回value,b返回true/false来判断是否存在
fmt.Println(a, b) //0 true
c, d := m["unknow"]
fmt.Println(c, d) //0 false
2.10 range
对于数组,可用range返回下标和对应的元素
nums := []int{2,3,4}
for i, num := range nums { //i返回下标,num返回元素
fmt.Println(i,num)
}
/*
0 2
1 3
2 4
*/
对于map,range返回key和value
m := map[string]string{"a":"A","b":"B"}
for k,v := range m {
fmt.Println(k,v)
}
/*
a A
b B
*/
2.11 函数
GO中函数定义时,无论是参数还是返回值,其变量类型都后置
func add(a int, b int) int { //参数a、b为int型,int后置;返回值也为int型,也后置
return a + b
}
func add2(a, b int) int { //同类型的参数可一起定义
return a + b
}
//参数为map型变量和string型变量;返回值为string和bool
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
}
2.12 指针
与C语言相比,GO中指针用法有限,主要用来在函数中修改参数的值
func add(n int) {
n += 2
}
func add2ptr(n *int) { //参数为指针,在类型前加*
*n += 2
}
func main(){
n := 5
add(n)
fmt.Println(n) //5
add2ptr(&n) //调用指针函数时变量前加“&”
fmt.Println(n) //7
}
调用非指针函数返回原本的变量时不会改变变量的值,调用指针函数则会改变
2.13 结构体
类似java、python的类,定义如下:
type user struct {
name string
password string
}
初始化时,若有未赋值的,则为0
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"
结构体也可作函数的参数,也分指针和非指针,指针结构体函数可修改原结构体的元素,也可在某些情况下减少大结构体的开销
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
调用:
fmt.Println(checkPassword(u, "haha")) //false
fmt.Println(checkPassword2(&u, "1024")) //true
结构体方法
类似java、python的类内部子函数,与上面结构体作参数的函数不同的是结构体声明变量提前。同样分指针和非指针,指针可对结构体内部信息作修改。
func (u user) checkPassword(password string) bool {
return u.password == password
}
//利用指针对user内部的password进行修改
func (u *user) resetPassword(password string) {
u.password = password
}
调用:
u.resetPassword("2048")
fmt.Println(u.checkPassword("2048")) //true
2.14 错误处理
调用errors模块进行错误处理
import (
"errors"
"fmt"
)
对于可能出现错误的函数,其返回值加上error;返回error时,若程序未出现错误,返回error为 "nil" (空);若出现错误,将错误信息赋值给error
func findUser(users []user, name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil //没有错误,err赋值为nil
}
}
return nil, errors.New("not found") //有错误则返回错误
}
调用:
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil { //若错误为空,输出错误终止程序
fmt.Println(err)
return
} //错误不为空才能继续执行以下程序
fmt.Println(u.name) //wang 本次程序无错,故直接输出name
有错误的调用:
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) //not found 本次有错,输出错误
return
} else {
fmt.Println(u.name)
}
2.15 字符串操作
Contains(a, b) :字符串a中是否包含b
a := "hello"
fmt.Println(strings.Contains(a, "ll")) //true
Count(a, b) :字符串a中b的数目
fmt.Println(strings.Count(a, "l")) //2
HasPrefix(a, b) :字符串a是否以b为前缀
HasSuffix(a, b) :字符串a是否以b为后缀
fmt.Println(strings.HasPrefix(a, "he")) //true
fmt.Println(strings.HasSuffix(a, "llo")) //true
Index(a, b) :字符串a中b开始出现的下标
fmt.Println(strings.Index(a, "ll")) //2
Join([]a, b) :数组a内的元素以b相连接
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) //he-llo
Repeat(a, n) :将字符串a复制n个
fmt.Println(strings.Repeat(a, 2)) //hellohello
Replace(a, b, c, n) :将字符串a中前n个不重叠的b都换成c,若n<0,则全部替换
fmt.Println(strings.Replace(a, "e", "E", -1)) //hEllo
Split(a, b) :将字符串a按b分隔开
fmt.Println(strings.Split("a-b-c", "-")) //[a b c]
ToLower(a) :字符串a全部变成小写
ToUpper(a) :字符串a全部变成大写
fmt.Println(strings.ToLower(a)) //hello
fmt.Println(strings.ToUpper(a)) //HELLO
len(a) :返回字符串a的长度,a中的中文元素,其长度不为1
fmt.Println(len(a)) //5
fmt.Println(len("你好")) //6
2.16 字符串格式化
通常使用fmt.Printf() 对字符串进行格式化, %v是万能输出,各种类型的变量都可通过它输出,%+v则是比%v更加详细的输出,%#v又是比%+v更详细的输出。 %.nf保留小数后n位
type point struct {
x, y int
}
....
s := "hello"
n := 123
p := point{1, 2}
fmt.Printf("s=%v\n", s) //s=hello
fmt.Printf("s=%v\n", n) //s=123
fmt.Printf("s=%v\n", p) //s={1 2}
fmt.Printf("s=%+v\n", p) //s={x:1 y:2}
fmt.Printf("s=%#v\n", p) //s=main.point{x:1, y:2}
f := 3.1415926
fmt.Printf("%.2f\n", f) //3.14
2.17 JSON处理
导入json模块
import (
"encoding/json"
"fmt"
)
结构体的变量名首字母大写即可使用json
type userInfo struct {
Name string
Age int `json:"age"` //可以在不想大写输出变量名的变量后加上这个
Hobby []string
}
json.Marshal(a)可对a进行序列化,将结构体内容返回成字符串,但输出时要加string()修饰,否则会打印出16进制编码
a := userInfo{"wang", 18, []string{"Golang","Python"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(string(buf)) //{"Name":"wang","age":18,"Hobby":["Golang","Python"]}
序列化后的字符串可以通过json.Unmarshal() 反序列化到空的变量中
var b userInfo
err = json.Unmarshal(buf, &b)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", b) //main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "Python"}}
2.18 时间处理
导入time模块
import (
"time"
"fmt"
)
获取当前时间:time.Now()
now := time.Now()
fmt.Println(now) //2022-05-07 16:09:15.0401635 +0800 CST m=+0.003224801
构造带时区的时间:time.Date()
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
fmt.Println(t) //2022-03-27 01:25:36 +0000 UTC
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) //2022 March 27 1 25
格式化时间:Format(s) ,不用像其他语言那样YYYY-mm-dd,选择一个想要的格式的时间字段s即可
fmt.Println(t.Format("2006-01-02 15:04:05")) //2022-03-27 01:25:36
另一种格式化时间:time.parse(a, b) :把时间b转换成时间a的格式
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) //true
计算时间差:Sub()
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
diff := t2.Sub(t)
fmt.Println(diff) //1h5m0s
//也可以将diff转换成时、分、秒
fmt.Println(diff.Minutes(), diff.Seconds()) //65 3900
时间戳:Unix()
fmt.Println(now.Unix()) //1651911542
2.19 数字解析
导入strconv模块
import (
"strconv"
"fmt"
)
strconv.ParseFloat(s, m) :将字符串s转换成m精度的数字
strconv.ParseFloat(s, n, m) :将代表n进制数字的字符串s转换成m精度的数字,若n=0则自动推测进制
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) //1.234
n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) //111
n1, _ := strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n1) //4096
strconv.Atoi(s) :自动转化s为数字,若转化不了则报错
n2, _ := strconv.Atoi("123")
fmt.Println(n2) //123
n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err) //0 strconv.Atoi: parsing "AAA": invalid syntax
\