编写程序的目的之一就是处理数据,本章主要介绍 Go 的基础数据类型。

2.1 什么是数据类型

数据类型定义了编程语言、编译器、数据库以及代码在执行时如何操作和处理数据的。例如,数据类型是数字,则可以执行数学运算。编程语言和数据库会根据数据类型向开发者提供功能。大多数编程语言提供了处理通用数据的标准库。数据库提供有查询语言,以便开发者与基础数据类型进行交互、查询数据。数据类型不管是否被明确声明,它们都是编程和计算的重要构成。

2.2 静态和动态语言

编程语言可分为静态类型(Static Typing)和动态类型(Dynamic Typing)。如果在数据类型使用不正确时,编译器会抛出错误,则这类语言为静态类型(也称为强类型)。如果运行时将语言从一种类型转换为另一种类型以执行程序,或者编译器未强制执行类型系统,则为动态类型(也称弱类型)。就像不能争论出科比更强还是詹姆斯更厉害一样,我们也无法得出哪种类型的语言更好。有些人重视静态类型语言的正确性和安全性,另一些人则喜欢动态类型语言简化了开发速度。

静态类型语言的优点:

  • 性能比动态类型语言更好

  • 错误通常可由编译器捕获

  • 更好的数据完整性

  • 编辑器可以提供代码完成及其他工具

动态类型语言的优点:

  • 开发速度通常更快

  • 无需等待编译器编译代码

  • 学习曲线短

  • 风格灵活

Go 属于静态语言,具有数据类型的概念,数据类型要么由开发人员明确声明,要么由编译器推断:

package main


import (

 "fmt"

)


func sayHello(s string) string {

 return "Hello " + s

}


func main() {

 fmt.Println(sayHello("Curry"))

}

不用担心看不懂上面这段代码。重要的是 sayHello 这个函数,声明了一个参数 s,s 的数据类型是 string,函数返回值也是 string。因此,当编译器编译程序时,它会检查传递给函数的参数是否为 string 类型。如果不是 string 类型,编译器将报错,进而避免将错误展示给用户。

为了更直观的比较动态语言和静态语言,我们看一段 JavaScript 的代码,JavaScript 是比较流行的动态类型语。如果你不了解它,也没有关系,重点是观察它处理不同数据类型的方式:

var addition = function (a, b) {
   return a + b;
}

给 addition 函数传递两个数字,我们能得到一个正确的结果:

addition(1, 3)

4

如果传递一个数字,一个字符串,则会返回一个奇怪的结果:

addition(1,"three")

1three

执行 addition 后,返回了一个字符串。虽然 JavaScript 有数据类型的概念,但在使用方式上却不做限制。在这个示例中,JavaScript 将数字类型强制转换成了字符串,返回字符串“1three”。看上去用法很灵活,却极有可能导致错误。

假设程序中使用了 addition 函数对输入进行计算,并将结果存储在数据库中。数据库也有数据类型的概念,如果在数据库中存储该执行结果的字段类型为整数,那么我们必须传递整数给数据库。而 JavaScript 的 addition 函数可以返回字符串或整数。如果用户输入的是字符串,那么一个字符串类型的数据将被插入数据库字段中,与数据库期望的数据类型不同,进而引发错误。更糟糕的是,此错误是运行时错误,意味着错误将直接影响用户。除非在程序中对错误有相应的处理,否则很可能会导致程序崩溃。

我们来看 Go 的示例,在 Go 中创建相同功能的函数,并声明了参数及返回值的数据类型。仅通过代码就能确定,addition 函数接收两个 int 类型的参数,并返回一个 int 类型的值。如果开发者错误的将 string 类型传递给了 addition 函数,编译器将捕获错误。

package main

import (
   "fmt"
)

func addition(x int, y int) int {
   return x + y
}

func main() {
   fmt.Println(addition(1, 3))
}

我们试着传递一个非 int 类型的数据,就会引发编译错误,帮助我们在程序提供给用户前发现错误:

package main

import (
   "fmt"
)

func addition(x int, y int) int {
   return x + y
}

func main() {
   var s string = "three"
   fmt.Println(addition(1, s))
}

执行这段代码:

$ go run addition.go
cannot use s (type string) as type int in argument to addition

Go 编译器还可以捕获其他错误,例如传递的参数个数不匹配。

2.3 布尔类型

在上面的示例中,我们接触了 Go 的数据类型。接下来,开始探索 Go 都有哪些基础数据类型的。首先是布尔类型,Go 中的布尔类型的值是 true 或 false。

我们声明一个变量名为 b 的布尔类型:

var b bool

在声明变量的时候,如果未给变量赋值,Go 默认会为变量分配一个该类型的零值,布尔类型的零值为 false,我们可以执行 example1.go 看下结果:

package main

import (
   "fmt"
)

func main() {
   var b bool
   fmt.Println(b)
}
$ go run example1.go
false

2.4 数字类型

数字是编程的基础,但如果你没有计算机或数学背景,可能会混淆某些术语。你可能听说过整数、有符号整数、无符号整数、浮点数、32位、64位、byte、rune。这些都是数字类型。要了解这些术语,首先要明白数字是以位的形式存储在计算机中。位是一系列0或1的布尔值,一个位可以是0或1。4个原始位与十进制的对照如表2-1,可以看到4个原始位总共对应16个二进制数字。

二元 小数
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 10
1011 11
1100 12
1101 13
1110 14
1111 15

表2-1

2.4.1 有符号和无符号整数

对于有符号整数,可以将其中的一位用作表示其值的符号——负号。表2-1表示无符号整数,其值范围是0到15。有符号整数的值可以是负数或正数,其值范围是-8到7。表2-2是4个有符号原始位:

二元 小数
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 -8
1001 -7
1010 -6
1011 -5
1100 -4
1101 -3
1110 -2
1111 -1

表2-2

声明整数类型:

var i int = 1

Int 类型是带符号的整数,因此支持正数和负数。根据计算机的基础体系结构,int 可以是带符号的 32 位整数或带符号的 64 位整数。

2.4.2 浮点数

浮点数就是包含小数点的数字,如11.1、12.22、3.1415926。int只支持整数类型,因此要使用小数,必须使用浮点数类型。Go 中的浮点数也可以是 32 位或 64 位。对于大多数现代 CPU,建议使用 float64。

声明浮点数类型:

var f float64 = 0.111

Go 的数字类型中还包含复数类型,但比较少用,这里就不再介绍了。

2.5 字符串类型

String 类型可以是任何字符序列:数字、字符、字母。String 的简单示例:cow、$^#%、a13453。

声明字符串类型:

var s string = "魏文弟"

2.6 检查变量的数据类型

有时,编译器会捕获到类型错误,处于调试或验证目的,我们需要检查变量的数据类型,根据检查结果再做下一步操作。

使用 fmt 包的 Printf 函数做变量数据类型检查:

package main

import (
   "fmt"
)

func main() {
   var s string = "魏文弟"
   var i int = 1
   var f float64 = 11.1

   fmt.Printf("%T\n", s)
   fmt.Printf("%T\n", i)
   fmt.Printf("%T\n", f)

}

也可以使用 reflect 包进行类型检查:

package main

import (
   "fmt"
   "reflect"
)

func main() {
   var s string = "魏文弟"
   var i int = 1
   var f float64 = 11.1

   fmt.Println(reflect.TypeOf(s))
   fmt.Println(reflect.TypeOf(i))
   fmt.Println(reflect.TypeOf(f))
}

2.7 类型转换

常见的编译任务是将一种数据类型转换为另一种。通常,这些数据是从网络、文件或数据库中读取的。Go 的 strconv 标准库对类型转换提供了良好的支持。

假设变量s作为字符串类型,其值为”true”,我们需要它和一个布尔值进行比较,这就需要我们先把s转换成布尔类型,因为 Go 中不同类型是不能进行比较的。

var s string = "true"
b, err := strconv.ParseBool(s)

变量 b 是 bool 类型,同样,也可以将布尔类型转换为字符串类型:

strconv.FormatBool(b)

完整转换示例:

package main

import (
   "fmt"
   "strconv"
)

func main() {

   var s string = "true"

   b, err := strconv.ParseBool(s)

   fmt.Printf("%T, %#v\n", b, err)

}

数据类型错误,是编译过程中常见的错误,使用数据源时,值得花一些时间来确认数据类型是正确的。如果数据源是数据库,请先确认数据库的字段类型,这可以节省大量的调试时间!

2.8 总结

这一章主要介绍了 Go 的基础数据类型,了解了静态和动态编程语言间的区别及其优缺点。介绍了如何检查和转换变量的数据类型,Go 不支持隐式的数据类型转换。如果你没有编程基础,可能一些知识超出了你的认知范围,没有关系,请继续往下看。

文档更新时间: 2021-11-08 09:26   作者:admin