前言: 由于去年的项目,合作方的服务使用的是Golang开发的,合作期间也进行过简单的探讨。加之时下Go、Rust这些语言大热🔥,最终决定把这门语言系统的学一下。开始以语言最基本特性和语法入手,毕竟都有过其他开发语言的学习经历,也基于Go语言自身的简洁性,学习和上手还都是比较顺畅的。学习的时候喜欢边看边敲做些笔记,前段时间又稍微整理完善了下就水成一份博客发一下吧。

  • 📖参考的主要课本为《Go语言趣学指南》(网上目前只找到英文版)、《Go语言实战》与《Go Web编程》,也包含一些网上的博文与教程之类的,;
  • 📓每个lesson也就包含一份示例源码和一份笔记,简单且系统地记录下相关知识点和一些自己的理解;
  • 🐶因为学习Golang之前,大家很多都是有了其他语言的基础,所以过于基本的语法就无需细究了;
  • 源码地址:https://github.com/Xuhy0826/Go-Go-Study
数值类型

实数

浮点类型

go语言中有两种浮点类型:

  1. float64(占8字节内存),默认的浮点类型。意思是说不显式地指定float32,都是使用float64来定义一个带小数点的变量
  2. float32(占4字节内存)
days := 365.2425    //短声明,days会被Go编译器推断为浮点类型默认的float64类型

如果需要使用float32,我们必须指定类型

var pi64 = math.Pi      
var pi32 float32 = math.Pi  //这样声明的变量才会是float32

零值

零值就是某个类型的默认值。在Go中,当创建一个变量但没有为其赋值时,Go会自动为其赋值对应类型的零值。浮点类型的零值很容易想到

var price float

等同于

price := 0.0

格式化输出浮点值

fmt.Printf()
fmt.Printf("%f\n", third)     //0.333333
fmt.Printf("%.2f\n", third)   //0.33,.2f就是表示小数点后保留2位
fmt.Printf("%4.2f\n", third)  //0.33,4.2f表示总宽(长)度为4,小数点后保留2位
fmt.Printf("%5.2f\n", third)  //0.33,5.2f表示总宽(长)度为5,小数点后保留2位,长度不够使用空格来补
fmt.Printf("%05.2f\n", third) //00.33,05.2f表示总宽(长)度为5,小数点后保留2位,长度不够使用“0”来补

浮点类型的精确性

由于计算机只能通过0和1来表示浮点数,所以浮点数会经常受到舍入错误的影响 比如:

piggyBank := 0.1
piggyBank += 0.2
fmt.Println(piggyBank) //0.30000000000000004

由上面提到的浮点类型的精确度问题,就会导致浮点数的比较出现意外。

fmt.Println(piggyBank == 0.3) //false

一个折中的解决方案就是使用一定精确度来判断是否相等

fmt.Println(math.Abs(piggyBank-0.3) < 0.0001) //true

那么说到底,避免浮点数精确度问题的最佳方案就是:不使用浮点数🐶

整数

Go中提供了10种整数类型,根据不同的大小和是否有符号分为

  • int8 1字节(8位)
  • uint8 1字节
  • int16 1字节
  • uint16 1字节
  • int32 1字节
  • uint32 1字节
  • int64 1字节
  • uint64 1字节
  • 还有两个是int和uint,Go在进行类型推断是会默认推断成int类型。在Go中,int类型会根据目标硬件(电脑是32位还是64位)选择合适的位长(32位机器上int就是32位值,64位机器就是64位长),所以如果想在32位的机器上操作特别大的数,要定义成int64而不是int。但是也不要认为int类型和int32或int64是一种类型,他们是3种类型。

选择合适的类型

适应不同的常见选择不同的类型来 【例】:使用uint8来表示颜色rgb值,是个很好的选择:

  • (1)能将变量限制在合法的范围之内
  • (2)对于未压缩的图片这种需要按顺序存储大量颜色的场景,可以极大的节省空间
var red, green, blue uint8 = 0, 141, 213
fmt.Printf("color:#%02x%02x%02x;", red, green, blue) //color:#008dd5;

回绕(wrap around)

整数类型不会有浮点类型的精度问题,但是存在自己的回绕问题。就是整数类型在达到自己的边界值(最大值或最小值),再向边界外延伸时,会回到最小值或最大值。例如一个uint8,它的最大值应该是255,此时再进行加1操作后,值就变成了0。其实只要知道整型在计算机中的二进制存储方式,这个事情还是很好理解的。

var numberA uint8 = 255	//到达类型最大值
numberA++
fmt.Println(numberA)	//0	环绕

大数

顾名思义,就是特别大的数,一般情况下,比较大的数我们也可使用简便写法 表示方法(类似科学计数法):

var distance int64 = 41.3e12    //就是41.3 * 10<sup>12</sup>

但是如果需要使用超过uint64上限的数时,Go为我们提供了big包来解决问题,引用时包名为:"math/big"。

big包

  • 存储大整数:big.Int
  • 存储任意精度的浮点数:big.Float
  • 存储如1/3的分数:big.Rat

big.Int的创建方式有两种

  1. 使用big.NewInt(int val)的方法
lightSpeed := big.NewInt(299792)
distance := new(big.Int)
distance.SetString("24000000000000000000000", 10)

大数类型可以精确地承载很大的数值,但是代价就是空间和性能的损耗。

大数在常量中的表现

和变量不同,当我们不为常量指定类型,并直接为其赋值一个很大的数,Go会直接将其标记为无类型(untyped)而不会引发溢出异常,并且可以在程序中正常使用

const distance = 240000000000000000000000
fmt.Println("Andromeda Galaxy is ", distance/299792/86400)  //output: Andromeda Galaxy is  9265683466462
字符串类型

string类型

关于字符串类型(string),和其他语言一样没什么区别,使用双引号包起来,如

peace := "peace"
var peace = "peace"
var peace string = "peace"
\n
fmt.Println(`
peace be upon you
upon you be peace`)

“字符串字面量”和“原始字符串字面量”都是string类型。

字符、代码点、符文和字节

  • 都知道计算机中字符是通过编码存取的,也就是每个字符都使用一个特定的数字表示。比如A就是65,那么书中将这个65称为字符A的代码点
  • rune类型(符文类型):Go中使用rune类型来表示字符的代码点,该类型本质上是int32类型的别名,也就是说rune和int32可以相互转换
  • byte类型:Go中的byte类型不仅可以表示二进制数据,而且被拿来表示ASCII码(ASCII共包含128个字符)。本质上byte类型是uint8类型的别名
var pi rune = 960
var alpha rune = 940
var omega rune = 969
var bang byte = 33

fmt.Printf("%v %v %v %v\n", pi, alpha, omega, bang) //960 940 969 33
//通过使用格式化变量%c,可以将代码点表示成字符
fmt.Printf("%c %c %c %c\n", pi, alpha, omega, bang) //π ά ω !

在Go中使用单引号来表示字符字面量,如果用户声明一个字符变量而没有为其制定类型,那么Go会将其推断成rune类型。下面三种写法是一样的功能。

grade := 'A'
var grade = 'A'
var grade rune = 'A'

字符串无法修改

字符串虽然是有字符“串”起来的,但是和C#、java等语言一样,Go中的字符串类型也是不可修改的。

//通过索引的方式访问字符串中的字符
message := "shalom"
c := message[5]
fmt.Printf("%c\n", c)

//字符串不可被修改
//message[5] = 'd'  //报错:cannot assign to message[5]

字符串与符文

Golang中字符串使用utf-8编码。utf-8是一种变长的编码方式,也就是说每个字符的长度可能占用不同的字节长度。比如中文字符就是需要占据两个字节长度,而英文字符或者数字则只需要占据1个字节长度。 为了方便,Go为此提供了utf包,里面提供了两个实用的方法

  • RuneCountInString函数,能够按照特定的字符返回字符串中字符的个数,而不是像len方法一样返回字节的长度
  • DecodeRuneInString函数,解码字符串的首个字符并返回解码后的字符占用的字节长度
question := "今天星期几?"
fmt.Println(len(question), "bytes")                    //18 bytes
fmt.Println(utf8.RuneCountInString(question), "runes") //6 runes

c, size := utf8.DecodeRuneInString(question)
fmt.Printf("First rune: %c %v bytes", c, size) //First rune: 今 3 bytes

//遍历字符串,挨个打印出来
for i, c := range question {
    fmt.Printf("%v %c\n", i, c)
}
rangeic

Go中与其他强类型语言(比如C#)类似,类型之间进行操作时,需要经过类型转换否则会报“类型不匹配”的错误。

数字类型转换

  • 整数类型 → 浮点类型:
age := 41
marsAge := float64(age)
  • 浮点类型 → 整数类型:

【注意】:浮点型的小数部分是被截断,而不是四舍五入

earthDays := 365.2425
fmt.Println(int(earthDays)) //output: 365

在数值类型进行转换时,一样要注意超出范围的问题,比如一个较大float64转成int16时。

字符串转换

  • rune/byte → string
var pi rune = 960
var alpha rune = 940
fmt.Println(string(pi), string(alpha)) //output: π ά
  • 数字类型 → string

情况特殊一点,为了将一串数组转换为string类型,必须将其中的每个数字都转换为相应的代码点(char)。也就是代表字符0的48~代表字符9的57。我们需要使用到strconv(代表“string conversion”)包提供的Itoa函数来完成这一工作。

countdown := 10
str := "Launch in T minus " + strconv.Itoa(countdown) + " seconds".

另一种方法,使用fmt.Sprintf函数,该函数会返回格式化后的string而不是打印

countdown := 9
str := fmt.Sprintf("Launch in T minus %v seconds", countdown)
fmt.Println(str) //Launch in T minus 9 seconds
  • string → 数字 一种不太常用的转换,也是使用strconv包的Atoi函数
count, err := strconv.Atoi("10")
if err != nil {
    //出错
}
fmt.Println(count) //10

上面这种写法是之后经常看到的,是Go处理异常的一种常用写法。由于Go的函数可以返回多个值,一般会将可能产生的异常一并返回。

布尔类型转换

fmtPrint
launch := false
launchText := fmt.Sprintf("%v", launch)
fmt.Println("Ready for launch:", launchText) //Ready for launch: false

某些语言中会把1和0当做true和false,Go中是不行的。


这里给大家推荐一个在线软件复杂项交易平台:米鼠网 https://www.misuland.com

米鼠网自成立以来一直专注于从事软件项目、人才招聘、软件商城等,始终秉承“专业的服务,易用的产品”的经营理念,以“提供高品质的服务、满足客户的需求、携手共创双赢”为企业目标,为中国境内企业提供国际化、专业化、个性化、的软件项目解决方案,我司拥有一流的项目经理团队,具备过硬的软件项目设计和实施能力,为全国不同行业客户提供优质的产品和服务,得到了客户的广泛赞誉。