这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
go语言=高性能高并发+简化语法(仅保留for循环)+python标准库+工具链+编译静态快速+跨平台+java垃圾回收
学习思路:与java/C等语言进行对比学习
1. 从hello world开始
package main//main包的一部分,程序的入口包(入口文件)
import "fmt"//导入fmt包,作用:格式化输入输出字符串
func main() {//main函数
fmt.Println("Hello World")
}
命令行操作
go run main.go //直接运行
go build main.go //先编译
./main //再运行
2. 变量声明
显式声明,其中类型和“=表达式”可省略其一。如果省略的是类型信息,那么将根据初始化表达式来推导变量的类型信息。如果初始化表达式被省略,那么将用零值初始化该变量,不会出现变量未初始化的问题。 数值类型变量对应的零值是0,布尔类型变量对应的零值是false,字符串类型对应的零值是空字符串,接口或引用类型(包括slice、指针、map、chan和函数)变量对应的零值是nil。数组或结构体等聚合类型对应的零值是每个元素或字段都是对应该类型的零值。
var 变量名字 类型 = 表达式 //注意类型后置
隐式声明,以“名字 := 表达式”形式声明变量,变量的类型根据表达式来自动推导,只可用于函数内部
常量 使用const关键字定义,根据使用的上下文来自动确定类型
3. 判断和循环
- 判断条件没有括号,大括号不可省略
3.1 if…else…
- Go中的if语句具有自动类型转换特性, 也支持在条件判断语句前执行简单的语句
3.2 循环 for
- 没有循环条件即为死循环,break和continue用法不变
3.3 switch
- 无需break默认单分支执行;
- switch语句中的变量可以是任何类型,在每个case后面可以跟多个值,用逗号隔开
- 支持在条件判断语句前执行简单的语句,这样就可以在一个switch语句中处理多个条件
- 在switch中不加判断条件,而在case中写条件分支,这样可以处理多个条件,优于多重if…else…嵌套
4. 数组与切片
4.1 数组
- 固定长度
4.2 切片 slice
- 可理解为可变长度的数组,是一个指向底层数组的指针,包含三个属性:指针、长度和容量
- 如果切片操作超出cap(s)的上限将导致一个panic异常
- 超出len(s)则是意味着扩展了slice,因为新slice的长度会变大
- 使用关键字make创建//直接赋值
- 通过内置函数append()动态增加元素,结果需要赋值回原slice
var slice_name []type = array_name[start_index : end_index] //切片的语法格式
make([]type, len, cap)//make创建格式,其中cap一般省略,注意内存的变化
months := [...]string{1: "January", /* ... */, 12: "December"}//直接赋值
months = append(months, "lunar month") //追加元素
5. map(哈希/字典)
- 无序性
- make创建
- delete删除
- 第二个返回参数是错误信息,表示是否存在该键值对
m := make(map[string]int) //创建空map
m2 := map[string]int{"one": 1, "two": 2}//创建且赋值map
r, ok := m["unknow"] //使用索引运算符检索特定键的值
fmt.Println(r, ok) //0 false
delete(m2, "one") //使用delete函数从map中删除键值对
6. range关键字
- 遍历数组、切片、字符串、map 和通道中的元素
- 对于数组、切片和字符串,它将返回每个元素的索引和值
- 对于 map,它将返回每个键和值
- 在 map 中遍历时,顺序是不确定的。如果需要以特定顺序遍历 map,可以先将键存储在切片中,然后使用该切片遍历键
- 对于通道,它将返回每个从通道中接收到的值。
for index/key/_, value := range array/m {
// do something with index/key/_ and value
}
for value := range channel {
// do something with value
}
7. 函数
- 定义
- 可赋值
- 可作参数/返回值 即函数式编程
func functionName(parameterName type) returnType {
// function body
}
result := apply(func(x int) int {return x*x}, 2)
fmt.Println(result) // output: 4
8. 指针
- & 取地址运算符
- * 指针间接寻址运算符
- 如果希望在函数中更改变量的值,需要使用指针;对比C的形参实参/值传递引用传递内容
- 不支持指针运算(如指针加法或指针减法),也不支持指针比较。
使用指针可以节省内存,因为在传递大的数组或结构体时只需要传递指针,而不是整个数组或结构体;可以让我们在不同函数之间共享数据,从而提高代码的可重用性。
9.结构体
- type和struct关键字定义
- 赋值时默认顺序绑定
- 访问结构体字段使用点号(.)
9.1 结构体方法
结构体可以定义方法,这些方法可以通过结构体实例调用。
//定义格式如下
//其中receiverType是接收器的类型,通常是结构体类型
//receiverName是接收器的名称,在方法中可以通过这个名称访问结构体的字段
func (receiverType receiverName) methodName(args) returnType {
// method body
}
//实例
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: "小余", password: "2002"}
fmt.Println(a)//修改前:{小余 2002}
a.resetPassword("6666")
fmt.Println(a)//修改后:{小余 6666}
fmt.Println(a.checkPassword("6666")) //确认密码是否为6666,结果返回布尔值true
}
10. 错误处理
Go 提供了一种简单方法来处理错误,即将错误作为函数的返回值。
在 Go 中,错误是一个内置的 error 类型,该类型实现了 error 接口。错误发生时,函数返回一个 error 值,可使用panic函数进行处理。
(可对比java异常处理)
11. 字符串
11.1 常用操作
package main
import (
"fmt"
"strings"
)
func main() {
a := "hello"
//判断该字符串中是否包含另一个字符串
fmt.Println(strings.Contains(a, "ll")) //true
//字符串计数
fmt.Println(strings.Count(a, "l")) //2
//接受一个字符串和一个前缀作为参数,并判断该字符串是否以该前缀开头
fmt.Println(strings.HasPrefix(a, "he")) //true
//接受一个字符串和一个后缀作为参数,并判断该字符串是否以该后缀结尾
fmt.Println(strings.HasSuffix(a, "llo")) //true
//查找字符串的位置
fmt.Println(strings.Index(a, "ll")) //2
//连接多个字符串
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) //he-llo
//重复多个字符串
fmt.Println(strings.Repeat(a, 2)) //hellohello
//替换字符串的某一部分
fmt.Println(strings.Replace(a, "e", "E", -1)) //hEllo
//以指定字符分割字符串
fmt.Println(strings.Split("a-b-c", "-")) //[a b c]
//字符串转换小写
fmt.Println(strings.ToLower(a)) //hello
//字符串转换大写
fmt.Println(strings.ToUpper(a)) //HELLO
b := "你好"
//获取字符串的长度,一个中文对应多个字符
fmt.Println(len(b)) //6
}
11.2 格式化
最常用的方法是使用fmt 包
格式化字符串中的占位符可以是 %d(number), %s(strings), %.2f(float), %v(any variance) 等等
在Go语言中,%+v 和 %#v是 fmt包中使用的两个格式化动词,可以输出变量的值。
%+v会输出详细的值信息,包括结构体字段名称和值,对用户更友好%#v会输出详细的值信息,包括类型和值, 此处值的输出格式是 Go 代码。
11.3 数字解析
strconv包是 Go 语言标准库中用于字符串与数字之间转换的包,它提供了许多函数来实现字符串和整数、浮点数、布尔值之间的转换
一些常用的函数如下:
ParseFloat(s string, bitSize int) (float64, error): 将字符串转换为浮点数ParseInt:将字符串类型转换为整型Atoi(s string) (int, error): 将字符串转换为整数Itoa(i int) string: 将整数转换为字符串FormatFloat(f float64, fmt byte, prec int, bitSize int) string: 将浮点数转换为字符串ParseBool(s string) (bool, error): 将字符串转换为布尔值
12. JSON处理
Go语言使用标准库中的encoding/json包进行JSON处理,它提供了编码和解码JSON数据的函数和类型。
json.Marshal函数将 Go 值编码为 JSON 数据json.Unmarshal函数将 JSON 数据解码为 Go 值。
13. 时间处理
Go语言中使用time包处理时间。
Go语言中的时间以纳秒为存储单位
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
//获取当前时间
fmt.Println(now) //2023-01-16 21:27:24.793331 +0800 CST m=+0.000122361
//构造指定时间
t := time.Date(2023, 1, 16, 10, 0, 0, 0, time.UTC)
t2 := time.Date(2023, 1, 16, 23, 59, 59, 59, time.UTC)
fmt.Println(t) //2023-01-16 10:00:00 +0000 UTC 2023
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) //2023 January 16 10 0
//格式化时间
fmt.Println(t.Format("2006-01-02 15:04:05")) //2023-01-15 10:00:00
diff := t2.Sub(t) //Sub:对两个时间做一个减法(计算时间差)
fmt.Println(diff) //13h59m59.000000059s
fmt.Println(diff.Minutes(), diff.Seconds()) //839.9833333343166 50399.000000059
t3, err := time.Parse("2006-01-02 15:04:05", "2023-01-16 10:00:00")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) //true
fmt.Println(now.Unix()) //1673875644 当前的时间戳
}