分类
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Pointer
Slice
String
Struct
UnsafePointer
)
// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
// const declaration. It is zero-indexed.
const iota = 0 // Untyped int.
Go中数据类型可以分为两大类:
- 基本数据类型
- 数值型
- 整数类型,byte(等价unit8)rune(等价int32)、int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64
- 浮点类型,float(默认float64)、float32、float64
- 布尔型,bool
- 字符串,string
- 数值型
- 派生数据类型(复杂数据类型)
- 指针
- 数组
- 结构体
- 管道
- 函数
- 切片
- 接口
- map
注意:
- 没有单独的字符型,可以使用byte存储字符并格式化转换为字符
- int及uint在32位系统占4字节,在64位系统占8字节,即是一个机器字
零值
当声明该数据类型的变量时,不手动赋初值就会使用零值,也可认为是默认值
| 数据类型 | 默认值 |
|---|---|
| 整数类型 | 0 |
| 浮点类型 | 0.000000 |
| 布尔类型 | false |
| 字符串类型 | “” |
数值类型
字符处理
package main
import (
"fmt"
)
func main() {
// type byte = uint8
// 需要格式化输出
var v1 byte = 'c'
fmt.Printf("%T, %c", v1, v1)
}
浮点类型
package main
import (
"fmt"
)
func main() {
var v1 float32 = 3.14
var v2 float64 = -3.14
var v3 float64 = 314e-2
var v4 float64 = -314e-2
v5 := -.14
fmt.Println(v1, v2, v3, v4, v5)
}
布尔类型
布尔类型占一个字节
package main
import "fmt"
func main() {
var b bool = true // 事实上goland会提示类型可以省略
fmt.Println(b)
//// bool is the set of boolean values, true and false.
//type bool bool
//
//// true and false are the two untyped boolean values.
//const (
// true = 0 == 0 // Untyped bool.
// false = 0 != 0 // Untyped bool.
//)
}
字符串
Go中没有字符类型,故Go中字符串类型底层为字节数组[]byte
package main import "fmt" func main() { // 字符串遍历,range s := "Hello 世界" // len 不可以遍历 for i, value := range s { fmt.Printf("%d %c\n", i, value) } // 转为rune切片 r := []rune(s) for i := 0; i < len(r); i++ { fmt.Printf("%c\n", r[i]) } }
字符串一旦赋值,便不可更改
如果想要“修改”字符串,只能强转[]byte复制一份,修改后强转为string,不过byte只允许英文,使用[]rune支持中文字符
package main
import "fmt"
func main() {
var s1 = "a string"
fmt.Println(s1)
fmt.Printf("%T", s1) // string
var s2 = `
func xxx() {
...
}
...`
fmt.Println(s2) // 反引号原样输出
var s3 = "a " + "b" + // 这个加号必须在第一行明示编译器还没结束,与自动添加分号进行区分
"c"
fmt.Println(s3)
}
字符串操作
package main
import (
"fmt"
"strings"
)
func main() {
//查找子串在字符串中第一次出现的位置,没有返回-1
fmt.Println(strings.Index("Hello World", "llo"))
//是否包含子串
fmt.Println(strings.Contains("Hello World", "llo"))
//统计字符串中有几个字串
fmt.Println(strings.Count("Hello World", "llo"))
//不区分大小写比较字符串,区分大小写比较直接使用==
fmt.Println(strings.EqualFold("Hello World", "hello world"))
//字符串替换,-1表示全部替换???
fmt.Println(strings.Replace("Hello World", "ll", "ii", 3))
//字符串分割
fmt.Println(strings.Split("src/go-code/main", "/"))
//大小写转换
fmt.Println(strings.ToUpper("Hello World"))
fmt.Println(strings.ToLower("Hello World"))
//去除字符串两边空格
fmt.Println(strings.TrimSpace(" Hello World "))
//去除字符串两边指定字符
fmt.Println(strings.Trim("---Hello World---", "-"))
//去除字符串左边指定字符
fmt.Println(strings.TrimLeft("---Hello World---", "-"))
//去除字符串右边指定字符
fmt.Println(strings.TrimRight("---Hello World---", "-"))
//判断字符串前缀
fmt.Println(strings.HasPrefix("https://xxx.com", "http"))
//判断字符串后缀
fmt.Println(strings.HasSuffix("main.go", ".go"))
}
数组
- Go中数组是值类型
- 数组元素类型相同,但元素个数不同则两者类型不同,即数组长度是数组类型的一部分
- 数组声明后长度固定,不可动态变化
一维数组
package main
import "fmt"
func main() {
// 声明数组变量,随后逐一赋值
var a [3]float64
a[0] = 1.0
a[1] = .3
a[2] = 2
// 声明变量,随后赋值
var b [3]float64 = [3]float64{1, 2, 3}
fmt.Printf("%T\n", b)
// 可以省略类型,由编译器自动推断
var c = [3]float64{1, 2, 3}
fmt.Printf("%T\n", c)
// 可以使用不定长度,由编译器计算
var d = [...]float64{1, 2, 3}
fmt.Printf("%T\n", d)
// 数组支持按序号赋值
var e = [...]float64{2: 3, 0: 1, 1: 2}
fmt.Printf("%T\n", e)
fmt.Println(e)
}
package main
import "fmt"
func change(array *[3]float64) { //必须传入长度,长度不一样也是不一样的类型
// 对数组进行遍历,这里带不带*都可,不带*Go会自动处理
for i, v := range *array {
fmt.Println(i, v)
// 单纯赋值不会改变其值
// v = 1
// v += 1
// 必须修改数组元素才行,两者均可
(*array)[i] += 1
array[i] += 1
}
}
func main() {
// 由于数组是值类型,所以想要改变数组需要将其引用传递进去
var a = [3]float64{1, 2, 3}
change(&a)
for _, v := range a {
fmt.Println(v)
}
}
数组遍历
package main
import "fmt"
func main() {
var a = [...]float64{1, 2, 3}
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
for _, value := range a {
fmt.Printf("%.2f\n", value)
}
}
二维数组
package main
import (
"fmt"
)
func main() {
// 二维数组与一维数组声明方式同理
var arr = [3][3]int{{1, 1}, {1: 1}}
fmt.Println(arr)
// 遍历方式同理
for index1, value1 := range arr {
for index2, value2 := range value1 {
fmt.Println(index1, index2, value2)
}
}
}
指针
- 可以通过指针改变指向的值
- 指针变量接受地址
package main
import "fmt"
func main() {
// 定义一个变量 int32
i := 1
// 使用 & 取地址,p是一个指针类型,当然这里类型可以省略
var p *int = &i
// 打印指针值
fmt.Println(p)
fmt.Printf("%+v", p)
}
数据类型转换
Go中没有隐式类型转换,不同类型间必须显式类型转换(强制类型转换)
package main
import "fmt"
func main() {
// 常量与变量不匹配会编译失败
// 基本数据类型和引用数据类型的类型转换是不一样的,基本数据类型复制
var n1 int8 = 256
fmt.Println(n1)
// cannot use 256 (untyped int constant) as int8 value in variable declaration (overflows)
}
数字间转换
package main
import (
"fmt"
)
func main() {
// 整数高低精度互转
var n1 int8 = 1
var n2 int32 = 1000
n1 = int8(n2) // 可以显式转换,但会丢失精度
n2 = int32(1)
fmt.Println("整数高低精度互转", n1, n2)
// 浮点高低精度互转
var n3 float32 = 1
var n4 float64 = 2
n3 = float32(n4)
n4 = float64(5)
fmt.Println("浮点高低精度互转", n3, n4)
// 整数与浮点互转
var n5 int8 = 1
var n6 float32 = .6
n5 = int8(n6)
n6 = float32(1) // 直接截断
fmt.Println("整数与浮点互转", n5, n6)
}
字符串与其他类型相互转换
package main
import (
"fmt"
"strconv"
)
func main() {
// 字符串与(整数类型、浮点数类型、布尔类型)互转,两种方式,fmt和strconv
// 字符串与整数类型互转
var n1 int64 = 66 // int64 -> string
res1 := fmt.Sprintf("%v", n1) // sprintf
fmt.Printf("%T, %v\n", res1, res1)
res1 = strconv.FormatInt(n1, 10) // format
fmt.Printf("%T, %v\n", res1, res1)
var n2 string = "66" // string -> int64
res2, _ := strconv.ParseInt(n2, 10, 64) // parse
fmt.Printf("%T, %v\n", res2, res2)
// 字符串与浮点类型互转
var n3 float64 = 0.03 // float64 -> string
res3 := fmt.Sprintf("%v", n3) // sprintf
fmt.Printf("%T, %v\n", res3, res3)
res3 = strconv.FormatFloat(n3, 'f', 9, 64) // format
fmt.Printf("%T, %v, %q\n", res3, res3, res3) // 注意这里
var n4 string = "0.06" // string -> float64
res4, _ := strconv.ParseFloat(n4, 64) // parse
fmt.Printf("%T, %v\n", res4, res4)
// 字符串与布尔值互转
var n5 bool = true // bool -> string
res5 := fmt.Sprintf("%v", n5) // sprintf
fmt.Printf("%T, %v\n", res5, res5)
res5 = strconv.FormatBool(n5) // format
fmt.Printf("%T, %v\n", res5, res5)
var n6 string = "false"
res6, _ := strconv.ParseBool(n6) // parse
fmt.Printf("%T, %v\n", res6, res6)
n7, _ := strconv.Atoi("123456789")
fmt.Printf("%T, %v\n", n7, n7)
n8 := strconv.Itoa(123456789)
fmt.Printf("%T, %v\n", n8, n8)
var n9 string = "shw3gsdf54dg"
res9 := []byte(n9)
fmt.Printf("%T, %v\n", res9, res9)
var n10 = [...]int{115, 104, 119, 51, 103, 115, 100, 102, 53, 52, 100, 103}
fmt.Println(n10)
}
package main
import "fmt"
func main() {
// 字符串 byte
var b = []byte("Hello World")
fmt.Printf("%T\n", b)
var str = string([]byte{9})
fmt.Printf("%T\n", str)
}
转换失败时
package main
import (
"fmt"
"strconv"
)
func main() {
s1 := "Hello World"
n1, err := strconv.ParseInt(s1, 10, 64)
fmt.Printf("%+v\n%+v", n1, err)
// 遇到错误自动处理为默认值,然后通知一下
// 0
// strconv.ParseInt: parsing "Hello World": invalid syntax
}
map,映射
自动扩容
var m map[string]string
var m map[string]map[string]string,多重map
key的类型:
- bool、数字、string、指针、channel
- 只包含上述类型的接口、结构体、数组
- slice、map、func不可以
package main
import "fmt"
func main() {
// 定义时直接写明常量值
var singleMap map[string]string = map[string]string{"1": "1", "2": "2", "3": "3"}
singleMap["A"] = "A"
singleMap["B"] = "B"
singleMap["C"] = "C"
fmt.Println(singleMap)
// 使用make
singleMap = make(map[string]string, 10)
singleMap["0"] = "0"
delete(singleMap, "0") // 内置函数,专门用于删除 key-value
value, flag := singleMap["0"]
fmt.Printf("%q %#v", value, flag)
// 多重map
var multiMap map[string]map[string]string
fmt.Println(multiMap)
}
package main
import "fmt"
func main() {
m := map[string]string{"1": "1", "2": "2", "3": "3"}
for key, value := range m {
fmt.Println(key, value)
//fmt.Printf(key, value)
//1%!(EXTRA string=1)2%!(EXTRA string=2)3%!(EXTRA string=3)
//体现Go错误自己处理的思想
}
}
slice,切片
切片是从堆开辟的“数组”,如果切片从数组创建,切片容量=数组元素数量-切片起始元素之前的元素数量(不包括起始元素)
[]int int切片
[]map[string]string map切片
创建切片
package main
import (
"fmt"
)
func main() {
// 1.从数组创建切片
var arr1 = [...]int{1, 1, 1, 1, 1, 1, 1, 2}
slice := arr1[1:3]
fmt.Println(slice, len(slice), cap(slice)) // 从数组创建切片,切片容量是切片在数组中起始位置到末尾所有元素的数量
fmt.Printf("%T\n", slice)
// 2.make创建切片
var arr2 = make([]int, 3, 10)
fmt.Println(arr2, len(arr2), cap(arr2))
fmt.Printf("%T\n", arr2)
// 二维数组的切片
var arr3 = [3][3]int{{1, 1}, {1: 1}}
fmt.Printf("%T", arr3[1:2])
// slice内存
// 起始地址(底层数组地址)、长度、容量 描述slice
// 起始地址指向的堆内存 存储数据
}
切片可以简写
arr[0:end] arr[:end]
arr[start:len(arr)] arr[start:]
arr[0:len(arr)] arr[start:end] arr[:]
从数组创建的切片是对数据的复制
package main
import "fmt"
func main() {
var arr1 = [...]int{1, 1, 1}
var arr2 = arr1[:]
var arr3 = arr2
fmt.Printf("%T %T %T\n", arr1, arr2, arr3) // arr1 是数组,arr2和arr3是切片
arr1[0] = 0
fmt.Println(arr1, arr2, arr3)
fmt.Printf("%p %p %p\n", &arr1, &arr2, &arr3) // 注意切片和数组输出地址的区别
arr2 = append(arr2, 9)
fmt.Println(arr1, arr2, arr3)
fmt.Printf("%p %p %p\n", &arr1, &arr2, &arr3) // 注意切片和数组输出地址的区别
arr1[0] = 8
fmt.Println(arr1, arr2, arr3)
fmt.Printf("%p %p %p\n", &arr1, &arr2, &arr3) // 注意切片和数组输出地址的区别
}
切片可以再切片
切片合并、切片拷贝
package main
import "fmt"
func main() {
// 切片合并
var arr1 = []int{1, 1, 1}
var arr2 = []int{2, 2, 2}
var arr3 = append(arr1, arr2...) //注意这里是把arr2作为可变参数传进去的
fmt.Printf("%v %T %d\n", arr3, arr3, cap(arr3))
// 注意arr3是一个全新的切片,append后会生成一个全新的切片
arr1[0] = 0
arr2[1] = 0
arr3[2] = 0
fmt.Println(arr1, arr2, arr3)
// 切片拷贝
arr4 := []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
arr5 := make([]int, 3)
copy(arr5, arr4) // copy就是用于切片拷贝的,只能用于切片类型
fmt.Println(arr4, arr5)
}
数组与切片的地址比较
package main
import "fmt"
func main() {
//关于数组和切片的地址,注意切片arr5和&arr5都格式为%p的不同
arr4 := [...]int{1, 1, 1}
arr5 := arr4[:]
fmt.Printf("%p %p %p %p %p %p\n", arr4, &arr4, &(arr4[0]), arr5, &arr5, &(arr5[0]))
fmt.Println(arr4, &arr4, &arr4[0], arr5, &arr5, &arr5[0])
//%!p([3]int=[1 1 1]) 0xc00000e198 0xc00000e198 0xc00000e198 0xc000008078 0xc00000e198
//[1 1 1] &[1 1 1] 0xc00000e198 [1 1 1] &[1 1 1] 0xc00000e198
}
bytes
reader
buffer
类型断言
类型断言
package main
import "fmt"
type name struct{}
func main() {
var a any
a = name{}
var b name
var f bool
b, f = a.(name)
fmt.Println(b, f)
if b, f := a.(name); f {
fmt.Println(b, f)
}
}
package main
import (
"fmt"
)
func TypeJudge(item any) {
//type switch
//Go特有的断言类型的switch语句,比较的是类型
switch item.(type) {
case bool:
fmt.Println("bool")
case float64:
fmt.Println("float64")
case nil:
fmt.Println("nil")
case string:
fmt.Println("string")
case *string:
fmt.Println("*string")
}
}
func main() {
s := ""
TypeJudge(&s)
}
接口类型(interface)
空接口interface{}是没有任何方法的接口,所有类型均满足空接口,则空接口可以存储任何类型的值
var any interface{}
any = "a string"
any = 123
any = true
基于空接口实现泛型编程
func Println(v interface{}) {
fmt.Println(v)
}
处理未知类型的类
data := []byte(`{"name":"John","age":30}`)
var result map[string]interface{}
json.Unmarshal(data, &result)
自定义类型与类型别名
自定义类型可以绑定方法
package main
import "fmt"
// 自定义类型 myInt
type myInt int
// 类型别名 yourInt
type yourInt = int
func main() {
var n1 myInt = 1
fmt.Printf("%T %v\n", n1, n1) // main.myInt 1
var n2 yourInt = 1
fmt.Printf("%T %v\n", n2, n2) // int 1
}