go-doc-类型

介绍

|类型|值和引用|描述|
|–|–|
|整数类型|值类型|int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、byte,默认值是0|
|浮点类型|值类型|float32、float64,默认值是0|
|字符型|值类型|byte,没有专门的字符型,使用byte来保存单个字母字符|
|布尔型|值类型|bool,默认值是false|
|字符串|值类型|string,默认值是""|
|数组|值类型||
|结构体|值类型|struct|
|指针|引用类型|*|
|管道|引用类型|channel|
|函数|引用类型|也是一种类型|
|切片|引用类型|slice|
|接口|引用类型|interface|
|映射|引用类型|map|

值类型:变量直接存储值,内存通常在栈中分配
引用类型:变量存储的是一个地址,这个地址对应的空间才真正存储数据的值,内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就称为一个垃圾,由GC来回收

字符串

字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字符连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。

字符串要点

  1. 字符串一旦赋值了,字符串就不能修改,在Go中字符串是不可变的
  2. 双引号,会识别转义字符
  3. 反引号,以字符串的原生形式输出
  4. 字符串拼接使用 + 号
  5. 当一个拼接操作很长是,可以分行写

字符串表示形式

  1. 双引号,会识别转义字符
  2. 反引号,以字符串的原生形式输出
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main // 表示文件所在的包名,在go中,每个文件都必须属于一个包

import "fmt" // 引入fmt包

// 定义主函数
func main() {
// 1. 使用双引号
var str1 string = "hello"

// 2. 使用反引号
var str2 string = `
package main // 表示文件所在的包名,在go中,每个文件都必须属于一个包

import "fmt" // 引入fmt包

// 逐个逐个地定义全局变量
var g1 = 100
var g2 = "tom"

// 一次性定义多个全局变量
var (
g3 = 11
g4 = "jim"
)

// 定义主函数
func main() {
// 1. 声明不赋值,使用默认值
var i int

// 2. 根据值自信判断变量类型(类型推导)
var num = 10.11

// 3. 省略var,使用:=
name := "tom"

// 4. 一次性声明多个
var n1, n2, n3 int

// 5. 一次性赋值多个
// n4 = 100
// n5 = "tom"
// n6 = 101
var n4, n5, n6 = 100, "tom", 101
}
`

// 3. 一行拼接
var str3 = "hello " + "world"

// 4. 分行拼接
var str4 = "hello " + "hello " +
"world " + "world" +
"hello world!"

fmt.Println(str1)
fmt.Println(str2)
fmt.Println(str3)
fmt.Println(str4)
}

string和slice

string底层是一个byte数组,因此string也可以进行切片处理

string可以通过切片的方式,先修改切片内容在转换回字符串,从而修改字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main 

import (
"fmt"
)

func main() {
var str = "中国1234567890"
var slic1 = str[0:7]
fmt.Println(slic1)

// 注意要用rune,不然中文处理有问题
var slic2 = []rune(str)
slic2[0] = '汉'
str = string(slic2)
fmt.Println(str)
}

指针

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

import (
"fmt"
)

func main() {
var num int = 1
var ptr *int = &num

fmt.Printf("ptr %v %v", &ptr, *ptr)
}

数组

数组可以存放多个同一类型数据。数组也是一种数据类型,在Go中,数组是值类型

数组要点

  1. 数组是多个相同类型数据的组合,一个数组一旦声明/定义,其长度是固定的,不能动态变化
  2. 数组中的元素可以是任何数据类型,包括值类型和引用类型,但不能混用
  3. 数组创建后,如果没有赋值,元素的值都是数组类型的默认值
  4. 数组的下标从0开始
  5. 数组下标必须在指定范围内使用,否则报panic:数组越界
  6. 数组是值类型,在默认情况下是值传递,因此会进行值拷贝。
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main 

import (
"fmt"
)

func main() {

// 初始化方式1
var nums [5]int
nums[0] = 0
nums[1] = 1
nums[2] = 2
nums[3] = 3
nums[4] = 4

fmt.Printf("nums 地址 = %p, nums [%d] 地址 = %p\n", &nums, 1, &nums[1])

// 初始化方式2
var nums2 [5]int = [5]int {1, 2, 3, 4, 5}
fmt.Println(nums2)

// 初始化方式3
var nums3 = [5]int {1, 2, 3, 4, 5}
fmt.Println(nums3)

// 初始化方式4
var nums4 = [...]int {1, 2, 3, 4, 5}
fmt.Println(nums4)

// 初始化方式5,指定索引对应的值
var nums5 = [5]int {2:1, 3:2, 4:3, 0:4, 1:5}
fmt.Println(nums5)

// 数组遍历1
for i := 0; i < len(nums5); i++{
fmt.Println(nums5[i])
}

// 数组遍历2
for i, val := range nums5 {
fmt.Println(i, val)
}

for _, val := range nums5 {
fmt.Println(val)
}
}

二维数组

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
35
36
37
38
39
40
package main 

import (
"fmt"
)

func main() {
// 初始化方式1
var arr [4][5]int;

arr[1][2] = 1
arr[1][3] = 1
arr[1][4] = 1

fmt.Println(arr)

// 初始化方式2
var arr2 = [2][3]int{{1,2,3},{4,5,6}}
fmt.Println(arr2)

// 初始化方式3
var arr3 = [...][3]int{{1,2,3},{4,5,6}}
fmt.Println(arr3)

// for遍历
for i := 0; i < len(arr3); i++ {
for j := 0; j < len(arr3[i]); j++ {
fmt.Print(arr3[i][j])
}
}

fmt.Println("")

// for-range遍历
for _, v := range arr3 {
for _, v2 := range v {
fmt.Print(v2)
}
}
}

切片

可以理解是一个动态数组,从底层来说,其实就是一个struct结构体。

切片要点

  1. 切片是数组的一个引用,因此切片是引用类型,进行传递时,准守引用传递的机制
  2. 切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度都一样
  3. 切片的长度是可以变换的,因此切片是一个可以动态变化数组
  4. 切片引用数组,切片内容改变, 数组内容也会改变
  5. 切片可以继续切片

切片创建方式

  1. 定义一个切片,然后让切片去引用一个创建好的数组
    • var slice = arr[1:3]
    • var slice = arr[0:end],可以简写arr[:end]
    • var slice = arr[start:len(arr)],可以简写arr[start:]
    • var slice = arr[0:len(arr)],可以简写arr[:]
  2. 通过make来创建
  3. 定义一个切片,直接指定具体数组

切片append操作原理

  1. 切片append操作的本质就是对数组扩容
  2. go底层会创建一个新的数组newArr(安装扩容后大小)
  3. 将slice原来包含的元素拷贝到新的数组newArr
  4. slice重新引用到newArr
  5. newArr是底层来维护,程序员不可见
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
35
36
37
38
39
40
41
42
43
44
package main 

import (
"fmt"
)

func main() {
var nums [5]int = [5]int {1, 2, 3, 4, 5}

// 1. 创建一个切片,引用数组,下标[1, 3)范围的内容
var slic = nums[1:3]

fmt.Println("切片内容:", slic)
fmt.Println("切片元素个数:", len(slic))
fmt.Println("切片容量:", cap(slic))

// 2. 通过make创建
var slic2 []int = make([]int, 5, 10)
slic2[1] = 10
slic2[3] = 10

fmt.Println(slic2)

// 3. 直接定义一个具体数组
var slic3 []int = []int {1, 2, 3, 4, 5, 6, 7, 8}
fmt.Println(slic3)

// 切片继续切片
var slic4 []int = slic3[1:3]
fmt.Println(slic4)

// 切片内容追加指定内容
slic4 = append(slic4, 100, 101, 102)
fmt.Println(slic4)

// 切片内容追加整个切片
slic4 = append(slic4, slic4...)
fmt.Println(slic4)

// 切片拷贝
var slic5 = make([]int, 20)
copy(slic5, slic4)
fmt.Println(slic5)
}

map

  • 基本语法
1
var 变量名 map[keytype]valuetype

map注意点

  1. map声明不分配内存,需要make来分配内存才可以使用。
  2. Go中map是无序的,跟添加的顺序也无关,每次遍历得到的顺序是不固定的
  3. map的容量达到后,再给map添加元素,会自动扩容,并不会发生panic,也说明map能动态的增长键值对
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
35
36
37
38
39
40
41
42
43
44
package main 

import (
"fmt"
)

func main() {
// 1.方式1,先声明在make
var m map[string]string
m = make(map[string]string, 10)
m["one"] = "111"
m["two"] = "222"

fmt.Println(m)

// 2.方式2,直接make
var m2 = make(map[string]string, 10)
fmt.Println(m2)

// 3.方式3,声明并赋值
var m3 = map[string]string{
"one":"111",
"two":"222",
}

fmt.Println(m3)

// 删除指定的key
delete(m3, "one")
fmt.Println(m3)

// 查找
var val, findRes = m3["one"]
if findRes {
fmt.Println("找到值 ", val)
} else {
fmt.Println("没有找到值 ")
}

// for-range变量
for k, v := range m3 {
fmt.Printf("k=%v v=%v\n", k, v)
}
}

map和slice

切片的数据类型如果是map,则我们成为slice of map,map切片,这样使用map的个数就可以动态变化了

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
package main 

import (
"fmt"
)

func main() {
var monsters []map[string]string
monsters = make([]map[string]string, 2)

if monsters[0] == nil {
monsters[0] = make(map[string]string, 2)
monsters[0]["name"] = "monster1"
monsters[0]["fight"] = "444"
}

if monsters[1] == nil {
monsters[1] = make(map[string]string, 2)
monsters[1]["name"] = "monster2"
monsters[1]["fight"] = "1111"
}

// 使用append追加信息
var newMonster = map[string]string{
"name":"monster3",
"fight":"222",
}

monsters = append(monsters, newMonster)

fmt.Println(monsters)
}

map排序

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
package main 

import (
"fmt"
"sort"
)

func main() {
var map1 = make(map[int]int, 10)
map1[10] = 14
map1[1] = 53
map1[4] = 22
map1[7] = 70

fmt.Println(map1)

// 排序
// 1. 先将map的key放入到切片中
// 2. 对切片进行排序
// 3. 遍历切片,然后按照key来输出map的值
var keys []int
for k, _ := range map1 {
keys = append(keys, k)
}

sort.Ints(keys)
fmt.Println(keys)

for _, k := range keys {
fmt.Println(k, map1[k])
}
}

类型转换

Go在不同类型的变量之间赋值时需要显示转换,也就是说Golang中数据类型不能自动转换

数值类型转换

  1. Go中,数据类型的转换可以是从表示范围小->表示范围大,也可以是范围大->范围小
  2. 被转换的是变量存储的数据,变量本身的数据类型并没有变化
  3. 在转换中,当结果范围不一致时,按溢出处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main // 表示文件所在的包名,在go中,每个文件都必须属于一个包

import (
"fmt"
)

// 定义主函数
func main() {
var i32 int32 = 100
var f32 float32 = float32(i32)
var i8 int8 = int8(i32)

fmt.Println("i32=%v f32=%v i8=%v", i32, f32, i8)
}

基础类型转string类型

  1. 使用 fmt.Sprintf() 进行转换
  2. 使用strconv 包的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main 

import (
"fmt"
"strconv"
)

func main() {
var num1 int = 99
var num2 float32 = 11.22

// 1. 使用fmt.Sprintf转换
var str1 string = fmt.Sprintf("%d %f", num1, num2)

// 2. 使用strconv包
var str2 string = strconv.FormatInt(int64(num1), 10)

fmt.Println(str1)
fmt.Println(str2)
}

string类型转基础数据类型

  1. 使用strconv 包的函数

string类型转基础数据类型注意事项

  1. 在将string类型转成基本数据类型是,要确保string类型能够转换成有效的数据。如果转换失败会转换成默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main 

import (
"fmt"
"strconv"
)

func main() {
var str string = "true"

// 1. 使用 strconv 包
var b bool
b, _ = strconv.ParseBool(str)

fmt.Printf("b = %v", b)
}

类型断言

类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言

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
35
36
37
38
39
40
41
42
43
44
45
package main 

import (
"fmt"
)

// 一个简单类型断言使用
func TypeJudge (items ...interface{}) {
for i, x := range items {
switch x.(type) {
case bool:
fmt.Println("bool 类型", i, x)
case int:
fmt.Println("int 类型", i, x)
case int8:
fmt.Println("int8 类型", i, x)
case int16:
fmt.Println("int16 类型", i, x)
case int32:
fmt.Println("int32 类型", i, x)
case int64:
fmt.Println("int64 类型", i, x)
}
}
}

func main() {
var x interface{}
var y int32 = 1

// 空接口,可以接收任意类型
x = y

// 使用类型断言
var z, res = x.(int64)
if res == true {
// 类型转换成功
fmt.Println("类型转换成功", z)
} else {
// 转换失败
fmt.Println("类型转换失败")
}

TypeJudge(1, false, 2, 3)
}