前端尝试转GO学习(第二天)
定义变量
package main
import "fmt"
func variableZeroValue() {
// 使用 var 来声明,后面为变量名 + 类型
var zeroNumber int
var emptyString string
// 打印 %q 可以空字符串
fmt.Printf("%d %q\n", zeroNumber, emptyString)
}
func main() {
variableZeroValue()
}
为变量赋初值,可以一行定义多个变量
func variableInitialValue() {
var numberOne, numberTwo int = 3, 4
var valueString string = "abc"
fmt.Println(numberOne, numberTwo, valueString)
}
可以不需要显示类型,编译器可以自行推断出变量的类型。
func variableTypeDeduction() {
var test1, test2, test3, test4 = 3, 4, true, "abc"
fmt.Println(test1, test2, test3, test4)
}
不使用 var 声明变量
func variableShorter() {
aa, bb, cc := 11, "abc", true
fmt.Println(aa, bb, cc)
}
后续想要修改的话,不能使用 :=,只能使用 =
func variableShorter() {
aa, bb, cc := 11, "abc", true
bb = "def"
fmt.Println(aa, bb, cc)
}
常量
表示固定的值。在计算机程序运行时,不会被程序修改的。
func consts() {
const filename = "abc.txt"
const a, b = 3, 4
// Cannot assign to filename
// 常量是不允许被修改的
filename = "casda"
fmt.Println(filename)
}
使用 const 来定义枚举类型
func enumsFunc() {
const (
React = 0
Vue = 1
Go = 2
)
fmt.Println("React", React)
fmt.Println("Vue", Vue)
fmt.Println("Go", Go)
}
使用 iota 可以进行自增。iota 在 const 关键字出现时将被重置为 0。
func enumsFunc() {
const (
React = 1 * iota
Vue
Go
)
fmt.Println("React", React) // 0
fmt.Println("Vue", Vue) // 1
fmt.Println("Go", Go) // 2
}
func enumsFunc() {
const (
React = 60 * iota
Vue
Go
)
fmt.Println("React", React) // 0
fmt.Println("Vue", Vue) // 60
fmt.Println("Go", Go) // 120
}
数据类型
1. 整型
- 有符号整型:int8、int16、int32、int64、int。
- 无符号整型:uint8、uint16、uint32、uint64、uint。
2. 浮点型
浮点型表示如 3.1415926。
| 类型 | 字节数 | 说明 |
|---|---|---|
| float32 | 4 | 32 位的浮点型 |
| float64 | 8 | 64 位的浮点型 |
func showFloat() {
var num1 float32 = math.MaxFloat32
var num2 float64 = math.MaxFloat64
fmt.Printf("num1的类型是%T,num1是%g\n", num1, num1)
fmt.Printf("num2的类型是%T,num1是%g\n", num2, num2)
}
浮点数能表示的数值很大,精度没有那么大:
float32的精度只能提供大约6个十进制数(表示小数点后6位)的精度。float64的精度能提供大约15个十进制数(表示小数点后15位)的精度。
3. 字符
字符串中的每一个元素叫作“字符”,定义字符时使用单引号。Go 语言的字符有两种,如表所示。
| 类 型 | 字 节 数 | 说 明 |
|---|---|---|
| byte | 1 | 表示 UTF-8 字符串的单个字节的值,表示的是 ASCII 码表中的一个字符,uint8 的别名类型 |
| rune | 4 | 表示单个 unicode 字符,int32 的别名类型 |
package main
import (
"fmt"
"unsafe"
)
func showChar() {
var x byte = 65
var y uint8 = 65
fmt.Printf("x = %c\n", x) // x = A
fmt.Printf("y = %c\n", y) // y = A
}
func sizeOfChar() {
var x byte = 65
fmt.Printf("x = %c\n", x)
fmt.Printf("x 占用 %d 个字节\n", unsafe.Sizeof(x))
var y rune = 'A'
fmt.Printf("y = %c\n", y)
fmt.Printf("y 占用 %d 个字节\n", unsafe.Sizeof(y))
}
func main() {
showChar()
sizeOfChar()
}
中文的话,就得使用 rune 类型
func chineseCharacters() {
var c rune = '学'
fmt.Printf("c = %c\n", c)
fmt.Printf("y 占用 %d 个字节\n", unsafe.Sizeof(c))
}
4. 字符串
字符串在 Go 语言中是以基本数据类型出现的,使用字符串就像使用其他原生基本数据类型 int、float32、float64、bool 一样。
func stringFunc() {
var study string // 定义名为str的字符串类型变量
study = "学习学习学习" // 将变量赋值
study2 := "正在学习中" // 以自动推断方式初始化
// 多行字符串
var study3 = `
hhhhhhhh💰
`
fmt.Printf(study, study2, study3)
}
5. 布尔类型
布尔(bool) 类型:true 或者 false 。
func showBool() {
a := true
b := false
fmt.Println("a=", a)
fmt.Println("b=", b)
fmt.Println("true && false = ", a && b)
fmt.Println("true || false = ", a || b)
}
在 Go 中,真值是用 true 表示,并且 不与 1 相等;假值是用 false 表示,并且 不与 0 相等。
6. 复数型
复数型用于表示数学中的复数,如 1+2j、1-2j、-1-2j 等。在 Go 语言中提供了两种精度的复数类型:complex64 和 complex128 ,分别对应 float32 和 float64 两种浮点数精度,如表所示。
| 类 型 | 字 节 数 | 说 明 |
|---|---|---|
| complex64 | 8 | 64 位的复数型,由 float32 类型的实部和虚部联合表示 |
| complex128 | 16 | 128 位的复数型,由 float64 类型的实部和虚部联合表示 |
func showComplex() {
// 内置的 complex 函数用于构建复数
var x complex64 = complex(1, 2)
var y complex128 = complex(3, 4)
var z complex128 = complex(5, 6)
fmt.Println("x = ", x)
fmt.Println("y = ", y)
fmt.Println("z = ", z)
// 内建的 real 和 imag 函数分别返回复数的实部和虚部
fmt.Println("real(x) = ", real(x))
fmt.Println("imag(x) = ", imag(x))
fmt.Println("y * z = ", y*z)
// 简化书写复数:
x1 := 1 + 2i
y2 := 3 + 4i
z3 := 5 + 6i
fmt.Println(x1, y2, z3)
// (1+2i) (3+4i) (5+6i)
}
fmt 格式输出
| 格式 | 含义 |
|---|---|
| %% | 一个%字面量 |
| %b | 一个二进制整数值(基数为 2),或者是一个(高级的)用科学计数法表示的指数为 2 的浮点数 |
| %c | 字符型。可以把输入的数字按照 ASCII 码相应转换为对应的字符 |
| %d | 一个十进制数值(基数为 10) |
| %f | 以标准记数法表示的浮点数或者复数值 |
| %o | 一个以八进制表示的数字(基数为 8) |
| %p | 以十六进制(基数为 16)表示的一个值的地址,前缀为 0x,字母使用小写的 a-f 表示 |
| %q | 使用 Go 语法以及必须时使用转义,以双引号括起来的字符串或者字节切片[]byte,或者是以单引号括起来的数字 |
| %s | 字符串。输出字符串中的字符直至字符串中的空字符(字符串以’\0‘结尾,这个’\0’即空字符) |
| %t | 以 true 或者 false 输出的布尔值 |
| %T | 使用 Go 语法输出的值的类型 |
| %x | 以十六进制表示的整型值(基数为十六),数字 a-f 使用小写表示 |
| %X | 以十六进制表示的整型值(基数为十六),数字 A-F 使用小写表示 |
复杂数据类型
数组
数组 是一个由 固定长度 的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在 Go 语言中很少直接使用数组。和数组对应的类型是 slice(切片) ,它是可以动态的增长和收缩的序列, slice 功能也更灵活。
数组声明
可以使用 [n]Type 来声明一个数组。其中 n 表示数组中元素的数量, Type 表示每个元素的类型。
package main
import "fmt"
func test01() {
// 声明时没有指定数组元素的值, 默认为零值
var arr [5]int
fmt.Println(arr) // [0 0 0 0 0]
arr[0] = 1
arr[1] = 2
arr[2] = 3
fmt.Println(arr) // [1 2 3 0 0]
}
func test02() {
// 直接在声明时对数组进行初始化
var arr1 = [5]int{1, 2, 3, 4, 5}
fmt.Println(arr1) // [1 2 3 4 5]
// 使用短声明
arr2 := [5]int{1, 2, 3, 4, 5}
fmt.Println(arr2) // [1 2 3 4 5]
// 部分初始化, 未初始化的为零值
arr3 := [5]int{2, 7}
fmt.Println(arr3) // [2 7 0 0 0]
// 可以通过指定索引,方便地对数组某几个元素赋值
arr4 := [5]int{1: 100, 4: 200}
fmt.Println(arr4) // [0 100 0 0 200]
// 直接使用 ... 让编译器为我们计算该数组的长度
arr5 := [...]int{1, 2, 3, 4, 5, 6}
fmt.Println(len(arr5)) // 6
}
func test03() {
// 特别注意数组的长度是类型的一部分,所以 [3]int 和 [5]int 是不同的类型
arr1 := [3]int{1, 2, 3}
arr2 := [5]int{1, 2, 3, 4, 5}
fmt.Printf("type of arr1 is %T\n", arr1) // type of arr1 is [3]int
fmt.Printf("type of arr2 is %T\n", arr2) // type of arr2 is [5]int
}
func test04() {
// 定义多维数组
arr := [3][2]string{
{"前端", "React"},
{"前端", "Vue"},
{"后端", "Go"}}
fmt.Println(arr) // [[前端 React] [前端 Vue] [后端 Go]]
}
func main() {
test01()
test02()
test03()
test04()
}
数组长度
内置 len 函数将返回数组长度
arr5 := [...]int{1, 2, 3, 4, 5, 6}
fmt.Println(len(arr5)) // 6
数组遍历
使用 for range 循环可以获取数组每个索引以及索引上对应的元素。
func showArr() {
arr := [...]string{"React", "Vue", "Go"}
for index, value := range arr {
fmt.Printf("arr[%d]=%s\n", index, value)
// arr[0]=React
// arr[1]=Vue
// arr[2]=Go
}
for _, value := range arr {
fmt.Printf("value=%s\n", value)
// value=React
// value=Vue
// value=Go
}
}
数组是值类型
Go 中的数组是值类型而不是引用类型。当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。
如果对新变量进行更改,不会影响原始数组。
func arrCopy() {
arr := [...]string{"React", "Vue", "Go"}
copy := arr
copy[2] = "Golang"
fmt.Println(arr) // [React Vue Go]
fmt.Println(copy) // [React Vue Golang]
}
切片(Slice)
切片是对数组的一个连续片段的引用,所以切片是一个引用类型。
切片本身不拥有任何数据,它们只是对现有数组的引用,每个切片值都会将数组作为其底层的数据结构。slice 的语法和数组很像,只是没有固定长度而已。
创建切片
使用 []Type 可以创建一个带有 Type 类型元素的切片。
// 声明整型切片
var numList []int
// 声明一个空切片
var numListEmpty = []int{}
也可以使用 make 函数构造一个切片,格式为 make([]Type, size, cap)
numList := make([]int, 3, 5)
可以通过对数组进行片段截取创建一个切片。
func silceArr() {
arr := [5]string{"React", "Vue", "Go", "Java", "Python"}
var s1 = arr[1:4]
fmt.Println(arr) // [React Vue Go Java Python]
fmt.Println(s1) // [) // [Vue Go Java]
}
切片的长度和容量
一个 slice 由三个部分构成:指针 、 长度 和 容量 。指针指向第一个 slice 元素对应的底层数组元素的地址,要注意的是 slice 的第一个元素并不一定就是数组的第一个元素。长度对应 slice 中元素的数目;长度不能超过容量,容量一般是从 slice 的开始位置到底层数据的结尾位置。简单的讲,容量就是从创建切片索引开始的底层数组中的元素个数,而长度是切片中的元素个数。
内置的 len 和 cap 函数分别返回 slice 的长度和容量。
s := make([]string, 3, 5)
fmt.Println(len(s)) // 3
fmt.Println(cap(s)) // 5
如果切片操作超出上限将导致一个 panic 异常。
s := make([]int, 3, 5)
fmt.Println(s[10]) //panic: runtime error: index out of range [10] with length 3
slice 是引用类型,所以不进行赋值,它的默认值是 nil
var numList []int
fmt.Println(numList == nil) // true
切片之间不能比较,因此不能使用 == 操作符来判断两个 slice 是否含有全部相等元素。特别注意,如果你需要测试一个 slice 是否是空的,使用 len(s) == 0 来判断,而不应该用 s == nil 来判断。
var numList []int
fmt.Print(len(numList) == 0) // true
切片元素的修改
切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中。
func modifySlice() {
var arr = [...]string{"React", "Vue", "Go", "Java", "Python"}
s := arr[:]
fmt.Println(arr) // [React Vue Go Java Python]
fmt.Println(s) // [React Vue Go Java Python]
s[2] = "Golang"
fmt.Println(arr) // [React Vue Golang Java Python]
fmt.Println(s) // [React Vue Golang Java Python]
}
arr[:] 没有起始值和结束值,默认就是 0 和 len(arr)
追加切片元素
使用 append 可以将新元素追加到切片上。
append 函数的定义是
func append(slice []Type, elems ...Type) []Type
其中 elems ...Type 在函数定义中表示该函数接受参数 elems 的个数是可变的。这些类型的函数被称为可变函数。
func appendSliceData() {
s := []string{"Vue"}
fmt.Println(s)
fmt.Println(cap(s))
s = append(s, "React")
fmt.Println(s)
fmt.Println(cap(s))
s = append(s, "Java", "Go")
fmt.Println(s)
fmt.Println(cap(s))
s = append(s, []string{"C++", "Python"}...)
fmt.Println(s)
fmt.Println(cap(s))
}
当新的元素被添加到切片时,如果容量不足,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回新的引用。现在新切片的容量是旧切片的两倍。
Map
在 Go 语言中,map 是散列表(哈希表)的引用。它是一个拥有键值对元素的无序集合,在这个集合中,键是唯一的,可以通过键来获取、更新或移除操作。无论这个散列表有多大,这些操作基本上是通过常量时间完成的。所有可比较的类型,如 整型 ,字符串 等,都可以作为 key 。
创建 Map
使用 make 函数传入键和值的类型,可以创建 map 。具体语法为 make(map[KeyType]ValueType) 。
// 创建一个键类型为 string 值类型为 int 名为 scores 的 map
scores := make(map[string]int)
steps := make(map[string]string)
也可以用 map 字面值的语法创建 map ,同时还可以指定一些最初的 key/value :
func makeMap() {
var test01 map[string]string = map[string]string{
"1": "Vue",
"2": "React",
"3": "Go",
}
fmt.Println(test01) // map[1:Vue 2:React 3:Go]
}
或者
test02 := map[string]string{
"1": "Vue",
"2": "React",
"3": "Go",
}
fmt.Println(test02)
Map 操作
添加元素
map[key] = value 向 map 添加元素。
test01["4"] = "Java"
更新元素
若 key 已存在,使用 map[key] = value 可以直接更新对应 key 的 value 值
test01["4"] = "python"
获取元素
直接使用 map[key] 即可获取对应 key 的 value 值, 如果 key 不存在,会返回其 value 类型的零值。
fmt.Print("获取元素", test01["4"]) // 获取元素python
删除元素
使用 delete(map, key) 可以删除 map 中的对应 key 键值对,如果 key 不存在,delete 函数会静默处理,不会报错。
delete(test01, "4")
判断 key 是否存在
map 中的某个 key 是否存在,可以使用下面的语法:value, ok := map[key]
v1, ok := test01["3"]
fmt.Println(ok) // true
fmt.Println(v1) // Go
v2, ok := test01["4"]
fmt.Println(ok) // false
fmt.Printf("%q", v2) // ""
这个语句说明 map 的下标读取可以返回两个值,第一个值为当前 key 的 value 值,第二个值表示对应的 key 是否存在,若存在 ok 为 true ,若不存在,则 ok 为 false 。
遍历 map
遍历 map 中所有的元素需要用 for range 循环。
for key, value := range test02 {
fmt.Printf("key: %s, value: %s\n", key, value)
}
获取 map 长度
使用 len 函数可以获取 map 长度
fmt.Println(len(test02)) // 3
map 是引用类型
当 map 被赋值为一个新变量的时候,它们指向同一个内部数据结构。因此,改变其中一个变量,就会影响到另一变量。
func mapReference() {
test03 := map[string]string{
"1": "Vue",
"2": "React",
"3": "Go",
}
fmt.Println("test03: ", test03) // map[1:Vue 2:React 3:Go]
test04 := test03
test04["1"] = "1111"
test04["2"] = "222"
test04["3"] = "3333"
fmt.Println("test03: ", test03) // map[1:1111 2:222 3:3333]
fmt.Println("test04: ", test04) // map[1:1111 2:222 3:3333]
}