数据类型
值类型和引用类型
在golang中,同其他语言相同,可以将类型大致分为两类:值类型与引用类型。
其中,值类型存储在栈中,他们在编译期就可以确定变量所占用的空间,并且值类型变量直接指向栈空间的值。使用等号=
将一个变量的值赋给另一个变量时,如j = i
,实际上是在内存中将 i
的值进行了拷贝。我们可以通过 &i
获取变量 i
的内存地址。在golang中,属于值类型的数据类型包含:
数字类型
布尔类型
字符串类型
数组:数组也存放在栈空间中,这个与Java不同
结构体:因为是值传递,所以在方法之间传递会进行值复制,如果结构体较大,需要使用指针
而引用类型拥有更复杂的存储结构,推崇存储在堆内存中,编译时一般无法确定其所占空间:
需要通过make创建并分配内存
初始化一系列属性:指针、长度、哈希分布、数据队列等。一个引用类型的变量
r1
存储的是r1
的值所在的内存地址(数字),或内存地址中第一个元素所在的位置,这个内存地址被称之为指针,这个指针实际上也被存在另外的某一个变量中。
在golang中,是引用类型的有:
指针
管道 channel
接口 interface
映射 map
函数 function
**值类型在传递参数时,进行值拷贝,引用类型则是引用拷贝。**主要是赋值的区别。
类型零值
零值也就是默认值,当一个类型声明之后没有被初始化,那么他的值就是零值,每种类型都有对应的零值,其中,引用类型对应的零值都为nil
:
数字类型,包含整型、浮点型、复数
0
布尔型
false
字符串
""
结构体
结构体中的每个字段都是零值
引用类型:slice
, pointer
,map
,channel
,function
,interface
nil
nil详解
在Go语言中,布尔类型的零值(初始值)为 false
,数值类型的零值为 0
,字符串类型的零值为空字符串""
,而指针、切片、映射、通道、函数和接口的零值则是 nil
。
注意,nil是一个值,像0一样,代表不存在
注意,使用nil
,要注意下面几点:
不是关键字和保留字
因为不是关键字和保留字,只是一个标识符,类似int等,所以可以定义一个名称为
nil
的变量:var nil = errors.New("my god")
没有默认类型
package main import ( "fmt" ) func main() { fmt.Printf("%T", nil) // .\main.go:9:10: use of untyped nil print(nil) }
引用类型的零值(初始值)都是nil
package main import ( "fmt" ) func main() { var m map[int]string var ptr *int var c chan int var sl []int var f func() var i interface{} fmt.Printf("%#v\n", m) // map[int]string(nil) fmt.Printf("%#v\n", ptr) // (*int)(nil) fmt.Printf("%#v\n", c) // (chan int)(nil) fmt.Printf("%#v\n", sl) // []int(nil) fmt.Printf("%#v\n", f) // (func())(nil) fmt.Printf("%#v\n", i) // <nil> }
不同引用类型的nil值,指针是相同的
package main import ( "fmt" ) func main() { var arr []int var num *int fmt.Printf("%p\n", arr) // 0x0 fmt.Printf("%p", num) // 0x0 }
不同引用类型的nil值,占用大小不同
package main import ( "fmt" "unsafe" ) func main() { var p *struct{} fmt.Println( unsafe.Sizeof( p ) ) // 8 var s []int fmt.Println( unsafe.Sizeof( s ) ) // 24 var m map[int]bool fmt.Println( unsafe.Sizeof( m ) ) // 8 var c chan string fmt.Println( unsafe.Sizeof( c ) ) // 8 var f func() fmt.Println( unsafe.Sizeof( f ) ) // 8 var i interface{} fmt.Println( unsafe.Sizeof( i ) ) // 16 }
两个
nil
字面量比较package main import ( "fmt" ) func main() { fmt.Println(nil==nil) // 报错 }
两个值都为
nil
的变量不可比较package main import ( "fmt" ) func main() { // 不同类型变量 var m map[int]string var ptr *int fmt.Printf(m == ptr) // invalid operation: arr == ptr (mismatched types []int and *int) }
package main import ( "fmt" ) func main() { // 相同类型变量 var s1 []int var s2 []int fmt.Printf(s1 == s2) // invalid operation: s1 == s2 (slice can only be compared to nil) }
变量(有可能是nil 或者 非nil)可以与nil值比较:
package main import ( "fmt" ) func main() { var s1 []int fmt.Println(s1 == nil) // true }
基本数据类型
数值型
整型
无符号整型
uint8
1
0 ~ 255
百
0
uint16
2
0 ~65535
6万多
0
uint32
3
0 ~ 4294967295
40多亿
0
uint64
4
0 ~ 18446744073709551615
大到没有概念
0
uint
系统决定,无符号整型
32位系统位int32
,64位系统位int64
0
var ui2 uint8 = 1
fmt.Printf("类型 uint8, 长度=%d字节 \n", unsafe.Sizeof(ui2))
var ui3 uint16 = 1
fmt.Printf("类型 uint16, 长度=%d字节 \n", unsafe.Sizeof(ui3))
var ui4 uint32 = 1
fmt.Printf("类型 uint32, 长度=%d字节 \n", unsafe.Sizeof(ui4))
var ui5 uint64 = 1
fmt.Printf("类型 uint64, 长度=%d字节 \n", unsafe.Sizeof(ui5))
有符号整型
int8
1
-128 ~ 127
正负百
0
int16
2
-32768 ~ 32767
正负3万多
0
int32
3
-2147483648 ~ 2147483647
正负大20多亿
0
int64
4
-9223372036854775808 ~ 9223372036854775807
正负大到没有概念
0
int
系统决定,有符号整型
32位系统位int32
,64位系统位int64
0
var i2 int8 = 1
fmt.Printf("类型 int8, 长度=%d字节 \n", unsafe.Sizeof(i2))
var i3 int16 = 1
fmt.Printf("类型 int16, 长度=%d字节 \n", unsafe.Sizeof(i3))
var i4 int32 = 1
fmt.Printf("类型 int32, 长度=%d字节 \n", unsafe.Sizeof(i4))
var i5 int64 = 1
fmt.Printf("类型 int64, 长度=%d字节 \n", unsafe.Sizeof(i5))
字符整型
byte
uint8
的别名
用与表示一个ASCII
rune
int32
的别名
用于表示一个Unicode代码点
// 获取对应字符的ASCII码值
var a byte = 'a'
fmt.Println(a)
// 将字符串转换为Unicode码点
first := "Hello, 世界"
fmt.Println([]rune(first))
表示一个ASCII:
var ch byte = 65
var ch byte = '\x41' // 16进制
var ch byte = 'A'
表示一个Unicode代码点:
var ch byte ='\u0041' // ASCII码值 六十进制41 十进制65 对应大写字母A
fmt.Printf("%c", ch) // ASCII对应单个字节,故可以使用byte类型接收
var ch2 rune = '\u266b' // 对应ASCII字符 🎵
fmt.Printf("%c", ch2) // 占用四个字节,故使用rune接收
var ch3 int64 = '\U0001F4B8' // 部分ASCII码值(比如生僻汉字),会占用四个字节以上,💸
fmt.Printf("%c", ch3) // 所以使用`\U`大写U字母来表示一个Unicode字符,占用8个字节,不够前面补0
浮点型
float32
(不常用)
3
IEEE-754 1.401298464324817070923729583289916131280e-45 ~ 3.402823466385288598117041834516925440e+38
精度6位小数
0
float64
4
IEEE-754 4.940656458412465441765687928682213723651e-324 ~ 1.797693134862315708145274237317043567981e+308
精度15位小数
0
fmt.Println("---------------- 浮点型 ------------------")
f0 := 3.14
fmt.Printf("类型推断: %T \n", f0)
var f1 float32 = 1
fmt.Printf("类型 float32, 长度=%d字节 \n", unsafe.Sizeof(f1))
var f2 float64 = 1
fmt.Printf("类型 float64, 长度=%d字节 \n", unsafe.Sizeof(f2))
// float32可能会有精度丢失,所以通常使用float64
var f3 float32 = 314e-2 // 314 / 10^2
fmt.Printf("指数方式表示: %f \n", f3)
var f4 float32 = 0.0314e2 // 0.0314 * 10^2
fmt.Printf("指数方式表示: %f \n", f4)
复数
我们把形如 z=a+bi(a、b均为实数)的数称为复数。其中,a 称为实部,b 称为虚部,i 称为虚数单位。当 z 的虚部 b=0 时,则 z 为实数;当 z 的虚部 b≠0 时,实部 a=0 时,常称 z 为纯虚数。复数域是实数域的代数闭包,即任何复系数多项式在复数域中总有根。
复数的运算:
加法法则:(a+bi)+(c+di)=(a+c)+(b+d)i`
减法法则:(a+bi)-(c+di)=(a-c)+(b-d)i`
乘法法则:
(a+bi)(c+di)=(ac-bd)+(bc+ad)i
除法法则:
complex64
8
(0+0i)
complex128
16
(0+0i)
var name complex128 = complex(x, y)
复数的值由三部分组成 RE + IMi
,其中 RE
是实数部分,IM
是虚数部分,RE
和 IM
均为 float
类型,而最后的 i
是虚数单位。
复数的定义和运算:
var x complex128 = complex(1, 2) // 1. 入门+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x + y) // "(4+6i)" 加法运算
fmt.Println(x * y) // "(-5+10i)" 惩罚运算
fmt.Println(real(x * y)) // "-5" 获取实部
fmt.Println(imag(x * y)) // "10" 获取虚部
布尔型
bool
1
true
,false
false
var a bool = true
fmt.Printf("bool的类型为: %T, 长度为 %d \n", a, unsafe.Sizeof(a))
字符串
字符串是一个不可变的UTF-8字符序列,并且每个属于ASCII码的字符占用单个字节,其他字符则是占用2-4个字节
不同于C、C++、Java,他们的UTF-8字符长度至少会占用两个字节
Go语言这样做不仅减少了内存和硬盘空间占用,同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码
双引号字符串支持转义字符
访问字符
var a string = "Hello 世界"
fmt.Printf("%c \n", []byte(a)[0]) // H
fmt.Printf("%c \n", []rune(a)[7]) // 界
字符拼接 "+"
fmt.Println("a" + "b") // 注意,其他类型不会进行字符串转换
多行字符串
多行字符串中的内容都会原样输出,转义字符不会生效。通常用语内嵌代码或者数据
const str = `第一行
第二\n行
第三行`
fmt.Println(str)
输出结果为:
第一行
第二\n行
第三行
类型转换
数值类型相互转换
// GO语言数据类型转换,只有显示转换(强制类型转换),没有隐式转换
fmt.Println("----------- 基本数据类型互相转换 -------------")
var i int = 100
var f float32 = float32(i)
fmt.Println(f)
// 注意,将大范围值转换为小范围值,可能会造成数据损失
var i1 int64 = 8888
var i2 int8 = int8(i1)
fmt.Println(i2)
// 赋值左右数据类型一致,否则会报错
var n1 int32 = 12
var n2 int64 = int64(n1) + 31
fmt.Println(n2)
基本类型 -> string
string
fmt.Println("--------------基本数据类型转换为string------------")
// 使用fmt.Sprintf(), S代表返回string,printf代表格式化打印
var s = fmt.Sprintf("%d", 100)
fmt.Println(s)
r1 := strconv.FormatInt(678, 10) // 将int数字678转换为10进制字符串
r2 := strconv.FormatInt(678, 16) // 将int数字678转换为16进制字符串
fmt.Println(r1)
fmt.Println(r2)
// 参数1 要进行format的浮点数
// 参数2 格式化格式,参见API文档
// 参数3 保留小数点为3位
// 参数4 代表该浮点数的bit大小,如果是float64则为64
f1 := strconv.FormatFloat(10.02, 'f', 3, 64)
fmt.Println(f1)
r3 := strconv.FormatBool(true)
fmt.Println(r3)
string
->基本类型
string
->基本类型fmt.Println("--------------string转换为基本类型------------")
b, _ := strconv.ParseBool("true")
fmt.Println(b)
i, _ := strconv.ParseInt("A", 16,64) // 将字符串转换, 第二个参数代表该字符串是16进制,第三个值是值数据的类型int64
fmt.Println(i)
f, _ := strconv.ParseFloat("12.00865", 64)
fmt.Println(f)
// 无效类型转换,将会返回对应类型的默认值
b2, _ := strconv.ParseBool("e")
fmt.Println(b2)
派生数据类型
派生数据类型又称为复合数据类型,是由基本类型复合而来。
指针
基本类型也有对应的指针类型
*int
*float32
*bool
...
通过&创建指针变量
var i = 100
// 通过取地址符&定义一个指针变量,指针变量通过 *type表示
var ptr *int = &i
// 指针变量是存放指定变量值得内存地址的引用
fmt.Println(ptr) // 0xc00001a0b8
// 初始化指针变量值的时候,初始值一定是地址
// 下面代码会报错
//var b = 89
//var ptrb *int = b
通过*获取指针变量对应的值
fmt.Println(*ptr) // 100,会根据指针变量的地址0xc00001a0b8,找到这个地址对应的内存空间中的值
改变指针变量指向的值
*ptr = 200
fmt.Println(i) // 200
fmt.Println(&i) // 再次查询地址将会发现地址已经发生变化
// 什么类型的指针,接收的一定是相应类型的值,如果不是会报错
// 下面代码会报错
//var c int32 =16
//var ptrc *float32 = &c
通过new()函数创建指针变量
str := new(string)
*str = "你好"
fmt.Println(*str)
数组
定义数组
// 定义数组变量,不初始化
var balance [10]float32
// 初始化数组,没有赋值的元素将会赋予类型默认值
balance = [10]float32{0, 1, 2, 3, 4, 5}
fmt.Println(balance)
// 定义并初始化数组
var balance2 = [10]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
fmt.Println(balance2)
var balance3 [10]float32 = [10]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
fmt.Println(balance3)
balance4 := [10]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
fmt.Println(balance4)
// 定义数组,不显式指定数组长度
balance5 := [...]float32 {1000.0, 2.0, 3.4, 7.0, 50.0}
fmt.Println(balance5)
下标访问
// 访问数组元素:下标直接访问
fmt.Println(balance5[0])
遍历数组:for
for
// 访问数组元素:遍历数组
for i := 0; i < len(balance5); i++ {
fmt.Printf("%f ", balance5[i])
if i == len(balance5) - 1 {
fmt.Println()
}
}
遍历数组:for range
for range
// for range 遍历
for index, value := range balance5 {
fmt.Printf("[%d]=%f ", index, value)
if index == len(balance5) - 1 {
fmt.Println()
}
}
数组比较
必须保证数组类型长度相同,才能进行数组比较:
a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{1, 3}
fmt.Println(a == b, a == c, b == c) // "true false false"
d := [3]int{1, 2}
fmt.Println(a == d) // 编译错误:无法比较 [2]int == [3]int
多维数组
// 声明一个二维整型数组,两个维度的长度分别是 4 和 2
var array [4][2]int
// 使用数组字面量来声明并初始化一个二维整型数组
array = [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化数组中索引为 1. 入门 和 3 的元素
array = [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化数组中指定的元素
array = [4][2]int{1: {0: 20}, 3: {1: 41}}
切片

切片是对数组连续片段的引用
这个片段可以是整个数组,也可以是指定起始位置的子集
切片内部主要包括三个部分
data,指向切片所在数组的开始元素位置的指针
len,切片的长度
cap,切片的容量(底层数组的长度)
定义切片:从已有数组生成
package main
import "fmt"
func main() {
// 切片建立在一个数组中,需要先定义一个数组
var intarr [6]int = [6]int{3,4,4,1,2,7}
// 根据上面的数组,构建一个切片
// 切片的长度是不固定的,所以不需要写切片长度
var slice0 []int = intarr[1:3] // 这个切面是针对数组intarr的切片,其切取元素位置范围为[1. 入门, 3)
// 输出数组
fmt.Println("数组为:", intarr) // [3 4 4 1. 入门 2 7]
fmt.Println("切片为:", slice0) // [4 4]
fmt.Println("切片的容量为:", cap(slice0)) // 5
}
从开始切到结尾
var slice []int = intarr[:]
从指定位置切到结尾
var slice []int intarr[2:]
从开始切刀指定位置
var slice []int intarr[:5]
重置切片:空切片
var slice []int intarr[0:0]
定义切片:直接定义
切片声明与数组声明很像,只不过切片的声明没有长度;这种方式声明切片与make()
函数效果一致,底层都会创建一个不可访问的数组
var strList []string = []string{} // 声明一个空的切片
定义切片:使用make()函数构造切片
make( []Type, size, cap ) // 创建一个切片,指定类型、长度、初始容量
a := make([]int, 2)
b := make([]int, 2, 10)
fmt.Println(a, b)
fmt.Println(len(a), len(b))
切片容量:cap()
cap()
cap(切片)
切片长度:len()
len()
len(切片)
添加元素:append()
在使用 append() 函数为切片动态添加元素时,如果空间不足以容纳足够多的元素,切片就会进行“扩容”,此时新切片的长度会发生改变
切片在扩容时,容量的扩展规律是按容量的 2 倍数进行扩充,也就是扩容因子为2
package main
import "fmt"
func main() {
var a []int
// 在切片尾部追加元素
a = append(a, 1) // 追加1个元素
fmt.Println(a)
a = append(a, 2, 3, 4) // 追加多个元素, 手写解包方式
fmt.Println(a)
a = append(a, []int{5, 6}...) // 追加一个切片, 切片需要解包,三个点代表对数组/切片解包
fmt.Println(a)
// 在切片头部追加元素
a = append([]int{0}, a...) // 在开头添加1个元素,
fmt.Println(a)
a = append([]int{-3, -2, -1}, a...) // 在开头添加1个切片
fmt.Println(a)
// 在切片指定位置插入元素
// 在切片第四个位置插入数字100
a = append(a[:4], append([]int{100}, a[4:]...)...)
fmt.Println(a)
}
// 切片可以进行链式调用
复制:copy()
复制需要保证切片类型一致
copy( destSlice, srcSlice []T) int
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
删除元素
切片的删除实际上仍然是根据当前切片切割,比如删除头部元素:
a = []int{1, 2, 3}
a = a[1:] // 删除开头1个元素
a = a[N:] // 删除开头N个元素
删除中间位置:
a = []int{1, 2, 3, ...}
a = append(a[:i], a[i+1:]...) // 删除中间1个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
删除尾部元素:
a = []int{1, 2, 3, ...}
a = append(a[:i], a[i+1:]...) // 删除中间1个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
遍历切片:range
// 创建一个整型切片,并赋值
slice := []int{10, 20, 30, 40}
// 迭代每个元素,并显示其值
for _, value := range slice {
fmt.Printf("Value: %d\n", value)
}
多维切片
var sliceName [][]...[]sliceType
//声明一个二维切片
var slice [][]int
//为二维切片赋值
slice = [][]int{{10}, {100, 200}}
映射
map,键值对映射结构,一种元素对(pair)的无序集合,pair 对应一个 key(索引)和一个 value(值),所以这个结构也称为关联数组或字典,这是一种能够快速寻找值的理想结构,给定 key,就可以迅速找到对应的 value
key不可以重复
key的类型不能是:
slice
、map
、function
value的不做限制
定义map
package main
import "fmt"
func main() {
var m1 map[string]int = make(map[string]int, 10) // map的容量不受限制,该参数10代表此map创建时的初始容量
m1["语文"] = 88
m1["数学"] = 98
fmt.Println(m1) // map[数学:98 语文:88]
m2 := map[string]int{
"语文": 88,
"数学": 98,
}
fmt.Println(m2) // map[数学:98 语文:88]
}
遍历map: range
scene := make(map[string]int)
scene["route"] = 66
scene["brazil"] = 4
scene["china"] = 960
for k, v := range scene {
fmt.Println(k, v)
}
添加/更新键值对
scene["china"] = 960
删除键值对: delete()
scene := make(map[string]int)
// 准备map数据
scene["route"] = 66
scene["brazil"] = 4
scene["china"] = 960
delete(scene, "brazil")
for k, v := range scene {
fmt.Println(k, v)
}
清空map
go没有提供清空map的方法,有两种方法可以清空map:
遍历调用
delete()
给变量重新赋予一个新创建的map
结构体
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存。
定义结构体
type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
…
}
type用于自定义类型,定义type可以对结构体进行复用
type Point struct {
X int
Y int
}
同类型变量可将字段放在一行
type Color struct {
R, G, B byte
}
空结构体,没有意义
type Ept struct {}
字段类型不受限制
结构体的字段类型不受限制,可以是基本类型,也可以是复合类型,比如函数等:
// Go program to illustrate the function
// as a field in Go structure
package main
import "fmt"
// Finalsalary of function type
type Finalsalary func(int, int) int
// Creating structure
type Author struct {
name string
language string
Marticles int
Pay int
// Function as a field
salary Finalsalary
}
// Main method
func main() {
// Initializing the fields
// of the structure
result := Author{
name: "Sonia",
language: "Java",
Marticles: 120,
Pay: 500,
salary: func(Ma int, pay int) int {
return Ma * pay
},
}
// Display values
fmt.Println("Author's Name: ", result.name)
fmt.Println("Language: ", result.language)
fmt.Println("Total number of articles published in May: ", result.Marticles)
fmt.Println("Per article pay: ", result.Pay)
fmt.Println("Total salary: ", result.salary(result.Marticles, result.Pay))
}
实例化结构体
基本实例化形式
type Point struct {
X int
Y int
}
var p Point
p.X = 10
p.Y = 20
实例化指针类型结构体(常用)
type Player struct{
Name string
HealthPoint int
MagicPoint int
}
tank := new(Player)
tank.Name = "Canon"
tank.HealthPoint = 300
取地址符的实例化(常用)
对结构体进行&
取地址操作时,视为对该类型进行一次 new 的实例化操作
type Command struct {
Name string // 指令名称
Var *int // 指令绑定的变量
Comment string // 指令的注释
}
var version int = 1
cmd := &Command{}
cmd.Name = "version"
cmd.Var = &version
cmd.Comment = "show version"
初始化结构体
键值类型初始化
type People struct {
name string
child *People
}
relation := &People{
name: "爷爷",
child: &People{
name: "爸爸",
child: &People{
name: "我",
},
},
}
多值列表初始化
字段顺序要保持一致
所有字段必须要初始化
package main
import _ "fmt"
func main() {
type People struct {
name string
child *People
}
relation := &People{
"爷爷",
&People{
"爸爸",
&People{
name: "我",
},
},
}
}
匿名结构体的初始化
package main
import (
"fmt"
)
// 打印消息类型, 传入匿名结构体
func printMsgType(msg *struct {
id int
data string
}) {
// 使用动词%T打印msg的类型
fmt.Printf("%T\n", msg)
}
func main() {
// 实例化一个匿名结构体
msg := &struct { // 定义部分
id int
data string
}{ // 值初始化部分
1024,
"hello",
}
printMsgType(msg)
}
管道
参见:并发编程/channel
函数
接口
参照:面向对象/接口
类型定义
// 定义了一个person类型,
type Person struct {
Name string
Age int
}
无论是类型还是类型别名,都可以为他们指定方法
类型别名
类型别名是Go1.9版本添加的新功能,主要用于解决代码升级、迁移中存在的类型兼容性问题。再重构、升级代码时,经常会遇到变量名称变更,C/C++选择使用宏快速定义一段新的代码,而Golang则使用类型别名:
// Go1.9之前,类型byte、rune通过自定义类型定义
type byte uint8
type rune int32
// Go1.9 之后,类型byte、rune则通过类型别名定义
type byte = uint8
type rune = int32
定义类型别名:
// 类型别名定义的格式:
type TypeAlias = Type
// 根据内置类型创建类型别名
type myInt = int64
// 根据自定义类型创建类型别名
type myDuration = time.Duration
// 根据其他类型别名创建类型别名
type myInt2 = myInt
无论是类型还是类型别名,都可以为他们指定方法
最后更新于
这有帮助吗?