高效掌握Go语言要点梳理
大家好,欢迎阅读本次干货分享。此博客是我对自身初步学习Go语言的总结,期望能助力大家快速掌握Go语言的基本编程能力,若有错误还请留言指正,会持续更新哟!
一、初步认识Go语言
(一)Go语言诞生的核心问题与目标
-
多核硬件架构挑战:随着计算机硬件发展,多核处理器成主流,并行计算普遍。但传统编程语言处理多核并行时力不从心,缺乏原生支持。而Go语言引入轻量级协程(goroutine)与通道(channel)机制,让并发编程更简便。开发者能轻松创建数千个并发执行的协程,无需操心线程管理的复杂事儿。
-
超大规模分布式计算集群需求:云计算与分布式系统兴起,超大规模分布式计算集群构建与维护渐成常态。这类集群需高效处理大量请求、数据共享与协调。Go语言的并发特性及通道机制,让编写分布式系统更易,开发者可用协程与通道处理并发任务、消息传递和协调工作。
-
Web模式带来的开发困境:Web应用兴起带来巨大开发规模与持续更新需求。传统编程语言开发大型Web应用时,可能面临可维护性、性能和开发效率等问题。Go语言凭借简洁语法、高效编译速度及并发支持,让开发者能更快速迭代和部署Web应用,还能更好应对高并发网络请求。
(二)Go语言应用典型范例
当下,Go语言在应用开发中应用广泛,众多知名公司与项目用它构建各类应用。以下是部分代表性产品与项目,它们以Go语言为核心开发语言:
这只是Go语言应用的一小部分实例,实际还有很多项目与产品用Go语言构建高性能、可靠且易维护的应用程序。这表明Go语言在现代应用开发中,尤其在分布式系统、云计算和高性能应用领域发挥着重要作用。
(三)Java、C++、C程序员学Go时的常见误区
当Java、C++、C等编程语言的程序员开始学Go时,可能会陷入一些误区,毕竟Go在某些方面与这些传统语言有别。常见误区如下:
-
过度依赖传统并发模型:传统编程语言如Java、C++、C处理并发常用线程和锁,而在Go中,用协程(goroutine)和通道(channel)才是更好的方式。新学Go的程序员可能还在用传统并发模型,没充分利用Go的轻量级协程与通道,错失Go的并发优势。
-
过度使用指针:C和C++强调指针使用,但Go语言设计时尽量少用指针操作。新学Go的程序员可能过度用指针,让代码变复杂。在Go中,除非真要修改值,否则应尽量避免用指针。
-
忽视错误处理:Go鼓励显式处理错误,而非简单忽略。这与一些其他语言习惯不同,那些语言常忽略或简单抛出错误。新学Go的程序员可能忽视错误处理,导致潜在问题未被察觉。
-
过度使用全局变量:在C和C++中,全局变量常见,但在Go中,全局变量使用被视为不良实践。Go鼓励用局部变量和传参方式传递数据,避免引入不必要的耦合与副作用。
-
不熟悉切片与映射:Go中的切片与映射是强大的数据结构,但其他语言程序员可能不熟悉。学会正确使用切片与映射很重要,因它们在Go中广泛用于集合和数据处理。
-
错误的Go风格:每种语言有独特编码风格与惯例。新学Go的程序员可能在Go代码中套用其他语言编码风格,让代码难读难理解。
为避免这些误区,学Go的程序员要花时间理解Go语言核心概念,包括并发模型、错误处理、数据结构等,还要积极参与Go社区,阅读Go官方文档与示例代码,更好适应Go的设计理念与最佳实践。
二、环境搭建(以Mac为例)
(一)环境配置
在macOS系统上搭建Go语言开发环境很简便,可按以下步骤操作:
-
通过Homebrew安装:若使用Homebrew包管理器,这是最便捷的方法。打开终端,执行以下命令安装Go语言:
brew install go -
手动安装:若想手动安装Go语言,可按如下步骤:
a. 访问官方网站[下载适配macOS的安装包goX.X.X.darwin-goX.X.X.darwin-
amd64.pkg](https://golang.org/dl/,通常下载的文件名为
amd64.pkg,其中X.X.X`代表Go的版本号)。
b. 双击下载的安装包,依照提示运行安装程序。采用默认设置即可,安装路径一般为/usr/local/go;
- 配置环境变量:安装完成后,需将Go语言的二进制文件路径添加到终端配置文件的PATH环境变量中,如此便能在终端直接运行Go命令。
a. 打开终端,运用文本编辑器(例如nano、vim等您偏好的编辑器)编辑终端配置文件。示例如下:
nano ~/.bash_profile
b. 在文件中添加下述行(依据实际安装路径调整),接着保存并退出编辑器:
export PATH=$PATH:/usr/local/go/bin
c. 使配置生效,可执行以下命令或重启终端:
source ~/.bash_profile
- 验证安装:打开终端,输入以下命令验证Go是否成功安装:
go version
若能看到Go的版本号,表明安装成功。
(二)IDE选择说明
我个人使用GoLand,直接从官网下载后,购买破解版就行,这里不多赘述!
三、Go语言程序基础学习
创建属于自己的工程目录/Users/zyf/zyfcodes/go/go-learning,并新建src目录。
(一)首个Go程序编写
在src目录下创建chapter1/hello目录,新建hello.go文件,代码如下:
package main
import (
"fmt"
"os"
)
/**
* @author zhangyanfeng
* @description 第一个Go程序
* @date 2023/8/20 23:45
* @param
* @return
**/
func main() {
if len(os.Args) > 1 {
fmt.Println("Hello World", os.Args[1])
}
}
此代码是简单的Go程序,它接收命令行参数并打印带参数的”Hello World”消息。以下是代码逐行分析:
package main:声明该文件属于名为”main”的包,这是Go程序的入口包名。import ("fmt" "os"):引入两个标准库包,分别是”fmt”用于格式化输出,和”os”用于与操作系统交互。func main() { ... }:这是程序的入口函数,程序运行时首先调用它。if len(os.Args) > 1 { ... }:该条件语句检查命令行参数数量是否大于1,判断是否有参数传递给程序。os.Args是字符串切片,包含所有命令行参数,第一个参数是程序名称。fmt.Println("Hello World", os.Args[1]):若有参数传递给程序,执行该行代码。它用fmt.Println函数打印消息,消息由”Hello World”和os.Args[1]组成,os.Args[1]表示传递给程序的第一个参数。
综上,该代码涵盖以下知识点:
– 包导入与标准库使用:通过import关键字导入”fmt”和”os”包,然后在代码中使用这些包提供的函数和类型。
– 命令行参数获取:使用os.Args获取命令行参数。
– 条件语句:使用if条件语句判断是否有命令行参数传递给程序。
– 字符串操作:使用字符串连接操作将”Hello World”与命令行参数拼接。
– 格式化输出:使用fmt.Println函数将消息输出到标准输出。
注意:若没传递参数给程序,代码不打印任何消息。若传递多个参数,代码只用第一个参数并忽略其他参数。
在该目录下执行“go run hello.go ZYF ”,运行结果为“Hello World ZYF”。
(二)基本程序结构学习
在src目录下创建chapter2。
1.变量
前提:在chapter2目录下创建variables,学习总结如下:
– 变量声明:用var关键字声明变量,例如:var x int。
– 类型推断:可用:=操作符进行变量声明和赋值,Go会根据右侧值自动推断变量类型,例如:y := 5。
– 变量赋值:用赋值操作符=给变量赋值,例如:x = 10。
– 多变量声明:可同时声明多个变量,例如:var a, b, c int。
– 变量初始化:变量可在声明时初始化,例如:var name string = "John"。
– 零值:未初始化的变量会被赋予零值,数字类型为0,布尔类型为false,字符串类型为空字符串等。
– 短变量声明:在函数内部,可用短变量声明方式,例如:count := 10。
新建fib_test.go,用于简单用斐波那契数列练习:
package variables
import "testing"
func TestFibList(t *testing.T) {
a := 1
b := 1
t.Log(a)
for i := 0; i < 5; i++ {
t.Log(" ", b)
tmp := a
a = b
b = tmp + a
}
}
func TestExchange(t *testing.T) {
a := 1
b := 2
// tmp := a
// a = b
// b = tmp
a, b = b, a
t.Log(a, b)
}
下面逐个解释代码中涉及的知识点:
– package variables:声明名为”variables”的包,这是用于测试的包名。
– import "testing":导入Go语言测试框架”testing”包,用于编写和运行测试函数。
– func TestFibList(t *testing.T) { ... }:定义测试函数”TestFibList”,用于测试斐波那契数列生成逻辑。这是测试函数的标准命名,以”Test”开头,接着是被测试的函数名。
在测试函数内部,声明两个整数变量a和b,初始化为1,这是斐波那契数列前两个数。用t.Log(a)将变量a的值打印到测试日志。用循环生成斐波那契数列前5个数,每次迭代打印b的值到测试日志,并更新a和b的值生成下一个数。
– func TestExchange(t *testing.T) { ... }:定义另一个测试函数”TestExchange”,用于测试变量交换逻辑。
在测试函数内部,声明两个整数变量a和b,分别初始化为1和2。用注释展示一种变量交换写法(通过中间变量),但被注释掉。然后用a, b = b, a实现a和b交换,这是Go语言特有的交换方式,无需额外中间变量。用t.Log(a, b)将交换后变量值打印到测试日志。
2.常量
前提:在chapter2目录下创建constant,学习总结如下:
– 常量声明:用const关键字声明常量,例如:const pi = 3.14159。
– 常量赋值:常量声明时必须赋值,赋值后不可修改。
– 枚举常量:可用一组常量模拟枚举,例如:
const (
Monday = 1
Tuesday = 2
// ...
)
- 类型指定:常量类型可指定,例如:
const speed int = 300000。 - 常量表达式:常量可用表达式计算,例如:
const secondsInHour = 60 * 60。 - 无类型常量:常量可为无类型,根据上下文自动推断类型。例如,
const x = 5会推断为整数类型。
新建constant_test.go,代码如下:
package constant
import "testing"
const (
Monday = 1 + iota
Tuesday
Wednesday
)
const (
Readable = 1 << iota
Writable
Executable
)
func TestConstant1(t *testing.T) {
t.Log(Monday, Tuesday)
}
func TestConstant2(t *testing.T) {
a := 1 //0001
t.Log(a&Readable == Readable, a&Writable == Writable, a&Executable == Executable)
}
下面逐个解释代码中涉及的知识点:
– package constant:声明名为”constant”的包,这是用于测试的包名。
– import "testing":导入Go语言测试框架”testing”包,用于编写和运行测试函数。
– const (...):定义两个常量块。
第一个常量块中,用iota常量生成器定义从1开始递增的常量。在此例中,Monday赋值为1,Tuesday赋值为2,Wednesday赋值为3。iota在常量块中每次使用时递增一次,后续常量依次递增;第二个常量块中,用iota定义按位左移的常量。在此例中,Readable赋值为1,Writable赋值为2(二进制中10),Executable赋值为4(二进制中100)。位运算中,左移操作可将二进制数左移指定位数。
– func TestConstant1(t *testing.T) { ... }:定义测试函数”TestConstant1″,用于测试第一个常量块中定义的常量。
用t.Log(Monday, Tuesday)将常量Monday和Tuesday的值打印到测试日志。
– func TestConstant2(t *testing.T) { ... }:定义另一个测试函数”TestConstant2″,用于测试位运算和常量使用。
在测试函数内部,声明整数变量a,初始化为1,即二进制中0001。用位运算和按位与操作检查变量a是否有Readable、Writable和Executable属性。例如,a&Readable == Readable表达式检查a二进制表示是否含Readable标志位。用t.Log()打印三个表达式结果到测试日志。
3.数据类型
前提:在chapter2目录下创建type,学习总结如下:
主要数据类型说明
Go语言有丰富内置数据类型,用于表示不同值和数据。以下是对Go语言主要数据类型的总结分析:
– 整数类型(Integer Types):Go语言提供不同大小整数类型,如int、int8、int16、int32和int64。无符号整数类型有uint、uint8、uint16、uint32和uint64。整数类型大小取决于计算机架构,如32位或64位。
– 浮点数类型(Floating-Point Types):Go语言提供float32和float64两种浮点数类型,分别对应单精度和双精度浮点数。
– 复数类型(Complex Types):Go语言提供complex64和complex128两种复数类型,分别由两个浮点数构成。
– 布尔类型(Boolean Type):布尔类型用于表示真(true)和假(false)值,用于条件判断和逻辑运算。
– 字符串类型(String Type):字符串类型表示一系列字符。字符串不可变,可用双引号"或反引号``定义。rune
- **字符类型(Rune Type)**:字符类型用于表示Unicode字符,是int32别名。通常用单引号‘表示字符,如‘A’`。
– 数组类型(Array Types):数组是固定大小同类型元素集合。声明数组时需指定元素类型和大小。
– 切片类型(Slice Types):切片是对数组封装,是动态长度可变序列。切片不保存元素,只引用底层数组部分。
– 映射类型(Map Types):映射是键值对无序集合,用于存储和检索数据。键和值可为任意类型,但键必须可比较。
– 结构体类型(Struct Types):结构体是用户定义复合数据类型,可包含不同类型字段,每个字段有名字和类型。
– 接口类型(Interface Types):接口是抽象类型,用于定义一组方法。类型实现接口方法集合即实现该接口。
– 函数类型(Function Types):函数类型表示函数签名,包括参数和返回值类型。函数可作为参数传递和返回。
– 通道类型(Channel Types):通道是协程间通信同步机制。通道有发送和接收操作。
– 指针类型(Pointer Types):指针类型表示变量内存地址。通过指针可直接访问修改变量值。
Go语言数据类型语法语义清晰,支持丰富内置功能。合理选择使用不同数据类型可提高程序效率可读性。
具体代码展开分析
“`go
