这是我参加青训营的第 1 天
Go语言简介
1. 什么是Go语言
- 高性能、高并发
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
2. Go语言的使用
2.1 开发环境
2.1.1 安装Golang
安装网址:
- go.dev/
- goproxy.cn 这是国内镜像,打开和下载的速度会很快
2.1.2 配置集成开发环境
目前使用最方便的是VSCode,这里介绍VSCode的下载方法和其中的Go语言插件安装
VSCode网址:code.visualstudio.com/
VSCode内部Go插件安装:
搜索之后,对第一个插件进行安装。
然后Win + r 打开命令行窗口,依次输入
"set GO111MODULE=on"
"set GOPROXY=https://goproxy.cn"
将下载代理换成国内镜像,然后编写Go语言文档的时候对提示要安装的软件进行全部安装即可。
2.2 基础语法
Hello World
package main // 代表属于main包的一部分
import ("fmt") // fmt 包主要是向屏幕输入输出字符串
func main() {
fmt.Println("Hello world")
}
运行语句
"go run 文件名.go"
变量
package main
import (
"fmt"
"math"
)
func main() {
var a = "initital"
var b, c int = 1, 2
var d = true
var e = float64
f := float32(e)
g := a + "foo"
const s string = "constant"
const h = 50
const i = 3e2 / h
}
Golang变量声明的时候,系统会对没写变量类型的变量进行自动变量判断,例如:var a = 1,这里的变量a会被自动赋值整型。
Golang还可以使用” := “的方式来声明变量。
Golang常量使用”const 变量名 = 变量值“的格式声明,其中常量没有具体的类型,系统会根据上下文来自动确定常量的类型。
if else
package main
import "fmt"
func main() {
if 1 == 2 {
fmt.Println("1")
} else {
fmt.Println("3")
}
}
在Golang的if语句中,if后面不会使用括号,而是对条件进行直接书写,若是写了括号,编译器会自动将括号去掉。
For 循环语句
package main
import "fmt"
func main() {
i := 1
for {
fmt.Println(i++) // 死循环
}
for i; i < 3; i++ { // 条件循环
fmt.Println("3")
}
}
Golang中,For循环也和if语句相同,都没有括号
Switch
package main
import "fmt"
func main() {
i := 1
switch i {
case 1:
fmt.Prinln("1")
case 2:
fmt.Prinln("2")
default:
fmt.Prinln("other")
}
t := time.Now()
switch {
case t.Hour() < 1:
fmt.Prinln("1")
default:
fmt.Prinln("other")
}
}
Golang中的Switch与C++ 中的最大区别就是,Golang中不需要每条执行语句后面加break,系统会自动进行结束。
Switch可以支持很多类型的条件,可以完美取代if的位置
数组
package main
import "fmt"
func main() {
var a [3]int // 一维数组定义
a[1] = 10
var b [3]int{0, 1, 2} // 数组定义以及赋值
var twoD [2][3]int // 二维数组定义
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
}
切片
package main
import "fmt"
func main() {
s := make([]string, 3)
s[0] = "a"
s = append(s, "b")
c := make([]string, len(s))
copy(c, s)
fmt.Println(s[2:5]) // 输出结果为2-4,不包括5
}
切片比数组更具有操作空间,因为数组为定长的,切片长度可以任何时间改变。
使用make定义数组,格式:make([]类型,长度)
使用append对数组进行数据追加,格式:append(数组名,数据,数组位置) ,当数组长度不够时,会对数组进行扩容,并且将扩容的数组返回原数组。
使用copy进行数组之间的复制,格式:copy(A,B) ,将B的数据复制给A
map
package main
import "fmt"
func main() {
m := make(map[string]int)
m["one"] = 1
r, ok = m["two"]
e, ok = m["one"]
fmt.Println(r, ok) // 0 false
fmt.Println(e, ok) // 1 true
delete(m, "one)
}
map中的数据完全无序
map使用make进行定义,格式:make(map[k的类型]value的类型)
map使用delete进行数据删除,格式:delete(数组名,下标)
map可以使用变量名,ok来判断数组中是否有该下标
range
package main
import "fmt"
func main() {
nums := [3]int{2, 3, 5}
for i; num := range nums { // i为下标,num为对应下标的内容
fmt.Println("index:", i, "number:", num)
}
m := make(map[string]int){"one": 1, "two": 2}
for k; v := range m {
fmt.Println("key:", k, "value:", v) // one: 1; two: 2
}
}
range功能为遍历数组
函数
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func exists(m map[string]int, k string) (v int, ok bool) {
v, ok = m[k]
return v, ok
}
func main() {
m := make(map[string]int){"one": 1, "two": 2}
v, ok := exists(m, "one")
fmt.Println(v, ok) // 1 True
}
Golang与其他语言不同的是,定义函数的时候,不管是参数还是函数本身,类型都是后置的。
Golang函数在后续项目应用的时候,都会返回两个值,一个是原生值,另一个是错误信息
指针
Golang初步支持指针,但是对于其它语言中的指针,Golang的指针使用方法甚少。
Golang指针主要的用途主要就是对传入的参数进行修改
package main
import "fmt"
func add(n *int) {
*n += 2
}
func main() {
n := 5
add(&n)
fmt.Println(n) // 7
}
结构体
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
a := user{name: "wang", password: "123"}
b := user{"wang", "123"}
fmt.Println("%#v\n", user)
}
func checkP(u user, password string) bool {
return u.password == password
}
func checkP(u *user, password string) bool {
// Golang的编译器会自动给u加*
return u.password == password // u为指针,u.为成员变量
}
使用结构体可以完成对内容的判断,还可以使用结构体指针对大结构体内容进行拷贝,节省额外的不必要的开销
%#V该语句会用最详细的方式来打印结构体
结构体方法
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() {
m := user{"wang", "1025"}
m.resetPassword("2044")
fmt.Println(checkPassword("2044")) // True
}
错误处理
package main
import "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)
}
}
JSON处理
基本的JSON类型有数字(十进制或科学记数法)、布尔值(true或false)、字符串,其中字符串是以双引号包含的Unicode字符序列,支持和Go语言类似的反斜杠转义特性,不过JSON使用的是\Uhhhh转义数字来表示一个UTF-16编码(译注:UTF-16和UTF-8一样是一种变长的编码,有些Unicode码点较大的字符需要用4个字节表示;而且UTF-16还有大端和小端的问题),而不是Go语言的rune类型。
这些基础类型可以通过JSON的数组和对象类型进行递归组合。一个JSON数组是一个有序的值序列,写在一个方括号中并以逗号分隔;一个JSON数组可以用于编码Go语言的数组和slice。一个JSON对象是一个字符串到值的映射,写成一系列的name:value对形式,用花括号包含并以逗号分隔;JSON的对象类型可以用于编码Go语言的map类型(key类型是字符串)和结构体。
将一个Go语言中类似movies的结构体slice转为JSON的过程叫编组(marshaling)。编组通过调用json.Marshal函数完成
为了生成便于阅读的格式,另一个json.MarshalIndent函数将产生整齐缩进的输出。该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进
编码的逆操作是解码,对应将JSON数据解码为Go语言的数据结构,Go语言中一般叫unmarshaling,通过json.Unmarshal函数完成,在这里我们可以选择性地解码JSON中感兴趣的成员
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golong", "TypeScript"}}
buf, err := json.Marshal(a) // 编组
if err != nil {
panic(err)
}
fmt.Println(buf) // 十六进制数据
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":[~]}
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/n", b) // main.userInfo{"Name":"wang","age":18,"Hobby":[~]}
}