1.2 语言基础概览
本章其余文章按主题拆开讲解语法与库用法。建议在读完 1.1 开发环境搭建指南 后阅读本文,串起 语言特点、类型地图、程序如何启动、运算符与 _、注释习惯;其中别处有专节的只点到为止,并给出链接。
Go 语言的主要特性
1. 起源与背景
21 世纪初,Google 内部在维护大规模 C++ 基础设施时,面临编译慢、依赖复杂、难以发挥多核等问题。Rob Pike、Ken Thompson、Robert Griesemer 等希望设计一门编译快、语法简单、适合网络与多核的语言。
- 2009 年 11 月:以开源项目形式公开发布,官网与文档见 https://go.dev/。
- 2012 年 3 月:正式发布 Go 1,并作出 Go 1 兼容性承诺——在 1.x 系列内,尽量保证源码级兼容,便于长期维护。
语言由 Google 发起,现由 Go 团队与全球社区共同演进,常见别称 Golang(来自早期域名 golang.org)。
2. 设计理念
- 简单可读:控制结构与关键字数量克制,强调显式、直白的代码,减少「炫技」写法;用组合与小接口组织程序,而不是深层类继承。
- 工程优先:内置
go fmt统一风格、go test测试、模块(modules) 管理依赖版本,把团队协作与交付成本考虑进语言生态。 - 并发是语法的一部分:
goroutine(轻量协程)与channel(通道)进入语言核心,配合select,把并发模型说清楚、写清楚。 - 安全与效率的折中:静态类型在编译期发现大量错误;垃圾回收(GC)减轻手工内存管理;在需要与 C 互操作或极致性能时,可使用
cgo、unsafe(均需审慎)。
一句话:让程序员用有限、一致的手段,稳定地写出可维护的服务端与系统软件。
3. 词法基础:关键字、声明与可见性
| 项目 | 说明 |
|---|---|
| 源文件 | 扩展名为 .go;可执行程序入口包名为 main,且需包含 func main()。 |
| 关键字(25 个) | 不能用作自定义标识符;完整列表见下。 |
| 预声明标识符 | 语言还预置了 int、string、true、nil、append、make、len 等名称,含义见 语言规范。 |
| 四种声明 | 顶层常用声明:var(变量)、const(常量)、type(类型别名或新类型)、func(函数或方法)。 |
| 可见性(导出规则) | 标识符(类型、函数、字段、方法等)若首字母大写,则对其它包可见(可导出);首字母小写则仅本包内可见。这是 Go 访问控制的核心约定,无 public/private 关键字。 |
25 个关键字如下(顺序可按需查阅 语言规范):
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var包、文件与 import 的组织方式,在后续章节结合示例说明。
4. 语言机制与工具链
在掌握关键字与可见性之后,下面这些能力贯穿日常开发,本章后文与其它章节会分主题展开。
| 类别 | 要点 |
|---|---|
| 类型与内置值 | 静态类型 + := 等推断;内置 slice、map、chan 等,直接支持容器与并发编程。 |
| 函数与错误 | 多返回值;常用 (结果, error) 显式处理错误;panic/recover 用于少数真正「异常」场景。 |
| 接口与组合 | interface 描述行为,类型隐式实现接口,无需 implements。 |
| 并发 | goroutine、channel、select;标准库另有 sync、context 等。 |
| 反射与互操作 | reflect 运行时检视类型(框架、序列化常用);cgo 调用 C 代码(构建与跨平台成本更高)。 |
| 编译与部署 | 静态编译,常得到单个可执行文件;设置 GOOS / GOARCH 可交叉编译。 |
| 依赖与工具 | Go modules(go.mod / go.sum);go fmt、go test、go vet、go doc 等命令行工具。 |
类型体系一览
Go 的类型可先粗分为 基本数据类型 与 复合数据类型。下面按「本章相关小节」做索引,详细定义与示例请看对应文章。
基本数据类型
语言内置的标量层面类型,赋值/传参时通常拷贝「整份位模式」(string 由运行时优化为共享底层,表现上仍像值)。
| 子类 | 内容提要 | 本章延伸阅读 |
|---|---|---|
| 布尔 | true / false | 1.8 布尔类型 |
| 数值 | 有符号/无符号整型、float32/float64、complex64/complex128 | 1.4 整型与浮点型 |
| 字符与文本 | byte(uint8)、rune(int32)、string(不可变文本) | 1.5 byte、rune 与字符串 |
复合数据类型
由其它类型组合或抽象而来;定长数组、结构体等往往整份拷贝;切片、映射、通道变量里多是头/描述符,拷贝头后底层数据常共享。
| 子类 | 内容提要 | 本章延伸阅读 |
|---|---|---|
| 数组与切片 | [n]T、[]T | 1.6 数组与切片 |
| 映射 | map[K]V | 1.7 字典 |
| 指针 | *T(拷贝的是地址) | 1.9 指针 |
| 通道 | chan T(并发与同步) | 后续并发章节 |
| 函数 | func,一等公民 | 第二章函数相关 |
| 接口 | interface{...},隐式实现 | 第二章接口相关 |
值类型与引用类型(简)
入门时常把类型分成两类:
- 值类型:变量里直接放数据本身,赋值、传参时一般拷贝一整份内容。例如
int、bool、浮点、定长数组[n]T、结构体struct。若结构体很大,可以传*T,只拷指针(一个地址),避免大块复制。 - 引用类型(常见说法):变量里主要放地址,或通过地址找到的数据,一处修改可能让别处也看到变化。例如 指针
*T;切片、映射、通道的变量里带有长度、指针、容量等头信息,赋值、传参虽然拷的是「头」这份内容,但底层数据仍可能被多处共用。
Go 的函数默认按值传参:对小类型传的是整份数据,对 slice、map 等传的是头信息(其中含指针)。make / new 与切片、映射的配合见 2.10 make 和 new 的区别;变量与类型还可对照 1.3 变量声明。
main 函数与 init 函数
可执行程序使用 package main,下面分别说明入口函数 main 与包初始化函数 init。
可执行程序入口
- 生成命令行可执行文件的包,包名一般为
package main,其中必须有一个func main(),作为进程入口。 main函数无参数、无返回值(进程退出码通过os.Exit等方式表达,不宜滥用)。
package main
import "fmt"
func main() {
fmt.Println("hello")
}init 函数(包初始化)
签名只能是 func init()(无参、无返回值),由运行时在调用 main 之前自动执行,不能在业务代码里手动调用 init()。每个包、每个 .go 文件都可以写多个 init。
启动顺序:依赖包先完成(包级变量 → 各 init)→ 本包包级变量 → 本包各 init → 最后 main(仅 package main)。同一包内多个 init 的顺序由文件名字典序、文件内书写顺序等规则决定,业务代码中不要依赖过于精细的顺序假设。
适合:注册、包内默认状态等;避免重逻辑、阻塞、难测的副作用。
package main
import "fmt"
func init() { fmt.Println("init:先于 main") }
func main() { fmt.Println("main:进程入口") }运算符概览
常用运算符分类如下(细规则见语言规范与各节例题)。
| 类别 | 运算符 | 备注 |
|---|---|---|
| 算术 | + - * / % | 整数除法向零取整;% 仅整数 |
| 比较 | == != < <= > >= | 返回布尔值 |
| 逻辑 | 与、或、非 | 可能短路求值 |
| 位运算 | 与、或、异或、清、移位 | &、&^、^、<<、>> |
| 赋值 | = += -= *= /= | 左值须可寻址 |
| 指针与通道 | & * <- | 取址、解引用、通道发送/接收 |
| 其他 | . () [] | 选择、调用、下标/切片 |
Go 没有三元运算符 ?:,也没有自增/自减表达式返回值(i++ 是语句,不是表达式)。
下划线 _
含义与规则
_ 是空白标识符:语法上必须写出标识符或接收值时,用 _ 表示丢弃该值、不引入可引用的新名字。同一作用域内可多次使用 _,不会像普通变量那样因「重复声明」报错。
多返回值、map 与通道
必须接住某一侧但不用时,写在 _ 上。
- map:
v, _ := m[k];若只关心键是否存在,常用_, ok := m[k]。 - 多返回值:例如
n, err := io.Read(...)只关心err时写作_, err := io.Read(...)。 - 通道:
v, _ := <-ch;需要区分零值与通道关闭时,用v, ok := <-ch。
for range:忽略下标或元素
- 只要元素、不要下标:
for _, v := range s { ... }。 - 只要下标、不要元素:惯用
for i := range s { ... },一般不必写for i, _ := range。
空白导入(仅执行 init)
不需要在代码里写包名,但需要该包的 init 副作用(例如向 database/sql、图像解码器等注册驱动)时,使用空白导入:
import _ "image/png" // 执行包内 init,注册 PNG 解码器包被正常导入时也会执行 init;前缀 _ 表示刻意不引用该包导出符号,从而避免「导入了未使用」的编译错误。
编译期接口实现检查
若希望某类型必须实现某接口,可在包级写(惯用法):
var _ io.Writer = (*MyWriter)(nil)若 *MyWriter 未满足 io.Writer,会在编译期失败,便于防止接口与实现脱节。
与 :=、作用域等更细的规则见 1.3 变量声明 中的 匿名变量 小节。
代码注释
行注释与块注释
//:行注释,从//到行尾。最常用。/*…*/:块注释,可跨行;多用于大段说明或临时屏蔽代码(不宜嵌套)。
// 单行说明
/*
多行说明
或暂时注释掉一段代码
*/包注释与导出符号的文档注释
- 紧挨在
package行之前的块注释,且中间无空行,通常作为包说明(go doc会展示)。 - 导出名字(首字母大写)若在其声明前写以该名字开头的句子,会被
go doc当作文档串起来;推荐用完整句子。
// Package mypkg 演示包注释习惯。
package mypkg
// User 表示一个用户;字段导出规则见后续结构体章节。
type User struct {
Name string
}工具链习惯:注释由完整句子构成,导出标识符的注释以它的名字起句,便于生成文档与 IDE 提示。
建议阅读顺序
- 1.1 开发环境搭建指南 — 装好工具链。
- 1.2 本文(语言基础概览) — 建立整体印象(你正在阅读)。
- 1.3 变量声明 —
var、:=、包级与函数内。 - 按上表从 1.4 起补全类型与流程控制各篇;并发、接口、错误处理等在后续章节继续深入。
这样读完本章,能对「Go 是什么风格、类型如何划分、程序如何启动、运算符与注释怎么用」形成整体认识,便于继续阅读本章后续各节。