安装Go语言:
前往go.dev下载go
安装VScode扩展
基础语法:
-
包管理
Go语言中的包是一个重要的概念,用于组织和管理代码。包可以看作是一组相关的源代码文件的集合。通过使用包,可以将代码模块化,使得代码更加清晰和易于维护。Go语言标准库中就包含了大量的包,例如fmt(用于格式化输入输出)、math(用于数学运算)等。
其中包操作有包引入和包声明- 包声明:每个Go源文件都必须属于一个包。在每个源文件的第一行通常会有一个package声明,指定该文件属于哪个包。
- 包引入:使用其他包中的函数、变量或类型时,需要先导入这些包。使用import关键字来导入包。 其中,包名可以使用相对路径或绝对路径
-
基本数据类型
在Go语言中,变量和常量是用来存储数据的重要元素。它们提供了存储值的方法,并允许你在程序中引用这些值。- 变量:可以用var声明或:=隐式声明,可以等于整型、浮点型、字符串。可以在变量名后规定类型,也可以让Go语言推测变量类型,但注意这不像Js那样类型可变,一个变量定义了一个类型后不可变类型。可以像python一样一组变量声明一组初值。一个变量如果只声明不赋值默认为0,但必须表明变量类型。
- 常量:用const声明不能隐式声明也不能多变量声明,另外可以用const()对集体声明,其余和变量声明方式一致。
- 枚举:用iota表示,本质是常量,但在const()中赋值,第一个赋值iota,当前值为0,下面其他会依次变成1、2、3……递增
- 匿名变量:用_表示,如果我们不关心返回值可以用这个
-
控制结构与range
- 选择:
- if 语句:if 语句用于执行基于条件判断的代码块。可以在 if 语句后面直接跟一个布尔表达式,也可以使用初始化语句。
- switch 语句:switch 语句用于多分支选择,可以根据多个条件来决定执行哪一段代码。
- 循环
- for 语句:for 语句是Go语言中唯一的循环结构。它可以用于无限循环、条件循环和迭代循环。
- 很遗憾,没有while
- range
range 语句用于遍历数组、切片、映射以及通道- 当你使用 range 遍历数组或切片时,它会返回两个值:索引和元素值。
- 当你使用 range 遍历字符串时,它会将字符串视为一系列的字节,range 会返回每个字节及其位置。
- 当你使用 range 遍历映射时,它会返回两个值:键和对应的值。
- 当你使用 range 遍历通道时,它会不断地从通道中读取数据,直到通道关闭为止。
- 跳转语句:break和continue
- 选择:
-
函数
- 定义:基本函数用func定义,格式 func 函数名(参数列表) (返回值列表) {代码块}
- 多返回值:在Go语言中,函数不仅可以返回单个值,还可以返回多个值。通常,多返回值用于返回一个主要的结果和一个或多个辅助信息(例如错误信息)。多返回值通过逗号分隔的列表来表示。返回值列表可以不规定参数名,但需要返回相关变量。如果都有变量名,规定的返回变量可以在函数中参与运算,同时可以只写一个return结束函数,也可以不return那几个变量,只return同类型变量。
- 匿名函数:没有函数名的函数,可以被定义后立即执行,可以被var和隐式赋值赋给别的变量,亦可以被当作参数传给别的函数做参数(注:上三种操作,除了第一种,剩下两种普通函数也可以这么干)
- 匿名函数支持闭包操作,即函数返回一个被加工过的匿名函数
-
数组与切片
在Go语言中,数组和切片是非常重要的数据结构,它们用于存储一组相同类型的元素。虽然它们的功能相似,但在使用上有着显著的区别。- 数组:数组是一组具有固定大小的相同类型元素的集合。一旦创建,数组的长度是固定的,不能改变。用arr := [5]int{1, 2, 3, 4, 5}形式定义,其中[5]可用[...]让编译器自己算{}里元素的大小,{}也不是要一定带着
- 切片:除了[]里不要写长度,其他和数组相似,有append追加和[:]切片以及sort包里其他的方法,可以用make()创建一个切片。也可以通过delete删除
-
映射
在Go语言中,映射(map)是一种无序的键值对集合。映射提供了一种快速查找数据的方式,其中键(key)是唯一的,而值(value)可以重复。映射是动态的数据结构,可以在运行时改变大小。 可以形如 m := map[string]int{"apple": 5,"banana": 10,"orange": 7,}定义也可以形如 m := make(map[string]int) \n m["apple"] = 5 \n m["banana"] = 10 \n m["orange"] = 7 \n 定义
可以用make定义,用delete删除
注意,他没有并发安全,如果要并发请加sync.Mutex
-
结构体
-
结构体可以通过type关键字和struct关键字定义。如:type Person struct
-
可以使用结构体字面量或new关键字创建结构体实例。如:p1 := Person{Name: "Alice", Age: 30} 也可以 p2 := new(Person) \n p2.Name = "Bob" \n p2.Age = 25
-
匿名结构体允许你在不预先定义的情况下直接定义结构体类型。如p := struct { Name string Age int }{ Name: "David", Age: 40, }
-
可以在结构体上定义方法,这些方法可以接收结构体的实例作为接收者。形如func (绑定的结构体) 函数名() 返回值,如果func (结构体指针) 函数命() 返回值 就会改变原结构体里的值
-
-
指针
在Go语言中,指针是一种特殊的变量类型,它存储的是另一个变量的内存地址,而不是实际的值。使用指针可以避免不必要的内存复制,提高程序的效率。下面详细介绍Go语言中指针的使用方法。- 声明指针:声明一个指针变量需要使用*符号,并指定该指针指向的数据类型。如var p *int
- 使用&操作符获取变量的内存地址。
- 使用*操作符对指针进行解引用,以访问或修改指针所指向的值。
- 指针的零值为nil,表示该指针没有指向任何有效的内存地址。
- 指针也可以作为数组的元素。
- 传递指针给函数可以避免复制大的数据结构,从而提高性能。
-
错误处理
Go语言中的错误通常使用error接口来表示。这个接口非常简单,只有单一的方法Error() string。任何实现了这个方法的类型都可以作为错误处理。
fmt.Errorf和errors.New:这两个函数可以用来创建新的错误实例,其中fmt.Errorf允许你使用格式化字符串。 -
JSON
Go语言提供了内置的支持来处理JSON数据,这主要通过 encoding/json 包来实现。这个包包含了一系列的功能,从序列化(把Go对象转换成JSON字符串)到反序列化(把JSON字符串转换成Go对象)。- 将Go对象序列化为JSON字符串:要将Go对象转换为JSON字符串,你可以使用 json.Marshal 或 json.MarshalIndent 函数。Marshal 函数简单地将Go对象转换为紧凑的JSON字符串,而 MarshalIndent 则允许你在生成的JSON字符串中添加缩进以便更易读。
- 将JSON字符串反序列化为Go对象:要将JSON字符串转换为Go对象,你可以使用json.Unmarshal函数。
-
时间
在Go语言中,时间操作主要通过标准库中的time包来完成。这个包提供了丰富的功能,包括时间的解析、格式化、计算以及各种时间相关的操作。下面将详细介绍这些功能。- 获取当前时间:要获取当前的时间,可以使用time.Now()函数。该函数返回一个Time对象,包含了当前的日期和时间信息。
- 时间格式化:Time对象可以通过Format方法进行格式化输出。这个方法接受一个字符串作为格式化模板,类似于C语言中的strftime函数。
- 解析时间字符串:要从字符串中解析时间,可以使用Parse方法。这个方法接受两个参数:一个是时间格式模板,另一个是要解析的时间字符串。
- 时间计算:Time对象支持基本的算术运算,比如加减秒数、分钟数、小时数等。
- 时间比较:Time对象可以直接使用比较运算符(如<, >, ==等)进行比较。
- 定时器:定时器是time包提供的另一种重要工具,用于在指定的时间间隔后执行某个操作。
- 周期性任务:如果需要周期性地执行某些任务,可以使用ticker。ticker类似于定时器,但是会每隔固定的时间间隔重复触发。
12.进程
os.Args, os.Getenv, os.Setenv, 和 exec.Command 是Go语言中用于处理命令行参数、环境变量以及执行外部命令的重要工具。下面分别介绍它们的使用方法:
-
- os.Getenv 和 os.Setenv:os.Getenv 用于从环境中读取值,而 os.Setenv 用于设置环境变量。
- exec.Command:exec.Command 用于创建一个新的进程来执行外部命令。它返回一个 Cmd 结构体,该结构体提供了控制进程执行的方法。
- os.Args:os.Args是一个字符串切片,包含了程序运行时的所有命令行参数。第一个元素通常是程序本身的路径,后续元素则是传递给程序的参数。
- 处理数字:
在Go语言中,处理数字字符转换为数字的常用函数包括strconv包中的Atoi用于将字符串转换为整数,以及ParseInt和ParseFloat用于更灵活地解析不同基数的整数和浮点数. - 处理字符串:
在Go语言中,处理字符串的主要函数和方法包括 len() 获取字符串长度,for ... range 遍历字符串中的每个字符(包括多字节字符),strings 包中的 Contains, Index, Replace, Split,Count,HasPrefix,HasSuffix,Join,Repeat,Replace,ToLower,ToUpper 等函数用于常见的字符串操作,如查找子串、替换和分割。此外,还有 fmt.Sprintf 和 fmt.Sprintf 的变体用于格式化字符串输出,而 strconv 包提供了将字符串与其他基本数据类型之间相互转换的功能。这些工具覆盖了大多数常见的字符串处理需求。
特性:
Go语言以其简洁、易读和易写的语法、静态类型和编译型的特点、高效的并发处理通过轻量级的协程和通道机制、丰富的标准库、跨平台和可移植性、强大的性能以及支持多范式编程,并在编译阶段进行静态类型检查以确保类型安全,成为一种高效、可靠且易于学习的语言。
示例程序
- 代码:
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"time"
)
// 自定义错误类型
type CustomError struct {
Code int
Message string
}
func (e *CustomError) Error() string {
return fmt.Sprintf("错误码: %d, 错误信息: %s", e.Code, e.Message)
}
// 常量和枚举示例
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
// 结构体定义
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
// 结构体方法
func (p *Person) Birthday() {
p.Age++
}
// 返回多个值的函数,包含错误处理
func divide(a, b float64) (quotient float64, err error) {
if b == 0 {
return 0, &CustomError{
Code: 1001,
Message: "除数不能为0",
}
}
quotient = a / b
return
}
// 模拟可能出错的文件操作
func processFile(filename string) error {
// 检查文件是否存在
if _, err := os.Stat(filename); os.IsNotExist(err) {
return fmt.Errorf("文件 %s 不存在: %w", filename, err)
}
return nil
}
// 闭包函数示例
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
// 执行外部命令的函数
func executeCommand(command string, args ...string) error {
cmd := exec.Command(command, args...)
// 获取标准输出
stdout, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("无法获取标准输出管道: %w", err)
}
// 开始执行命令
if err := cmd.Start(); err != nil {
return fmt.Errorf("命令启动失败: %w", err)
}
// 打印标准输出
output := make([]byte, 1024)
n, err := stdout.Read(output)
if err != nil {
return fmt.Errorf("读取标准输出失败: %w", err)
}
fmt.Printf("命令输出: %s\n", output[:n])
// 等待命令完成
if err := cmd.Wait(); err != nil {
return fmt.Errorf("命令执行失败: %w", err)
}
return nil
}
func main() {
// 1. 基础错误处理
if result, err := divide(10, 0); err != nil {
fmt.Println("除法错误:", err)
if customErr, ok := err.(*CustomError); ok {
fmt.Printf("这是一个自定义错误,错误码:%d\n", customErr.Code)
}
} else {
fmt.Printf("除法结果: %f\n", result)
}
// 2. 文件操作错误处理
err := processFile("nonexistent.txt")
if err != nil {
fmt.Println("文件处理错误:", err)
}
// 3. 进程和环境变量操作
// 获取当前进程ID
fmt.Printf("当前进程ID: %d\n", os.Getpid())
// 获取当前工作目录
currentDir, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
fmt.Printf("当前工作目录: %s\n", currentDir)
// 设置和获取环境变量
os.Setenv("MY_CUSTOM_VAR", "自定义环境变量值")
fmt.Printf("环境变量MY_CUSTOM_VAR: %s\n", os.Getenv("MY_CUSTOM_VAR"))
// 获取所有环境变量
fmt.Println("\n环境变量列表:")
for _, env := range os.Environ()[:3] { // 只显示前三个环境变量
fmt.Println(env)
}
// 获取命令行参数
fmt.Printf("\n命令行参数: %v\n", os.Args)
// 执行外部命令
fmt.Println("\n执行外部命令:")
cmd := exec.Command("echo", "Hello, World!")
output, err := cmd.Output()
if err != nil {
fmt.Println("命令执行错误:", err)
} else {
fmt.Printf("命令输出: %s\n", output)
}
// 4. 基本数据类型和变量
var name string = "Alice" // 使用了这个变量
age := 25
pi := 3.14159
fmt.Printf("\n基本数据类型变量: name = %s, age = %d, pi = %f\n", name, age, pi)
// 5. 数组和切片
numbers := [5]int{1, 2, 3, 4, 5}
slice := numbers[1:4]
slice = append(slice, 6)
// 6. 映射
var userAges sync.Map
userAges.Store("Bob", 30)
userAges.Store("Alice", 25)
// 7. range遍历
fmt.Println("\n使用range遍历切片:")
for i, v := range slice {
fmt.Printf("索引: %d, 值: %d\n", i, v)
}
// 8. JSON处理
person := Person{Name: "Charlie", Age: 35}
jsonData, err := json.MarshalIndent(person, "", " ")
if err != nil {
fmt.Println("JSON编码错误:", err)
} else {
fmt.Printf("\nJSON数据:\n%s\n", string(jsonData))
}
// 9. 时间处理
now := time.Now()
fmt.Printf("\n当前时间: %s\n", now.Format("2006-01-02 15:04:05"))
// 10. 字符串处理
str := "Hello, World!"
fmt.Printf("\n字符串操作:\n")
fmt.Printf("长度: %d\n", len(str))
fmt.Printf("包含'World': %v\n", strings.Contains(str, "World"))
fmt.Printf("转大写: %s\n", strings.ToUpper(str))
fmt.Printf("分割结果: %v\n", strings.Split(str, ", "))
// 11. 数字处理
numStr := "123"
num, err := strconv.Atoi(numStr)
if err != nil {
fmt.Println("字符串转换为数字失败:", err)
} else {
fmt.Printf("\n字符串转换为数字: %d\n", num)
}
// 12. 错误处理链
if err := executeCommand("nonexistent-command"); err != nil {
fmt.Printf("\n命令执行错误: %v\n", err)
}
// 13. 使用errors包:自定义错误
errExample := errors.New("这是一个由errors包创建的简单错误")
fmt.Println("\n使用errors包创建的错误:", errExample)
// 14. 使用path/filepath包
filePath := "some/path/to/file.txt"
absolutePath, err := filepath.Abs(filePath)
if err != nil {
fmt.Println("获取绝对路径失败:", err)
} else {
fmt.Printf("文件的绝对路径: %s\n", absolutePath)
}
// 15. 使用sort包
unsorted := []int{5, 2, 8, 1, 4}
sort.Ints(unsorted)
fmt.Println("\n排序后的切片:", unsorted)
// 16. 使用sort包对字符串进行排序
names := []string{"Bob", "Alice", "Charlie"}
sort.Strings(names)
fmt.Println("\n排序后的名字:", names)
}
- 运行结果: