在 Go 编程语言中,数据类型用于声明函数和变量。Go 是一个静态类型(statically typed)语言,意味着一旦定义变量类型,该变量只能存储该类型的数据。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
数据分类
Go 语言将数据分成如下四大类(categories):
- 基本类型(basic type):其包括数字类型(number)、布尔类型(boolean)和字符串类型(string);
- 聚合类型(aggregate type):该分类下包括数组类型(array)和结构体(struct);
- 引用类型(reference type):指针(pointer)、切片(slice)、字典(map)、函数(function)和通道(channel)都属于该分类;
- 接口类型(interface type):Go 语言中接口(interface)是一个类型,是一个抽象的类型。
基本类型
Go 语言基本类型有数字类型、布尔类型和字符串类型。
数字类型
在 Go 语言中,数字类型又细分为整数类型、浮点类型和复数类型。
有符号和无符号整数都有四种不同的大小,如下表所示。有符号的整数用 int
表示,无符号整数用 uint
表示。
类型 | 描述 |
---|---|
int8 | 8 位有符号整数 |
int16 | 16 位有符号整数 |
int32 | 32 位有符号整数 |
int64 | 64 位有符号整数 |
uint8 | 8 位无符号整数 |
uint16 | 16 位无符号整数 |
uint32 | 32 位无符号整数 |
uint64 | 64 位无符号整数 |
int | int 和 uint 都包含相同的大小,32 位或 64 位 |
uint | int 和 uint 都包含相同的大小,32 位或 64 位 |
rune | 它是 int32 的同义词 |
byte | 它是 uint8 的同义词 |
uintptr | 它是无符号整数类型,它的宽度没有定义,但它可以保存指针值的所有位 |
示例如下:
package main
import "fmt"
func main() {
// Using 8-bit unsigned int
var X uint8 = 225
fmt.Println(X, X-3)
// Using 16-bit signed int
var Y int16 = 32767
fmt.Println(Y+2, Y-2)
}
输出如下:
225 222 -32767 32765
浮点类型分为如下两个类型:
类型 | 描述 |
---|---|
float32 | 32 位 IEEE 754 浮点数类型 |
float64 | 64 位 IEEE 754 浮点数类型 |
示例如下:
// Go program to illustrate
// the use of floating-point
// numbers
package main
import "fmt"
func main() {
a := 20.45
b := 34.89
// Subtraction of two
// floating-point number
c := b - a
// Display the result
fmt.Printf("Result is: %f", c)
// Display the type of c variable
fmt.Printf("\nThe type of c is : %T", c)
}
输出如下:
Result is: 14.440000 The type of c is : float64
复数类型分为两部分,如下表所示。float32 和 float64 也是这些复数的一部分。内置函数从其虚部和实部创建复数,并且内置虚部和实部提取这些部分。
类型 | 描述 |
---|---|
complex64 | 包含 float32 作为实部和虚部的复数 |
complex128 | 包含 float64 作为实部和虚部的复数 |
示例如下:
// Go program to illustrate
// the use of complex numbers
package main
import "fmt"
func main() {
var a complex128 = complex(6, 2)
var b complex64 = complex(9, 2)
fmt.Println(a)
fmt.Println(b)
// Display the type
fmt.Printf("The type of a is %T and the type of b is %T", a, b)
}
输出如下:
(6+2i) (9+2i) The type of a is complex128 and the type of b is complex64
布尔类型
boolean 数据类型只有 true 和 false 两个值。boolean 类型的值不会隐式或显式转换为任何其他类型。
示例如下:
// Go program to illustrate
// the use of booleans
package main
import "fmt"
func main() {
// variables
str1 := "Knowledgedict"
str2 := "knowledgeDict"
str3 := "Knowledgedict"
result1 := str1 == str2
result2 := str1 == str3
// Display the result
fmt.Println(result1)
fmt.Println(result2)
// Display the type of
// result1 and result2
fmt.Printf("The type of result1 is %T and the type of result2 is %T", result1, result2)
}
输出如下:
false true The type of result1 is bool and the type of result2 is bool
字符串类型
string 数据类型用于存储字符序列(文本)。字符串值必须用双引号括起来。
字符串是不可变的字节序列,这意味着一旦创建了字符串,就无法更改该字符串。字符串可以包含任意数据。
代码示例如下:
// Go program to illustrate
// the use of strings
package main
import "fmt"
func main() {
// str variable which stores strings
str := "knowledgedict"
// Display the length of the string
fmt.Printf("Length of the string is:%d", len(str))
// Display the string
fmt.Printf("\nString is: %s", str)
// Display the type of str variable
fmt.Printf("\nType of str is: %T", str)
}
执行结果如下:
Length of the string is:13 String is: knowledgedict Type of str is: string
聚合类型
Go 语言中聚合类型包括数组类型和结构体类型。
数组类型
Go 编程语言中的数组与其他编程语言非常相似。我们需要存储一组相同类型的数据,这种类型的集合使用数组存储在程序中。
数组是一个固定长度的序列,用于在内存中存储同质元素。由于它们的固定长度数组不像 Go 语言中的 Slice 那样流行。在数组中,您可以在其中存储零个或多个零个元素。
初始化语法如下:
array_name := [length]Type{item1, item2, item3,...itemN}
示例如下:
// Go program to illustrate how to create
// an array using shorthand declaration
// and accessing the elements of the
// array using for loop
package main
import "fmt"
func main() {
// Shorthand declaration of array
arr := [4]string{"knowledge", "dict", "188", "knowledgedict"}
// Accessing the elements of
// the array Using for loop
fmt.Println("Elements of the array:")
for i := 0; i < 3; i++ {
fmt.Println(arr[i])
}
}
输出如下:
Elements of the array: knowledge dict 188
结构体类型
Go 语言中的结构体是用户定义的类型,它允许将不同类型的元素封装成一个类型。任何具有一组属性/字段的现实世界实体都可以表示为结构体。这个概念通常与面向对象编程中的类进行比较。它可以称为不支持继承,但支持组合的轻量级类。
例如,Address 具有 name、city 和 Pincode。将这几个属性分组到一个结构体中是有意义的,如下所示:
type Address struct {
name string
city string
Pincode int
}
示例如下:
// Golang program to show how to
// declare and define the struct
package main
import "fmt"
// Defining a struct type
type Address struct {
Name string
city string
Pincode int
}
func main() {
// Declaring a variable of a `struct` type
// All the struct fields are initialized
// with their zero value
var a Address
fmt.Println(a)
// Declaring and initializing a
// struct using a struct literal
a1 := Address{"Akshay", "Dehradun", 3623572}
fmt.Println("Address1: ", a1)
// Naming fields while
// initializing a struct
a2 := Address{Name: "Anikaa", city: "Ballia",
Pincode: 277001}
fmt.Println("Address2: ", a2)
// Uninitialized fields are set to
// their corresponding zero-value
a3 := Address{Name: "Delhi"}
fmt.Println("Address3: ", a3)
}
输出如下:
{ 0} Address1: {Akshay Dehradun 3623572} Address2: {Anikaa Ballia 277001} Address3: {Delhi 0}
引用类型
Go 语言中的引用类型(reference type)指的是指针(pointer)、切片(slice)、字典(map)、函数(function)和通道(channel)类型。
指针类型
Go 编程语言中的指针是一个变量,用于存储另一个变量的内存地址。Go 语言中的指针也称为特殊变量。这些变量用于在系统中的特定内存地址存储一些数据。内存地址总是以十六进制格式(以 0x
开头,如 0xFFAAF
等)。
变量是存储实际数据的内存位置的名称。要访问存储的数据,我们需要该特定内存位置的地址。手动记住所有内存地址(十六进制格式)是一种开销,这就是我们使用变量来存储数据的原因,而变量只需使用它们的名称就可以访问。
Go 语言还允许使用文字表达式将十六进制数保存到变量中,即从 0x
开始的数字是十六进制数。
示例如下:
// Golang program to demonstrate the variables
// storing the hexadecimal values
package main
import "fmt"
func main() {
// storing the hexadecimal
// values in variables
x := 0xFF
y := 0x9C
// Displaying the values
fmt.Printf("Type of variable x is %T\n", x)
fmt.Printf("Value of x in hexadecimal is %X\n", x)
fmt.Printf("Value of x in decimal is %v\n", x)
fmt.Printf("Type of variable y is %T\n", y)
fmt.Printf("Value of y in hexadecimal is %X\n", y)
fmt.Printf("Value of y in decimal is %v\n", y)
}
输出结果如下:
Type of variable x is int Value of x in hexadecimal is FF Value of x in decimal is 255 Type of variable y is int Value of y in hexadecimal is 9C Value of y in decimal is 156
切片类型
在 Go 语言中,切片比数组更强大、更灵活、更方便,是一种轻量级的数据结构。Slice 是一个可变长度的序列,用于存储相似类型的元素,不允许在同一个 slice 中存储不同类型的元素。它就像一个具有索引值和长度的数组,但是切片的大小被调整大小,它们不像数组那样是固定大小的。在内部,切片和数组是相互连接的,切片是对底层数组的引用。允许在切片中存储重复的元素。切片中的第一个索引位置始终为 0,最后一个为长度(切片长度 -1)。
切片就像数组一样声明,但它不包含切片的大小。所以它可以根据需要动态变化。
具体语法如下:
[]T
或者
[]T{}
或者
[]T{value1, value2, value3, ...value n}
其中 T
指的是元素类型。
示例如下:
// Golang program to illustrate
// the working of the slice components
package main
import "fmt"
func main() {
// Creating an array
arr := [7]string{"This", "is", "the", "tutorial",
"of", "Go", "language"}
// Display array
fmt.Println("Array:", arr)
// Creating a slice
myslice := arr[1:6]
// Display slice
fmt.Println("Slice:", myslice)
// Display length of the slice
fmt.Printf("Length of the slice: %d", len(myslice))
// Display the capacity of the slice
fmt.Printf("\nCapacity of the slice: %d", cap(myslice))
}
输出如下:
Array: [This is the tutorial of Go language] Slice: [is the tutorial of Go] Length of the slice: 5 Capacity of the slice: 6
字典类型
在 Go 语言中,字典类型的官方称谓是 map,它是哈希表(Hash Table)的一个实现。
如果一个字典类型的键的类型为 K
,且元素的类型为 T
,那么用于表示这个字典类型的类型字面量:
map[K]T
字典类型声明中的元素类型可以是任意一个有效的 Go 语言数据类型(除了函数类型、字典类型或切片类型)。键的类型必须是可比较的。如果字典类型的键类型是接口类型,那么就要求在程序运行期间,该类型的字典值中的每一个键值的动态类型都必须是可比较的。否则在进行相应操作的时候会引发运行时异常。如下:
map[int]string //合法
map[string]struct{name, department string} //合法
map[string]interface{} //合法
map[[]int]string //不合法
map[map[int]string]string //不合法
初始化示例如下:
package main
import "fmt"
func main() {
mp := map[string]bool{"knowledge": true, "dict": false}
// Display map
fmt.Println("map:", mp)
}
输出如下:
map: map[dict:false knowledge:true]
函数
函数通常是程序中的代码块或语句,使用户能够重用相同的代码,最终节省内存的过度使用,节省时间,更重要的是,提供更好的代码可读性。所以基本上,函数是执行某些特定任务并将结果返回给调用者的语句的集合。一个函数也可以执行一些特定的任务而不返回任何东西。
语法:
func function_name(Parameter-list)(Return_type){
// function body.....
}
函数示例如下:
// Go program to illustrate the
// use of function
package main
import "fmt"
// area() is used to find the
// area of the rectangle
// area() function two parameters,
// i.e, length and width
func area(length, width int) int {
Ar := length * width
return Ar
}
// Main function
func main() {
// Display the area of the rectangle
// with method calling
fmt.Printf("Area of rectangle is : %d", area(12, 10))
}
输出结果:
Area of rectangle is : 120
通道
Go 语言中的通道(channel)是一种特殊的类型。
在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。
通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。
示例如下:
package main
import "fmt"
func main() {
//演示一下管道的使用
//1. 创建一个可以存放3个int类型的管道
var intChan chan int
intChan = make(chan int, 3)
//2. 看看intChan是什么
fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n", intChan, &intChan)
//3. 向管道写入数据
intChan <- 10
num := 211
intChan <- num
intChan <- 50
// //如果从channel取出数据后,可以继续放入
<-intChan
intChan <- 98 //注意点, 当我们给管写入数据时,不能超过其容量
//4. 看看管道的长度和cap(容量)
fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 3, 3
//5. 从管道中读取数据
var num2 int
num2 = <-intChan
fmt.Println("num2=", num2)
fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 2, 3
//6. 在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock
num3 := <-intChan
num4 := <-intChan
//num5 := <-intChan
fmt.Println("num3=", num3, "num4=", num4 /*, "num5=", num5*/)
}
输出结果如下:
intChan 的值=0xc000104000 intChan本身的地址=0xc000012028 channel len= 3 cap=3 num2= 211 channel len= 2 cap=3 num3= 50 num4= 98
接口类型
Go 语言中的接口是一种类型,类似于 Python 中的抽象基类。Go 语言中使用接口来体现多态,是 duck-type 的一种体现。
接口类型描述了方法的集合,实现此接口即这些方法的具体类型是这个接口的实例。
io.Writer 提供了所有可以写入 bytes 的抽象,如文件、内存缓冲区、网络连接、HTTP 客户端、压缩工具、hash 等。
io.Reader 代表了任意可以读取 bytes 的类型。
Closer 是任意可以关闭的值,如文件或网络连接。
语法示例如下:
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
接口名在单词后面一般都需添加 er 的后缀,代表这是一个接口。
当接口名与方法名都是大写时,代表该接口与方法均可被外部包进行访问。
参数列表以及返回值列表参数变量名可以省略。
示例如下:
package main
import "fmt"
type animal interface {
move()
roar()
}
func sapo(a animal) {
a.move()
a.roar()
}
type dog struct {
name string
}
func (d dog) move() {
fmt.Printf("%s在移动\n", d.name)
}
func (d dog) roar() {
fmt.Printf("%s在吼叫\n", d.name)
}
type wolf struct {
name string
}
func (w wolf) move() {
fmt.Printf("%s在移动\n", w.name)
}
func (w wolf) roar() {
fmt.Printf("%s在吼叫\n", w.name)
}
func main() {
d1 := dog{
name: "大黄",
}
w1 := wolf{
name: "灰太狼",
}
sapo(d1) // 大黄调用动物的撒泼方法,由于会动会叫就是动物,所以大黄有两种类型,一种是动物,一种是狗
sapo(w1)
}
输出如下:
大黄在移动 大黄在吼叫 灰太狼在移动 灰太狼在吼叫