欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

速读Go语言圣经

程序员文章站 2024-02-17 15:35:10
...

The Go Programming Language

中文版教程:https://studygolang.com/book/42?fr=sidebar
前面1-5章都可以在Go语言教程|菜鸟教程,runoob.com/go/go-tutorial.html找到对应的教程。

第一章 入门

本章介绍了Go语言的基础组件。提供了足够的信息和示例程序,可以帮你快速入门。同时作者指出,学习Go语言,请不要按照自己熟悉的语音的套路写新语言程序。

package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}

第二章 程序结构

变量声明

一般语法如下,下面这种一般声明的是全局变量。如果初始化表达式被省略,那么将用零值初始化该变量。多维数组也是一样的。

var 变量名字 类型 = 表达式

简短变量声明

在函数内部,有一种称为简短变量声明语句的形式可用于声明和初始化局部变量。它以”名字:=表达式“形式声明变量,变量的类型根据表达式来自动推导。

t := 0.0

第三章 基础数据类型

Go语言将数据类型分为四类:基础类型、复合类型、引用类型和接口类型。本章介绍基础类型,包括:数字、字符串和布尔型。复合数据类型–数组和结构体–是通过组合简单类型,来表达更加复杂的数据结构。引用类型包括指针、切片、字典、函数、通道,虽然数据种类很多,但它们都是对程序中一个变量或状态的间接引用。这意味着对任一引用类型数据的修改都会影响所有该引用的拷贝。

第四章 复合数据类型

数组和结构体是聚合类型;它们的值由许多元素或成员字段的值组成。数组是由同构的元素组成–每个数组元素都是完全相同的类型–结构体则是由易购的元素组成的。数组和结构体都是有固定内存大小的数据结构。相比之下,slice和map则是动态的数据结构,它们将根据需要动态增长。

  • slice是由数组实现的,map是由hashtable实现的。个人认为,slice很像stl里面的vector,map很像stl里面的hashmap。

第五章 函数

本章的运行示例是一个网络蜘蛛,也就是web搜索引擎中负责抓取网页部分的组件,它们根据抓取网页中的链接继续抓取链接指向的页面。一个网络蜘蛛的例子给我们足够的机会去探索递归函数、匿名函数、错误处理和函数其它很多特性。(to be continue…)

第六章 方法

在本章中,OOP编程的第一方面,我们会想你展示如何有效地定义和使用方法。我们会覆盖到OOP编程的两个关键点,封装和组合。
在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。

package geometry

import "math"

type Point struct{ X, Y float }

// traditional function
func Distance(p, q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

上面的代码里那个附加的参数p,叫做方法的接收器(receiver),早期的面向对象语言留下的遗产将调用一个方法称为“向一个对象发送消息”。

在Go语言中,我们并不会像其它语言那样用this或者self作为接收器;我们可以任意的选择接收器的名字。由于接收器的名字经常会被使用到,所以保持其在方法间传递时的一致性和简短性是不错的主意。这里的建议是可以使用其类型的第一个字母。

在方法调用过程中,接收器参数一般会在方法名之前出现。这和方法声明时一样的,都是接收器参数在方法名之前。下面是例子:

p := Point{1, 2}
q := Point{4, 6}
fmt.Println(Distance(p, q))	//"5", function call
fmt.Println(p.Distance(q))	//"5", method call

可以看到,上面的两个函数调用都是Distance,但是却没有发生冲突。第一个Distance的调用实际上用的是包级别的函数geometry.Distance,而第二个则是使用刚刚声明的Point,调用的是Point类下声明的Point.Distance方法。

通过嵌入结构体来扩展类型

在类型中内嵌的匿名字段也可能是一个命名类型的指针,这种情况下字段和方法会被间接地引入到当前的类型中。添加这一层间接关系让我们可以共享通用的结构并动态地改变对象之间的关系。

一个struct类型也可能会有多个匿名字段。我们将ColoredPoint定义为下米娜这样:

type ColoredPoint struct {
	Point
	color.RGBA
}

然后这种类型的值便会有用Point和RGBA类型的所有方法,以及直接定义在ColoredPoint中的方法。当编译器解析一个选择器到方法时,比如p.ScaleBy,它会首先去找直接定义在这个类型里的ScaleBy方法,然后找呗ColoredPoint的内嵌字段们引入的方法,然后去找Point和RGBA的内嵌字段引入的方法,然后一直递归向下找。如果选择器有二义性的话编译器会报错,比如你在同一级里有两个同名的方法。

第七章 接口

接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具适应能力。

很多面向对象的语言都有相似的接口概念,但Go语言中接口类型的独特之处在于它是满足隐式实现的。也就是说,我们没有必要对于给定的具体类型定义所有满足的接口类型;简单地拥有一些必须的方法就足够了。这种设计可以让你创建一个新的接口类型满足已经存在的具体类型却不会去改变这些类型的定义;当我们使用的类型来自于不收我们控制的包时这种设计格外有用。

类型开关

Go语言实现多态,利用一个接口值可以持有各种具体类型值的能力并且将这个接口认为是这些类型的union(联合)。类型断言用来动态地区别这些类型并且对每一种情况都不一样。在这个方式中,重点在于具体的类型满足这个接口,而不是在于接口的方法(如果它确实有一些的话),并且没有任何的信息隐藏。我们将以这种方式使用的接口描述为discriminated unions(可辨识联合)。

func sqlQuote(x interface{}) string {
	switch x := x.(type) {
		case nil:
			return "NULL"
		case int, uint:
			return fmt.Sprintf("%d", x) 	// x has type interface{} here.
		case bool:
			if x {
				return "TRUE"
			}
			return "FALSE"
		case string:
			return sqlQuoteString(x) 		// (not shown)
		default:
			panic(fmt.Sprintf("unexpected type %T: %v", x, x))
	}
}

尽管sqlQuote接受一个任意类型的参数,但是这个函数只会在它的参数匹配类型开关中一个case时运行到结束;其它情况的它会panic出"unexpected type"消息。虽然x的类型是interface{},但是我们把它认为是一个int、uint、bool、string和nil值的discriminated union(可识别联合)

第八章 Goroutines和Channels

Go语言中的并发程序可以用两种手段来实现。本章讲解goroutine和channel,其支持”顺序通信进程“(communicating sequential processes)。CSP是一种现代的并发编程模型,在这种编程模型中值会在不同的运行实例(goroutine)中传递,尽管大多数情况下仍然是被限制在单一实例中。
尽管Go对并发的支持是众多强力特性之一,但跟踪调试并发程序还是很困难,在线性程序中形成的直觉往往还会使我们误入歧途。如果这是读者第一次接触并发,推荐稍微多花一些时间这两个章节中的样例。
(to be continue…)

第九章 基于共享变量的并发

在本章中,我们会细致地了解并发机制。尤其是在多goroutine之间的共享变量,并发问题的分析手段,以及解决这些问题的而基本模式。最后我们会解释goroutine和操作系统线程之间的技术上的一些区别。
(to be continue…)

第十章 包和工具

Go语言的编译速度主要得益于三个语言特性。

  • 第一点,所有导入的包必须在每个文件的开头显式声明,这样的话编译器就没有必要读取和分析整个源文件来判断包的依赖关系。
  • 第二点,禁止包的环状依赖,因为没有环状依赖,包的依赖关系形成一个有向的无环图,每个包可以被独立编译,而且很可能被并发编译。
  • 第三代,编译后包的目标文件不仅仅记录包本身的导出信息,目标文件同时还记录了包的依赖关系。因此,在编译一个包的时候,编译器只需要读取每个直接导入包的目标文件,而不需要遍历所有依赖的文件。

第十一章 测试

Go语言的测试技术是相对低级的。它依赖一个go test测试命令和一组按照约定方式编写的测 试函数,测试命令可以运行这些测试函数。编写相对轻量级的纯测试代码是有效的,而且它很容易延伸到基准测试和示例文档。
在实践中,编写测试代码和编写程序本身并没有多大区别。我们编写的每一个函数也是针对每个具体的任务。我们必须小心处理边界条件,思考合适的数据结构,推断合适的输入应该 产生什么样的结果输出。编程测试代码和编写普通的Go代码过程是类似的;它并不需要学习 新的符号、规则和工具。

第十二章 反射

Go语音提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内 在操作,但是在编译时并不知道这些变量的具体类型。这种机制被称为反射。反射也可以让 我们将类型本身作为第一类的值类型处理。

有时候我们需要编写一个函数能够处理一类并不满足普通公共接口的类型的值,也可能是因 为它们并没有确定的表示方式,或者是在我们设计该函数的时候还这些类型可能还不存在,各种情况都有可能。
没有一种方法来检查未知类型的表示方式,我们被卡住了。这就是我们为何需要发射的原因。

第十三章 底层编程

主要讲使用unsafe包来摆脱Go语言规则带来的限制,讲述如何创建C语言函数库的绑定,以及如何进行系统调用。
就是和C联合编程。

相关标签: 闲谈