一、go的简单介绍和安装

首先本人重新开坑介于这么久没有进行总结和学习也该问问自己了

1.1 介绍

​ go总体来讲就是将c和java的特性给结合和优化所诞生出来的,由Rob Pike(罗伯.派克),Ken Thompson(肯.汤普森)和Robert Griesemer(罗伯特.格利茨默)三位大佬所创造出来的,拥有比较简洁的语法和很好的性能,同时也是编译性语言,而go主要用于web或者游戏服务器,简单来说就是后台,也就是为什么越来越多的公司选择后端都会使用go,而不是繁琐的java。

1.2 安装

​ 安装的sdk实际上直接看官网即可,这里就将安装在windows上。

Windows 下载的是 .msi 后缀,默认情况下 .msi 文件会安装在 c:\Go 目录下。你可以去自定义,但是要将go目录添加倒环境变量中,也就是系统变量中的Path路径。安装完成以后,在cmd中输入go version,如果有显示版本证明安装成功。

1.3 使用vscode安装go插件

如果你没有梯子的话安装go的扩展是会非常慢的,这点请注意。

1.4 编写hello world

​ 安装完以后看默认文档中的bin是否有这些文件,有就没问题,然后在vscode中的工作文件夹中创建一个名为main.go的文件,写下hello world

1
2
3
4
5
6
7
8
9
10
package main

//导入一个fmt包
import "fmt"

//定义一个函数
func main() {
//打印
fmt.Println("hello world!")
}

然后在vscode的终端中输入go run ./main.go

即可编译成功,如图

二、数据类型介绍

​ 跟很多语言的数据类型一样,都会int float string 等众多数据类型,如

int unint byte float32 float64 等等,其中int有负数,unint无负数

浮点数代码项目中的小数,零值是一个变量的默认值

2.1 变量

接下来,将用代码来解释数字类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//整数i int类型
var i int = 10
//自动推导变量类型
var a = 10
//定义多个类型
var (
b int = 1
c int = 2
)
//自动推导多个类型
var (
j = 1
k = 2
)
//定义浮点类型
var (
x float32 = 1.1451491018 //1.1451491
y float64 = 1.1451491018 //1.1451491018
)
//指向i的指针
pi := &i //10

类型实际上是有默认值,如

1
var i int //i的值为0

而如果我们要声明一个变量,实际上还可以进行简化

1
2
3
e := 10 //int
f := false //bool
g := "世界" //string

2.2 常量

​ 定义一个常量很简单,只需要关键词const即可,但常量一旦定义则不可修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//常量
const name = "常量不可修改"
name1 := "简化常量"
//定义多个常量
const (
one = 1
two = 2
three = 3
four = 4
)
//定义多个规律常量
const (
//one=(0)+1
//two=(0+1)+1
//three=(0+1+1)+1 以此类推
one1 = iota + 1
two1
three1
four1
)

2.3 类型转换

​ 类型转换实际上是用的strconv这个包,关于里面的api这里就不必多少了这里就简单的说明一些,代码所示

1
2
3
4
5
6
7
//Itoa int类型转为string类型
is := strconv.Itoa(i)
//Atoi string类型转为int类型
is2, err := strconv.Atoi(is)
//简易类型转换
i2f := float64(i)
f2i := int(y)

2.4 strings包

​ strings包也是go官方的api库,跟java中的string包也是类似,这里就写几个api,如下代码所示

1
2
3
4
5
6
7
8
9
s1 := "abcdefgh"
//判断s1前缀是否有H
fmt.Println(strings.HasPrefix(s1, "h"))
//在s1中查找字符串
fmt.Println(strings.Index(s1, "h"))
//将s1全部转为大写
fmt.Println(strings.ToUpper(s1))
//判断s1是否有H字符串
fmt.Println(strings.Contains(s1, "i"))

三、条件语句

3.1 if条件语句

​ 首先要说明的是if后面的表达式不需要(),这跟很多语言不同,比如说java,每个条件分支大括号是必须的,if紧跟大括号,{不能占一行,else同理,在if…else条件语句中可以增加多个else if…else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//if语句
i := 10
if i > 10 { //必须紧跟大括号
fmt.Println("i>10")
} else { //{不能占一行
fmt.Println("i<=10")
}

//else if语句
j := 7
if j > 10 {
fmt.Println("j>=10")
} else if j > 5 && j <= 10 {
fmt.Println("5<j<=10")
} else {
fmt.Println("j<=5")
}

if语句也可以进行简化,将定义变量合并起来

1
2
3
4
5
6
7
if i := 6; i > 10 {
fmt.Println("i>=10")
} else if i > 5 && i <= 10 {
fmt.Println("5<i<=10")
} else {
fmt.Println("i<=5")
}

3.2 switch语句

​ 当if条件过多时,就可以用switch语句来替代。switch的case从上到下逐一进行判断。

一旦满足条件,立即执行对应的分支并返回,其余分支不再做判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
switch i := 6; {
case i > 10:
fmt.Println("i>=10")
case i > 5 && i <= 10:
fmt.Println("5<i<=10")
default:
fmt.Println("i<=5")
}
//switch的变种 利用数字进行判断
switch j := 1; j {
case 1:
fallthrough
case 2:
fmt.Println("1")
default:
fmt.Println("没有匹配")
}
//switch的变种 利用bool来进行判断
switch 2 > 1 {
case true:
fmt.Println("2>1")
case false:
fmt.Println("2<=1")
}

3.3 for循环语句

​ 简单语句,一般用于for循环的初始化。for循环的条件,表示for循环什么时候结束。

更新语句,一般用于更新循环的变量

1
2
3
4
5
sum := 0
for i := 0; i <= 100; i++ {
sum += i
}
fmt.Println("sum=", sum)

由于go没有while语句,所以只能用for来模仿一个

1
2
3
4
5
6
7
sum1 := 0
i1 := 1
for i1 <= 100 {
sum1 += i1
i1++
}
fmt.Println("sum1=", sum1)

也跟其他语言一样,有着break和continue。continue可以跳出本次循环,继续执行下一个循环
break可以跳出整个for循环,哪怕for循环没有执行完,也会强制终止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//break
sum2 := 0
i2 := 1
for {
sum2 += i2
i++
if i2 > 100 {
break
}
}
fmt.Println("sum2=", sum2)
//continue跳出
sum3 := 0
for i3 := 1; i3 <= 100; i3++ {
if i3&2 != 0 {
continue
}
sum3 += i3
}
fmt.Println("sum3=", sum3)

四、数组

​ 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。

4.1 数组的简单定义

1
array := [5]string{"a", "b", "c", "d", "e"}

也可以用…来省略

1
array1 := [...]string{"a", "b", "c", "d", "e"}

如果要初始化索引则不可用…来进行省略

1
array2 := [5]string{1: "b", 3: "d"}

数组也可以用for循环来进行表示

1
2
3
for i := 0; i < 5; i++ {
fmt.Printf("数组索引:%d,对应值:%s\n", i, array[i])
}

但大部分情况下使用的是for range这种Go语言的新型循环

1
2
3
for i, v := range array {
fmt.Printf("数组索引:%d,对应值:%s\n", i, v)
}

如果不需要索引值,可以使用_将索引丢弃

1
2
3
for _, v := range array {
fmt.Printf("对应值:%s\n", v)
}

4.2 二维数组

1
2
3
4
5
6
7
8
func main() {
aa := [2][2]int{}
aa[0][0] = 1
aa[0][1] = 2
aa[1][0] = 4
aa[1][1] = 5
fmt.Println(aa)
}//其结果为[[1 2] [4 5]]

4.3 byte的一些使用

UTF8编码下,一个汉字对应三个字节,所以字符串s的长度其实是12

1
2
3
4
5
s1 := "hello 世界"
bs := []byte(s1)
fmt.Println(bs)
fmt.Println(s[0], s1[1], s1[11])
fmt.Println(len(s1))

五、切片

切片和数组类似,可以把它理解为动态数组,对数组任意分隔,就可以得到一个切片。基于数组生成切片,包含索引start,但是不包含索引end,切片和数组一样,也可以通过索引定位元素

1
2
3
array3 := [5]string{"a", "b", "c", "d", "e"}
s := array3[2:5]
fmt.Println(s)//结果为[c d e]
1
2
3
4
//修改切片
slice := array[2:5]
slice[1] = "f"
fmt.Println(array)//结果为[a b c f e]

这里的len函数是指slice的长度,而cap函数指的是整个数组的长度

1
2
slice1 := []string{"a", "b", "c", "d", "e"}
fmt.Println(len(slice1), cap(slice1))

append的简单使用

1
2
3
4
5
6
//追加一个元素
slice2 := append(slice1, "f")
//多加多个元素
slice3 := append(slice1, "f", "g")
//追加一个切片
slice4 := append(slice1, slice...)

六、map结构

6.1 声明map和使用

map是一一个无序的K-V键值对集合,结构为map[K]V,其中K对应Key,V对应Value
map的门操作符可以返回两个值
1.第一个值是对应的Value
2.第二个值标记该Key是否存在,如果存在,它的值为true

添加键值对或者更新对应Key的Value
nameAgeMap[“世界”] = 20
获取指定Key对应的Value
age := nameAgeMapr[“世界”]

1
2
3
4
5
6
7
//声明
nameAgeMap := make(map[string]int)
nameAgeMap["世界"] = 20
age, ok := nameAgeMap["世界"]
if ok {
fmt.Println(age)
}

6.2 遍历Map

对于map,for range返回两个值
1.第一个是map的Key
2.第二个是map的Value

1
2
3
4
5
6
nameAgeMap["世界"] = 20
nameAgeMap["世界1"] = 21
nameAgeMap["世界2"] = 22
for k, v := range nameAgeMap {
fmt.Println("key is", k, "Value is ", v)
}//其结果为key is 世界2 Value is 22 key is 世界 Value is 20 key is 世界1 Value is 21

七、函数

通过函数,可以把开发任务分解成一个个小的单元,这些小单元可以被其他单元复用,进而提高开发效率、降低代码重合度任何一个函数的定义,都有一个func关键字main是函数的名字main函数名字后面的一对括号0是不能省略的括号里可以定义函数使用的参数

函数的声明格式

1
2
3
func funcName(params) result {
body
}

关键字func
函数名字funcName
函数的参数params,用来定义形参的变量名和类型
result是返回的函数值,用于定义返回值的类型

1
2
3
4
5
6
7
8
9
func sum(a, b int) int {
return a + b
}
func main(){
//变量名称在前,变量类型在后
//变量名称叫作参数名称,也就是函数的形参
r := sum(1, 2)
fmt.Println(r) //结果为3
}

Go语言的函数可以返回多个值,也就是多值返回
第一个值返回函数的结果,第二个值返回函数出错的信息

不管是自定义的函数sum、sum1,还是我们使用到的函数Printin,都会从属于一个包也就是package,不同包的函数要被调用,那么函数的作用域必须是公有的,也就是函数名称的首字母要大写

函数名称首字母小写代表私有函数,只有在同一个包中才可以被调用
函数名称首字母大写代表公有函数,不同的包也可以调用
任何一个函数都会从属于一个包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func sum1(a, b int) (int, error) {
if a < 0 || b < 0 {
return 0, errors.New("a或者b不能是负数")
}
return a + b, nil
}
r1, err := sum1(1, -1)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(r1)
}

// 函数的返回值也可以有变量名称
func sum2(a, b int) (sum2 int, err error) {
if a < 0 || b < 0 {
return 0, errors.New("a或者b不能是负数")
}
sum2 = a + b
err = nil
return
}
//也可以不需要erro
r2, _ := sum1(1, -1)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(r2)
}
//匿名函数就是没有名字的函数
sum3 := func(a, b int) int {
return a + b
}
fmt.Println(sum3(1, 2))

方法必须要有一个接收者,这个接收者是一个类型,这样方法就和这个类型绑定在一起,称为这个类型的方法,接收者的定义和普通变量、函数参数等一样
前面是变量名,后面是接收者类型

在函数中再定义函数(函数嵌套),定义的这个匿名函数,也可以称为内部函数更重要的是,在函数内定义的内部函数,可以使用外部函数的变量等,这种方式也称为闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Age uint

func (age Age) string() {
fmt.Println("age is", age)
}

// 值类型接收者和指针类型接收者
func (age *Age) Modify() {
*age = Age(22)
}
age := Age(22)
age.string()
//指针
age.Modify()

八、总结

本期我们讲了安装使用、数据类型、条件语句、数组、切片、Map和函数。如果有代码基础,实际上这些只需要一个小时即可掌握。

如果硬要难的话可能是函数中的闭包没有见过,这里需要着重看一下。