引言

04-13Ctrl+D 收藏本站

关灯 直达底部

本书介绍如何使用Go语言的语言特性以及标准库中的常用包来进行地道的Go语言编程。同时,本书也设计成在学会 Go语言后依然有用的参考资料。为了实现这两个目标,这本书覆盖面非常广,尽量保证每一章只涵盖一个主题,各章之间会进行内容上的交叉引用。

从语言的设计精神来说,Go语言与 C 语言非常相似,是一门精小而高效的语言,它有便利的底层设施,如指针。不过Go语言还提供了许多只在高级或者非常高级的语言中才有的特性,如 Unicode 字符串、强大的内置数据结构、鸭子类型、垃圾收集和高层次的并发支持,使用通信而非常规的共享数据和锁方式。另外,Go语言还提供了一个庞大且覆盖面全的标准库。

虽然所有的Go语言特性或者编程范式都会以完整可运行的示例来详细讲解,但是本书还是假设读者有主流编程语言的经验,比如C、C++、Java、Python或其他类似的语言。

要学好任何一门语言,使用它进行编程都是必经之路。为此,本书采用完全面向实战的方式,鼓励读者亲自去练习书中的例子,尝试着去解决练习题中给出的问题,自己去写程序,以获得宝贵的实践经验。正如我以前写的书一样,本书中所引用的代码片段都是“活代码”。也就是说,这些代码自动提取自.go源文件,并直接嵌入到提供给出版商的PDF文件中,故此不会有剪切和粘贴错误,可以直接运行。只要有可能,本书都会提供小而全的程序或者包来作为贴近实际应用场景的例子。本书的例子、练习和解决方案都可以从www.qtrac.eu/gobook.html这个网址获得。

本书的主要目的是传授Go语言本身,虽然我们使用了Go语言标准库中的许多包,但不会试图全都涉及。这并不是问题,因为本书向读者提供了足够的Go语言知识来使用任何标准库中的包或者是任何第三方Go语言的包,当然还能够创建自己的包。

为什么是Go

Go语言始于2007年,当时只是Google内部的一个项目,其最初设计者是Robert Griesemer、Unix泰斗Rob Pike和Ken Thompson。2009年11月10日,Go语言以一个自由的开源许可方式公开亮相。Go语言由其原始设计者加上Russ Cox、Andrew Gerrand、Ian Lance Taylor以及其他许多人在内的一个Google团队开发。Go语言采取一种开放的开发模式,吸引了许多来自世界各地的开发者为这门语言的发展贡献力量。其中有些开发者获得了非常好的声望,因此他们也获得了与Google员工一样的代码提交权限。此外,Go Dashboard这个网站(godashboard.appspot.com/project)也提供了许多第三方的Go语言包。

Go语言是近15年来出现的最令人兴奋的新主流语言。它是第一个直接面向21世纪计算机和开发者的语言。

Go语言被设计为可高效地伸缩以便构建非常大的应用,并可在普通计算机上用几秒钟即完成编译。快如闪电的编译速度可能在一定程度上是因为语言的语法很容易解析,但更主要是因为它的依赖管理。如果文件app.go依赖于文件pkg1.go,而pkg1.go又依赖于pkg2.go,在传统的编译型语言中,app.go需要依赖于pkg1.go和pkg2.go目标文件。但在Go语言中,一切pkg2.go导出的内容都被缓存在pkg1.go的目标文件中,所以pkg1.go的目标文件足够独立构建 app.go。对于只有三个源文件的程序来说,这看不出什么优劣,但对于有着大量依赖关系的大型应用程序来说,这样做可以获得巨大的编译速度提升。

由于Go语言程序的构建是如此之快,因此它也适用一些本来应该使用脚本语言的场景(见“Go语言版Shebang脚本”,参见1.2节)。此外,Go语言可用于构建基于Google App Engine的Web应用程序。

Go语言使用了一种非常干净且易于理解的语法,避免了像老的语言如C++(发布于1983年)或Java(发布于1995年)一样的复杂和冗长。Go语言是一种强静态类型的语言,这在有些程序员看来是构建大型应用程序的必备特性。然而,使用 Go语言进行编程并不需要像使用别的静态语言那样打太多的字,这要归功于 Go语言简短的“声明并初始化”的变量声明语法(由于编译器会推断类型,因此并不需要显式地写明),以及它对鸭子类型强大而便捷的支持。

像C和C++这样的语言,当涉及内存管理时需要程序员非常谨慎地面对,特别是对于并发程序,要跟踪它们的内存分配简直犹如噩梦,而这些本来可以交给计算机去做。近年来,C++在这方面用各种“智能”指针进行了很大的改善,但在线程库方面还一直在追赶Java。通过使用垃圾收集器,Java减轻了程序员管理内存的负担。虽然C++语言现在有一个标准的线程库,但是C语言还只能使用第三方线程库。然而,在C、C++或Java中编写并发程序仍然需要相当地谨慎,以确保在恰当的时间正确地锁定和解锁资源。

Go编译器和运行时系统会处理这些繁琐的跟踪问题。对于内存管理而言,Go语言提供了一个垃圾收集器,因此无需使用智能指针或者手动释放内存。Go语言的并发机制基于计算机科学家C.A.R.Hoare提出的CSP(Communicating Sequential Processes)模型构建,这意味着许多并发的Go语言程序不需要加任何锁[1]。此外,Go语言引入goroutine ——一种非常轻量级的进程,可以一次性大量创建,并可跨处理器和处理器核心自动进行负载平衡,以提供比老的基于线程的语言更细粒度的并发。事实上,因为Go语言的并发支持使用起来如此简单和自然,移植单线程程序到Go时经常会发现转为并发模型的机会大增,从而可以更充分地利用计算机资源。

Go语言是一门务实的语言,与语言的纯净度相比,它更关注语言效率以及为程序员带来的便捷性。例如,Go语言的内置类型和用户自定义的类型是不一样的,因为前者可以高度优化,后者却不能。Go语言也提供了两个基本的内置集合类型:切片(slice,它的实际用途是为了提供变长功能的数组)和映射(map,也叫键值字典或散列表)。这些集合类型非常高效,并且在大多数情况下都能非常好地满足需求。当然,Go语言也支持指针(它是一个完全编译型的语言,因此在性能方面没有虚拟机挡路),所以它可以轻松创建复杂的自定义类型,如平衡二叉树。

虽然C语言仅支持过程式编程,而Java则强制要求程序员按照面向对象的方式来编程,但Go语言允许程序员使用最合适的编程范式来解决问题。Go语言可以被用做一个纯粹的过程式编程语言,但对面向对象编程也支持得很好。不过,我们也会在后文看到,Go语言面向对象编程的方式与C++、Java或Python非常不同,它更容易使用且在形式上更加灵活。

就像C语言一样,Go语言也不支持泛型(用C++的话来说就是模板)。然而,Go语言所提供的别的功能特性消除了对泛型支持的依赖。Go并不使用预处理器或者包含文件(这也是为什么它编译得如此之快的另一个原因),因此也无需像 C和C++那样复制函数签名。同时,因为没有使用预处理器,程序的语义就无法在Go语言程序员背后悄悄变化,但这种情况在C和C++下使用#define时一不小心就会发生。

可以说,C++、Objective-C和Java都试图成为更好的C语言(后者是间接地成为了更好的C++语言)。尽管Go语言干净而轻盈的语法容易让人联想到Python,Go语言的切片和映射也非常类似于Python的列表和字典,但Go语言也可以被认为试图成为一个更好的C。然而,与任何其他语言相比,Go语言从语言本质上都更接近于C语言,并可以被认为保留了C语言的所有精华的同时试图消除C语言中的缺陷,同时加入了许多强大而有用的独有特性。

Go语言最初被构思为一门可充分利用分布式系统以及多核联网计算机优势且适用于开发大型项目的编译速度很快的系统级语言。现在,Go语言的触角已经远远超出了原定的范畴,它正被用做一个具有高度生产力的通用编程语言。使用 Go语言开发和维护系统都让人感觉是一种享受。

本书的结构

第1章开始讲解如何建立和运行Go程序。这一章通过5个简短的示例简要介绍了Go语言的语法与特性,以及一些标准库。每个例子都介绍了一些不同的特性。这一章主要是为了让读者尝试一下Go语言,以此让读者感受一下学习Go语言需要学习的大致内容是什么。(这一章章还讲解了如何获取和安装Go语言环境。)

第2章至第7章更深入地讲解了Go语言的方方面面。其中有三章专门讲解了Go语言的内置数据类型:第2章涵盖了标识符、布尔值和数值类型,第3章涵盖了字符串,第4章涵盖了Go语言内置的集合类型。

第5章描述并讲解了Go语言的语句和控制结构,还解释了如何创建和使用自定义的函数,最后展示了如何使用Go语言创建一个过程式的非并发程序。

第6章展示了如何在Go语言中进行面向对象编程。本章的内容包括可用于聚合和嵌入(委托)其他类型的结构体,可作为一个抽象类型的接口,以及如何在某些情况下产生类似继承的效果。由于 Go语言中进行面向对象编程的方式可能与大多数读者的经验不同,这一章会给出几个完整的例子并详细讲解,以确保读者完全理解Go语言的面向对象编程方式。

第7掌讲解了Go语言的并发特性,与面向对象编程一章相比,这一章给出了更多实例,以确保读者对这些新的Go语言特性有透彻的了解。

第8章展示了如何读取和写入自定义的二进制文件、Go二进制(gob)文件、文本、JSON以及XML文件。(读取和写入文本文件的知识在第1章和后续几章中都有所涉及,因为这些知识可以更易于提供一些有价值的示例和练习。)

本书的最后一章是第9章。这一章先展示了如何导入和使用标准库包、自定义包以及第三方软件包。它还展示如何对自定义的包进行文档的自动提取、单元测试和性能基准测试。这一章的最后一节对Go编译器(gc)提供的工具集以及Go语言的标准库做了简要的概述。

Go语言虽然小巧,但它同时也是一门功能丰富和强大表达能力(在语法结构、概念和编程习惯方面)的语言。本书的例子都符合良好的Go语言编程范式[2]。当然,这种做法也意味着有些概念出现时不会被当场解释。但我们希望读者相信,所有的概念都会在本书中进行解释(当然,没有当场解释的内容都会以交叉引用的形式给出相应讲解的位置)。

Go是一门迷人的语言,使用起来感觉非常好。学习Go语法和编程习惯并不会很难,但它的确引入了一些新颖的、对许多读者来说可能不那么熟悉的概念。这本书试图给读者概念上的突破,尤其是在面向对象的Go语言编程和并发Go语言编程方面。如果只阅读那些定义良好却非常简要的文档,读者可能需要花费数周甚至数月的时间才能真正理解相关的知识。


[1].指不需要用户主动加锁,而不是指从内部实现来说没有锁。——译者注

[2].这里有一个例外:前面几章中,即使通道只被当做单向通道使用,我们也总是将通道声明为双向的。从第7章开始,通道只被声明为只有某一特殊的方向,这样,这里所说的Go语言风格用法也就讲得通了。