一、Go类型系统概述
Go是静态类型 语言,编译时检查类型,同时通过类型推断 简化代码。类型分为基础类型 和复合类型两大类。
二、基本数据类型
1. 布尔类型 (bool)
go
var b1 bool = true
var b2 = false // 类型推断
b3 := true // 简短声明
// 零值:false
// 大小:1字节
// 只能为 true/false,不能与数字互转
2. 整数类型
有符号整数(默认int)
go
// 明确大小的类型
var i1 int8 = 127 // -128 ~ 127
var i2 int16 = 32767 // -32768 ~ 32767
var i3 int32 = 100000 // -21亿 ~ 21亿,别名rune
var i4 int64 = 1<<63-1 // -9.22e18 ~ 9.22e18
// 平台相关类型(推荐使用)
var i5 int = 100 // 32位系统占4字节,64位系统占8字节
var i6 = 42 // 自动推断为int
// 字面量类型推断
n1 := 42 // int
n2 := 3.14 // float64
n3 := 1 << 33 // 如果int是32位,这会溢出,最好用int64(1) << 33
无符号整数
go
var u1 uint8 = 255 // 0~255,别名byte
var u2 uint16 = 65535
var u3 uint32 = 0xFFFFFFFF
var u4 uint64 = 1<<64-1
var u5 uint = 100 // 平台相关
var u6 uintptr // 存储指针,用于底层编程
// byte是uint8的别名
var b byte = 'A' // 65
var b2 byte = 0xFF // 255
特殊整数类型
go
// rune:int32别名,表示Unicode码点
var r1 rune = 'A' // 65
var r2 rune = '中' // 20013
var r3 rune = '\u4e2d' // "中"的Unicode
var r4 rune = 0x4e2d // 同样表示"中"
// byte与rune转换
b := byte('A') // 65
r := rune(b) // 65
3. 浮点数类型
go
var f1 float32 = 3.1415926
var f2 float64 = 3.141592653589793
// 默认float64
f3 := 3.14 // float64
f4 := float32(3.14) // 必须显式转换
// 科学计数法
f5 := 1.23e4 // 12300
f6 := 1.23e-4 // 0.000123
// 特殊值
var inf float64 = 1.0 / 0.0 // +Inf
var negInf = -1.0 / 0.0 // -Inf
var nan = 0.0 / 0.0 // NaN
4. 复数类型
go
var c1 complex64 = 3 + 4i // 实部虚部都是float32
var c2 complex128 = 3 + 4i // 实部虚部都是float64
c3 := complex(3.0, 4.0) // 使用complex函数
// 操作
realPart := real(c1) // 3.0
imagPart := imag(c1) // 4.0
conjugate := complex(real(c1), -imag(c1))
5. 字符串类型 (string)
go
// 基本操作
s1 := "Hello, 世界"
len1 := len(s1) // 13字节(非字符数)
len2 := len([]rune(s1)) // 8字符
// 遍历(按字节)
for i := 0; i < len(s1); i++ {
fmt.Printf("%02x ", s1[i])
}
// 遍历(按字符)
for i, ch := range s1 { // ch是rune类型
fmt.Printf("%d: %c\n", i, ch)
}
// 原始字符串(反引号)
raw := `第一行
第二行\t不会转义
第三行`
三、复合数据类型
1. 数组 (Array) - 固定长度
go
// 声明方式
var arr1 [3]int // [0 0 0]
var arr2 = [3]int{1, 2, 3}
arr3 := [...]int{1, 2, 3, 4} // 编译器推断长度
arr4 := [5]int{1: 10, 3: 30} // [0 10 0 30 0]
// 多维数组
var matrix [2][3]int = [2][3]int{
{1, 2, 3},
{4, 5, 6},
}
// 数组是值类型
a1 := [2]int{1, 2}
a2 := a1 // 复制整个数组
a2[0] = 100 // 不影响a1
2. 切片 (Slice) - 动态数组
go
// 创建方式
var s1 []int // nil切片,len=0, cap=0
s2 := []int{} // 空切片,len=0, cap=0(有底层数组)
s3 := make([]int, 5) // len=5, cap=5
s4 := make([]int, 3, 10) // len=3, cap=10
s5 := []int{1, 2, 3} // 字面量
s6 := arr1[1:3] // 从数组创建
// 切片操作
slice := []int{1, 2, 3, 4, 5}
sub1 := slice[1:3] // [2, 3]
sub2 := slice[:3] // [1, 2, 3]
sub3 := slice[2:] // [3, 4, 5]
// 追加和扩容
slice = append(slice, 6) // 容量不足时自动扩容(通常2倍)
slice = append(slice, 7, 8, 9)
// 复制切片
src := []int{1, 2, 3}
dst := make([]int, 2)
n := copy(dst, src) // n=2,只复制前两个元素
// 删除元素
slice = []int{1, 2, 3, 4, 5}
slice = append(slice[:2], slice[3:]...) // 删除索引2
3. 映射 (Map) - 键值对
go
// 创建方式
var m1 map[string]int // nil map,不能直接使用
m2 := make(map[string]int) // 空map
m3 := make(map[string]int, 10) // 指定初始容量
m4 := map[string]int{
"apple": 5,
"banana": 2,
}
// 操作
m4["orange"] = 3 // 添加/修改
value, exists := m4["apple"] // exists=true
delete(m4, "banana") // 删除
// 遍历(无序)
for key, value := range m4 {
fmt.Println(key, value)
}
// 注意事项
var nilMap map[string]int
// nilMap["key"] = 1 // panic!
4. 结构体 (Struct)
go
// 定义
type Person struct {
Name string
Age int
Email string
private string // 小写开头,不可导出
}
// 初始化
p1 := Person{"Alice", 25, "alice@test.com", ""} // 必须按顺序
p2 := Person{Name: "Bob", Age: 30} // 部分字段
p3 := Person{} // 零值
p4 := &Person{Name: "Charlie"} // 返回指针
// 匿名字段
type Point struct {
X, Y int
}
// 嵌入(组合)
type Circle struct {
Point // 匿名字段
Radius int
}
c := Circle{Point{1, 2}, 3}
fmt.Println(c.X, c.Y) // 可直接访问
5. 指针 (Pointer)
go
var x int = 10
var p *int = &x
// 操作
fmt.Println(*p) // 10
*p = 20
fmt.Println(x) // 20
// new函数
ptr := new(int) // 分配零值,返回指针
*ptr = 100
// 指针的零值
var p2 *int // nil
// *p2 = 10 // panic!
// 结构体指针(可直接访问字段)
type Vertex struct { X, Y int }
v := &Vertex{1, 2}
v.X = 10 // 等价于 (*v).X
6. 函数类型
go
// 函数类型定义
type MathOp func(int, int) int
type Predicate func(string) bool
// 函数变量
var add MathOp = func(a, b int) int {
return a + b
}
// 高阶函数
func calculate(x, y int, op MathOp) int {
return op(x, y)
}
// 闭包
func makeMultiplier(factor int) func(int) int {
return func(x int) int {
return x * factor
}
}
double := makeMultiplier(2)
result := double(5) // 10
7. 通道 (Channel)
go
// 创建
ch1 := make(chan int) // 无缓冲
ch2 := make(chan string, 5) // 缓冲大小为5
// 单向通道
var sendOnly chan<- int = ch1 // 只能发送
var recvOnly <-chan int = ch1 // 只能接收
// select多路复用
select {
case msg := <-ch1:
fmt.Println(msg)
case ch2 <- "hello":
fmt.Println("sent")
case <-time.After(1 * time.Second):
fmt.Println("timeout")
}
// 关闭和检测
close(ch1)
value, ok := <-ch1 // ok=false表示通道已关闭
8. 接口 (Interface)
go
// 定义
type Writer interface {
Write([]byte) (int, error)
}
// 空接口
var any interface{}
any = 42
any = "hello"
any = []int{1, 2, 3}
// 类型断言
if str, ok := any.(string); ok {
fmt.Println(str)
}
// 类型开关
switch v := any.(type) {
case int:
fmt.Printf("int: %d\n", v)
case string:
fmt.Printf("string: %s\n", v)
default:
fmt.Printf("unknown: %T\n", v)
}
四、类型转换详解
1. 数值类型转换
go
// 必须显式转换
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
// 注意精度损失和溢出
var f2 float64 = 3.99
var i2 int = int(f2) // 3,截断小数
var big int64 = 1 << 40
var small int32 = int32(big) // 溢出,结果为0
// 常量转换(编译时)
const Pi = 3.14159
const IntPi = int(Pi) // 3
2. 字符串转换
go
import "strconv"
// 字符串 ↔ 数值
s1 := "123"
num, err := strconv.Atoi(s1) // 字符串转int
s2 := strconv.Itoa(456) // int转字符串
// Parse系列
b, _ := strconv.ParseBool("true")
f, _ := strconv.ParseFloat("3.14", 64) // bitSize: 32或64
i64, _ := strconv.ParseInt("100", 10, 64) // base, bitSize
ui64, _ := strconv.ParseUint("100", 10, 64)
// Format系列
s3 := strconv.FormatBool(true)
s4 := strconv.FormatInt(-42, 16) // "-2a",16进制
s5 := strconv.FormatUint(42, 2) // "101010",二进制
s6 := strconv.FormatFloat(3.1415, 'f', 2, 64) // 'f'格式,2位小数
3. 字符串 ↔ 字节/符文切片
go
// string ↔ []byte(有拷贝)
str := "Hello, 世界"
bytes := []byte(str) // 转换为字节切片
str2 := string(bytes) // 转换回来
// string ↔ []rune(有拷贝)
runes := []rune(str) // [72 101 108 108 111 44 32 19990 30028]
str3 := string(runes)
// 危险:数字直接转字符串
var n int = 65
s := string(n) // "A" 而不是 "65"!
// 正确做法:
s = strconv.Itoa(n) // "65"
// []byte ↔ []rune(通过string中转)
bytes = []byte{228, 184, 173} // "中"的UTF-8
runes = []rune(string(bytes)) // [20013]
4. unsafe转换(高风险)
go
import "unsafe"
// 1) 字符串 ↔ 字节切片(零拷贝,但危险)
func StringToBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(&struct {
string
int
}{s, len(s)}))
}
func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// 2) 数字类型转换
var floatBits float64 = 3.14
var intBits uint64 = *(*uint64)(unsafe.Pointer(&floatBits))
// 3) 结构体转换(相同内存布局)
type A struct { X, Y int }
type B struct { X, Y int }
var a A = A{1, 2}
var b *B = (*B)(unsafe.Pointer(&a))
5. 自定义类型转换
go
// 方法实现转换
type Celsius float64
type Fahrenheit float64
func CToF(c Celsius) Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}
func FToC(f Fahrenheit) Celsius {
return Celsius((f - 32) * 5 / 9)
}
// 实现Stringer接口
func (c Celsius) String() string {
return fmt.Sprintf("%.1f°C", c)
}
五、类型断言与类型开关
1. 类型断言
go
var i interface{} = "hello"
// 安全断言
s, ok := i.(string) // s="hello", ok=true
f, ok := i.(float64) // f=0.0, ok=false
// 直接断言(可能panic)
s2 := i.(string) // 成功
// s3 := i.(int) // panic!
// 多返回值函数中的使用
if s, ok := getValue().(string); ok {
// 安全使用s
}
2. 类型开关
go
func describe(i interface{}) {
switch v := i.(type) {
case nil:
fmt.Println("nil")
case int:
fmt.Printf("int: %d\n", v)
case string:
fmt.Printf("string: %s\n", v)
case bool:
fmt.Printf("bool: %v\n", v)
case []interface{}:
fmt.Printf("slice of length %d\n", len(v))
case map[string]interface{}:
fmt.Printf("map with %d keys\n", len(v))
case func(int) int:
fmt.Printf("function, call result: %d\n", v(10))
default:
fmt.Printf("unknown type: %T\n", v)
}
}
六、零值与初始化
1. 所有类型的零值
go
bool // false
int // 0
float64 // 0.0
string // ""
pointer // nil
slice // nil
map // nil
channel // nil
interface{} // nil
func // nil
array // 所有元素为零值
struct // 所有字段为零值
2. 初始化方式
go
// 零值初始化
var a int // 0
var b string // ""
// 显式初始化
var c int = 10
var d = 20 // 类型推断
// 简短声明(函数内)
e := 30
f := "hello"
// 多变量初始化
var x, y = 1, "a"
i, j := 10, 20
// 交换值
i, j = j, i // 不需要临时变量
七、类型比较与相等性
1. 可比较类型
go
// 可比较:== 和 !=
bool
int, float, string
pointer
channel
interface{}
array(当元素可比较时)
struct(当所有字段可比较时)
// 不可比较
slice(除了与nil比较)
map(除了与nil比较)
func
2. 比较规则
go
// 基本类型
fmt.Println(1 == 1) // true
fmt.Println(3.14 == 3.14) // true
fmt.Println("hello" == "hello") // true
// 接口类型
var i1, i2 interface{} = 10, 10
fmt.Println(i1 == i2) // true
// 切片只能与nil比较
var s []int = nil
fmt.Println(s == nil) // true
// fmt.Println(s1 == s2) // 编译错误!
// map也只能与nil比较
var m map[string]int
fmt.Println(m == nil) // true
八、类型别名与定义
1. 类型别名 (Type Alias)
go
// 完全相同的类型
type ByteAlias = byte
type StringAlias = string
var b byte = 'A'
var ba ByteAlias = b // 可直接赋值
// 别名可用于解耦
type ID = int64
type Timestamp = int64
func Process(id ID, ts Timestamp) {
// 实际上是两个int64,但语义不同
}
2. 类型定义 (Type Definition)
go
// 新类型
type Celsius float64
type Fahrenheit float64
var c Celsius = 20
var f Fahrenheit = 68
// c = f // 编译错误!
// 但可显式转换
c = Celsius(f)
总结
Go的类型系统设计哲学:
- 显式优于隐式 - 类型转换必须明确
- 简单一致 - 零值机制统一
- 类型安全 - 编译时检查
- 实用主义 - 接口和类型断言提供灵活性
掌握Go的类型系统需要理解:
- 值类型 vs 引用类型
- 零值机制
- 类型转换的显式性
- 接口的动态特性
- 类型安全和运行时安全的平衡
通过合理使用类型定义、接口和类型转换,可以编写出既安全又灵活的Go代码。