这是我参与「第五届青训营 」笔记创作活动的第1天
一、本堂课重点内容:
本节课介绍了go语言的优势、开发环境、基础语法和标准库,并通过三个案例巩固知识
二、详细知识点介绍:
1. go语言优势:
- 高性能、高并发
- 语法简单、学习曲线平缓
- 标准库丰富
- 工具链完善
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
2. 工具
3. 基础语法
3.1 变量
3.1.1定义变量
- 格式:
var identifier typename - 例子:
var num int =10 - 格式:
typename := value - 例子:
var num int
num=10
num2:=10
- 如果没有赋值则为系统默认值:
| 类型 | 默认值 |
|---|---|
| 数值型 | 0 |
| 布尔型 | false |
| 字符串 | “” |
3.1.2多全局变量声明
- 格式:
var ( vname1 v_type1 vname2 v_type2 ) - 例子:
var{
a int
b int
}
3.2if else
格式:
if 表达式{
}else if 表达式{
}else{
}
3.3循环
| 形式 | 语法 |
|---|---|
| 和c语言中的for相同 | for init; condition; post {} |
| 和c语言中的while相同 | for condition{} |
和c语言中的for(;;)相同 | for{} |
3.4switch
Switch v{
case var1:
...
case var2:
...
default:
...
}
其中的变量v可以是任何类型,var1和var2可以是同类型的任意值,类型不局限为常量或者整数,或者最终结果为相同类型的表达式。
3.5数组
3.5.1声明数组
- 格式:
var variable_name [SIZE] variable_type - 例子:
var num [10] int
3.5.2初始化数组
| 方法 | 格式 |
|---|---|
| 直接赋值 | var num=[10]int{0,0,0,...} |
| 通过字面量 | num:=[10]int{0,0,0,...} |
| 数组长度不确定 | var num=[...]int{0,0}或num:=[...]int{0,0} |
| 指定下标进行部分初始化 | num:=[10]int{1:1,5:10} |
3.6切片
3.6.1添加元素
| 函数 | 位置 | 例子 |
|---|---|---|
| append() | 在切片尾部添加 | num=append(num,3) |
| 在切片头部添加 | num=append([]int{0},num) |
3.6.2删除元素
| 位置 | 方法 | 例子(删除相应位置的N个数) |
|---|---|---|
| 从开头删除 | 直接移动数据指针 | num=num[N:] |
| 将后面的数据向开头移动 | num= append(num[:0], num[N:]...) | |
| 使用copy将后续数据向前移动 | num= num[:copy(num,num[N:])] | |
| 从中间位置删除 | append() | num = append(num[:i], num[i+N:], ...) |
| copy() | num=num[:copy(a[:i], a[i+N:])] | |
| 从尾部删除 | 直接删除 | num = num[:len(num)-N] |
补充:make函数(分配内存)
- 语法结构:
make(type,len,cap) - 例子:
s:=make([]string,3)
s[0]="s"//len为数组的长度,cap=0,cap是底层数组的大小
- 容量的用处在哪?
- 原理:当append扩展长度时,如果新的长度小于容量,不会更换底层数组,否则,go 会新申请一个底层数组,拷贝这边的值过去,把原来的数组丢掉。
- 用途:在数据拷贝和内存申请的消耗与内存占用之间提供一个权衡。
3.7map
3.7.1初始化map:
//way1:
m1 := make(map[string]string)
m1["age"] = "10"
m1["name"] = "gufei"
//way2:
m2 = map[string]string{"age": "10", "name": "gufei"}
3.7.2插入数据
格式:map_variable["age"] = 10
3.7.3删除数据
格式: delete(map_variable,"age")
特点如下:
-
map是无序的(原因为无序写入以及扩容导致的元素顺序发生变化),每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取
-
map的长度是不固定的,是一种引用类型(和slice一样)
-
可使用内置len函数,返回map拥有的key的数量
-
map的key可以是所有可比较的类型,如布尔型、字符串型、键等。
3.8range
3.8.1遍历数组
格式:
myArray := [3]int{1, 2, 3}
for i, ele := range myArray {
fmt.Printf("index:%d,element:%d\n", i, ele)
fmt.Printf("index:%d,element:%d\n", i, myArray[i])
}
3.8.2遍历slice
mySlice := []string{"I", "am", "a","girl"}
for i, ele := range mySlice {
fmt.Printf("index:%d,element:%s\n", i, ele)
fmt.Printf("index:%d,element:%s\n", i, mySlice[i])
}
3.8.3遍历字符串
s:="gufei"
for i,ele := range s {
fmt.Println(string(ele))
fmt.Printf("index:%d,element:%s\n", i, string(s[i]))
}
3.8.4遍历map
myMap := map[int]string{1:"语文",2:"数学",3:"英语"}
for key,value := range myMap {
fmt.Printf("key:%d,value:%s\n", key, value)
fmt.Printf("key:%d,value:%s\n", key, myMap[key])
}
3.9函数
格式:
func fuction_name([parameter list])(return types){
函数体
}
例子:
func add(a int,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
}
3.10指针
例子:
func add(n *int){
*n+=2
}
func main(){
n:=5
add(&n)
}
3.11结构体
3.11.1声明结构体
格式:
//way1:
type struct_variable_type struct {
member definition
member definition
...
member definition
}
//例子:
type user struct{
name string
password string
}
//way2:
variable_name := structure_variable_type {value1, value2...valuen}
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
//例子:
a=user{"wang","1234"}
b:=user{name:"wang",password:"1234"}
3.11.2访问结构体成员
- 格式:
结构体变量名.成员名 - 例子:
3.12结构体方法
例如:
3.13错误处理
- 习惯用函数的返回值来处理错误信息,可在函数返回值里加err,如果出现错误可以用errors.new去创建。
- 例如:
func findUsers(users []user,name string )(v *user,err error){
for i,u:=range users{
if u.name==name{
return &u,nil
}
}
return nil,errors.New("not found")
}
3.14字符串操作
字符串标准库里有多个工具函数如:
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
fmt.Println(len(a)) // 5
b := "你好"
fmt.Println(len(b)) // 6
3.15字符串格式化
在go语言中可用%v处理任意类型的变量,用%+v可更加详细(增加变量名的信息),用%#v会进一步详细(还增加调用的函数信息)
例如:
s := "hello"
n := 123
p := point{1, 2}
fmt.Println(s, n) // hello 123
fmt.Println(p) // {1 2}
fmt.Printf("s=%v\n", s) // s=hello
fmt.Printf("n=%v\n", n) // n=123
fmt.Printf("p=%v\n", p) // p={1 2}
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
3.16json处理("encoding/json"包)
对于一个已有的结构体,只要保证每个字段的第一个字母是大写(若要字母小写,在构造结构体时可在变量后面写 json:"小写的变量名"),可通过json.Marshal去序列化,打印的时候需要加string强制类型转换为字符串,否则会打印出一些十六进制的编码
例如:
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf) // [123 34 78 97...]
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
当然也可以通过json.Unmarshal去反序列化赋值到空的变量里.
例如:
var b userInfo
err = json.Unmarshal(buf, &b)
fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
3.17时间处理("time"包)
- time.Now():快速获取当前时间
- t:=time.Date(年,月...):构造一个带时区的时间,可利用t.Year(),t.Month()等方式可以获取该时间点的信息
- t.Format()或者time.Parse():解析时间
- time.Unix():获取时间戳
3.18数字解析("strconv"包)
- strconv.ParseFloat()或strconv.ParseInt(字符串,进制,精度):解析字符串
- strconv.Atoi():将十进制字符串转成数字
- strconv.Itoa():将数字转成字符串
3.19进程信息
- os.Args:获取进程在执行时的一些命令行参数
- os.Getenv()或者os.Setenv():获取或者写入环境变量