Go语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题

124 阅读11分钟

一、语言特点

  1. 高性能

    go语言有与c++、java相当的性能,还内建了对高并发的支持,不像很多编程语言以库的形式支持,在go语言里,不需要去寻找经过高度性能优化的第三方库来应用,只需要使用标准库或者基于标准库的第三方库即可开发高并发应用程序。

  2. 语法简单、学习曲线平缓

    go语言语法风格类似于c语言,并在c语言的基础上进行了大幅度的简化,比如去掉了不需要的表达式括号,循环也只有for循环一种表示方法,可以同时实现数值,键值等的遍历。go语言仅需要10行代码就可以实现一个可承载静态文件访问的,支持高并发,高性能的服务器。

  3. 丰富的标准库

    拥有极其丰富、功能完善、质量可靠的标准库,很多情况下不需要借助第三方库就可以完成大部分基础功能的开发,大大降低了学习和使用成本。最关键的是,标准库有很高的稳定性和兼容性保障,还能持续享受语言迭代带来的性能优化,这些是第三方库所不具备的。

  4. 完善的工具链

    go语言在诞生之初就拥有丰富的工具链,无论是编译、代码格式化、错误检查、帮助文档、包管理,还有代码补充提示,这些都有对应的工具。go语言还内置了完整的单元测试框架、能支持单元测试、性能测试、代码覆盖率、数据竞争检测、性能优化等,这些都是能够保障代码能够正确稳定运行的必备工具。

  5. 静态链接

    在go语言里,所有的编译结果默认都是静态链接的,包含了运行所需的所有库,只需要拷贝编译之后的唯一可执行文件,不附加任何东西就能够部署运行。在容器环境下运行,镜像体积可以控制的非常小,部署非常方便快捷

  6. 快速编译

    go语言拥有静态语言里几乎最快的编译速度,在本地开发的时候,修改完一行代码,都能够在一秒钟左右编译完成。

  7. 跨平台

    go语言本身能够在常见的linux、windows、macOS等操作系统下运行,也能够用来开发安卓、iOS软件。go语言还能在路由器、树莓派等设备上去运行。go语言还有很方便的交叉编译特性,能够轻易的在你的笔记本上编译出来一个二进制文件拷贝到路由器上面运行,而无需配置交叉编译环境。

  8. 垃圾回收

    go语言是一门带垃圾回收的语言,和java类似,写代码的时候,无需考虑内存的分配释放,可以专注于业务逻辑。

二、开发环境

  • 开发环境--安装Golang

    go.dev/

    studugolang.com/dl

    goproxy.cn/

  • 开发环境--配置集成开发环境

    • vscode 一款由微软公司开发的,能运行在 Mac OS X、Windows 和 Linux 上的跨平台开源代码编辑器,虽然它是一款编辑器,但是它可以通过扩展程序为编辑器实现,包括代码高亮、代码提示编译调试、文档生成等功能,配置完成之后可以视为一个功能齐全的IDE。 安装 VSCode ,直接从官网下载安装即可,安装完成之后,需要在左边扩展里面搜索 Go 插件然后安装。

    • Goland

      Goland 是由 JetBrains 公司开发的一个新的商业 IDE,相比 vscode ,它在重构、代码生成等方面做得更好。

  • 开发环境--基于云的开发环境

    gitpod.io/#github.com…

    短链接:hi-hi.cn/gitpod

三、基础语法

1. Hello, World!

我们从经典的 "Hello, World!" 程序开始。Go 语言的 Hello, World! 代码如下:

 package main
 ​
 import "fmt"
 ​
 func main() {
     fmt.Println("Hello, World!")
 }

代码解析

  • 第一行 package main:声明该文件属于 main 包。main 包是程序的入口包。
  • 第三行 import "fmt":导入 Go 标准库中的 fmt 包,用于格式化和输出字符串。
  • main 函数:这是程序的入口点。fmt.Println 用于输出 "Hello, World!" 字符串。

运行程序

  • 使用 go run helloworld.go 命令直接运行。
  • 如果想将代码编译成二进制文件,可以使用 go build 命令,生成的二进制文件可以通过 ./helloworld 直接运行。

扩展阅读fmt 包中还有许多其他用于格式化和输出的函数。访问 pkg.go.dev 并输入 fmt 查看该包的详细文档。

2. 变量与常量

接下来我们来看变量的声明和使用。

Go 是一门强类型语言,每个变量都有自己的类型。常见类型包括字符串、整数、浮点型和布尔值等。

变量声明

  1. 使用 var 关键字声明变量。例如:

     var name string = "Go"
    

    Go 会自动推导变量的类型,当然你也可以显式指定类型。

  2. 使用简短声明:用 := 直接赋值。例如:

     name := "Go"
    

常量声明: 常量使用 const 关键字,例如:

 const pi = 3.14

与变量不同,常量没有确定的类型,Go 会根据上下文自动推导。

3. 条件判断:if else

Go 的 if else 语法与 C 或 C++ 类似,但有以下不同:

  • if 后不需要括号,若添加括号,保存时编辑器会自动去除。
  • if 后的代码块必须用大括号 {} 包裹,不能简写在同一行。

示例代码:

 if x > 10 {
     fmt.Println("x 大于 10")
 } else {
     fmt.Println("x 小于或等于 10")
 }

4. 循环:for

Go 语言中没有 whiledo while 循环,只有一种 for 循环。最简单的 for 循环如下:

 for {
     // 死循环
 }

也可以写成经典的 C 风格:

 for i := 0; i < 10; i++ {
     fmt.Println(i)
 }

控制循环

  • 使用 break 跳出循环。
  • 使用 continue 跳过当前迭代,继续下一个循环。

5. 分支结构:switch

Go 语言的 switch 结构与 C 或 C++ 类似,但有一个显著区别:在 Go 中,switchcase 分支自动包含 break,不会自动贯穿多个分支。

此外,Go 的 switch 更加强大,允许使用任意变量类型,甚至可以取代 if else 语句。例如:

 switch x {
 case 1:
     fmt.Println("x 是 1")
 case 2:
     fmt.Println("x 是 2")
 default:
     fmt.Println("x 不是 1 也不是 2")
 }

无条件的 switch: Go 支持不带表达式的 switch,在 case 中写条件分支:

switch {
case x < 0:
    fmt.Println("x 是负数")
case x == 0:
    fmt.Println("x 是零")
default:
    fmt.Println("x 是正数")
}

6. 数组

数组是 Go 语言中长度固定的、按编号存储的元素序列。以下是一个存放 5 个 int 元素的数组 a

var a [5]int

特点

  • 数组长度固定,一旦声明无法改变。
  • 可以通过索引访问或修改数组中的元素。

在实际业务代码中,数组使用较少,因为其长度固定。更常用的动态长度的数据结构是 切片


7. 切片

切片类似于数组,但长度可以动态变化,且提供了更多灵活的操作。例如,可以使用 make 函数创建一个切片:

s := make([]int, 5) // 创建长度为5的切片

切片操作

  • 取值:切片可以像数组一样通过索引取值。

  • 追加元素:使用 append 向切片添加元素,并将返回值重新赋给原切片。

    s = append(s, 10)
    
  • 切片的内部结构:切片包含三个部分:长度、容量和指向底层数组的指针。若容量不足,append 会自动扩容并返回新的切片。

切片还支持类似 Python 的切片操作:

subSlice := s[1:4] // 取出索引1到4(不包括4)的元素

不同于 Python,Go 不支持负数索引。

8. Map

Map 是一种键值对(key-value)数据结构,类似于其他语言的哈希表或字典。在 Go 中,map 是常用的数据结构之一。

创建 Map

m := make(map[string]int) // 键为string类型,值为int类型

操作

  • 存取元素:可以直接通过键存储和读取值。

  • 删除元素:使用 delete 函数删除键值对。

    delete(m, "key")
    

注意:Go 的 map 是无序的,遍历时不会按插入或字母顺序输出,而是随机顺序。

9. 遍历:range

slicemap 中,range 可以简化遍历操作:

for i, v := range s {
    fmt.Println(i, v)
}
  • 对数组或切片,range 返回索引和对应值。

  • 若不需要索引,可用下划线 _ 忽略:

    for _, v := range s {
        fmt.Println(v)
    }
    

10. 函数

Go 语言的函数支持多返回值和后置变量类型。以下是一个实现两个变量相加的函数示例:

func add(a int, b int) int {
    return a + b
}

多返回值: 在业务代码中,许多函数都会返回两个值:第一个是结果,第二个是错误信息。

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

11. 指针

Go 支持指针,但相比 C/C++,指针操作较为有限。指针主要用于在函数中对传入参数进行修改。

指针示例

func addTwo(num *int) {
    *num += 2
}

在调用函数时使用 & 获取变量的地址:

x := 5
addTwo(&x) // x 现在为7

如果不使用指针,函数中的参数值只是原始变量的副本,无法直接修改原始变量。

12. 结构体

结构体是一种包含多个字段的数据结构,用于将不同类型的数据组合在一起。例如:

type User struct {
    Name     string
    Password string
}

初始化结构体

u := User{Name: "Alice", Password: "1234"}

也可以仅初始化部分字段:

u := User{Name: "Alice"}

使用结构体指针可以避免大结构体的复制,提高性能:

func updateUser(u *User) {
    u.Password = "newpassword"
}

13. 结构体方法

可以为结构体定义方法,使其类似于其他语言中的类方法。例如,将 checkPassword 方法绑定到 User 结构体:

func (u *User) checkPassword(password string) bool {
    return u.Password == password
}

调用时,可以直接使用点操作符:

u.checkPassword("1234")

值接收者与指针接收者

  • 使用指针接收者可以修改结构体实例的字段。
  • 使用值接收者则不会修改结构体的原始值,因为操作的是一个副本。

14. 错误处理

在 Go 语言中,错误处理使用一个单独的返回值来传递错误信息,不同于 Java 的异常处理机制。Go 的错误处理方法能够清晰地指明错误的来源函数,并且可以通过简单的 ifelse 语句来处理错误。

示例

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

在函数返回时,如果出现错误,我们可以返回 nilerror,如果没有错误则返回结果和 nil

15. 字符串操作

Go 标准库中的 strings 包提供了多种常用的字符串工具函数,例如:

  • Contains:检查字符串是否包含另一个字符串
  • Count:统计子字符串的出现次数
  • Index:查找子字符串的索引
  • Join:连接多个字符串
  • Repeat:重复字符串
  • Replace:替换子字符串

示例

import "strings"

s := "hello world"
fmt.Println(strings.Contains(s, "world")) // 输出: true

16. 字符串格式化

Go 语言中的 fmt 包提供了丰富的字符串格式化工具,Printf 函数类似于 C 语言的 printf,但提供了更灵活的格式选项:

  • %v:可以打印任意类型
  • %+v:详细打印结构体内容
  • %#v:更详细的输出,显示完整的结构体定义

示例

import "fmt"

type Person struct {
    Name string
    Age  int
}

p := Person{"Alice", 30}
fmt.Printf("%v\n", p)   // 输出: {Alice 30}
fmt.Printf("%+v\n", p)  // 输出: {Name:Alice Age:30}
fmt.Printf("%#v\n", p)  // 输出: main.Person{Name:"Alice", Age:30}

17. JSON 处理

Go 语言中的 encoding/json 包简化了 JSON 的序列化和反序列化。只要结构体的字段首字母大写(公开字段),就可以使用 json.Marshal 将其序列化为 JSON 字符串,或用 json.Unmarshal 解析 JSON 数据。

示例

import "encoding/json"

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

u := User{"Alice", "alice@example.com"}
jsonData, _ := json.Marshal(u) 
fmt.Println(string(jsonData))  // 输出: {"name":"Alice","email":"alice@example.com"}

JSON Tag:可以通过 json 标签修改输出 JSON 中的字段名称。

18. 时间处理

在 Go 中,time 包用于处理时间和日期。最常用的函数是 time.Now() 获取当前时间。time.Date 可以用于构造一个带时区的时间对象。

示例

import "time"

now := time.Now()
fmt.Println(now.Format("2006-01-02 15:04:05")) // 格式化输出当前时间

past := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
duration := now.Sub(past) // 计算时间差
fmt.Println(duration.Hours()) // 得到差值的小时数

时间戳:可以通过 Unix() 方法获取时间戳。

19. 数字解析

Go 中 strconv 包用于字符串和数字之间的转换,常用的函数包括:

  • strconv.Atoi:将字符串解析为整数
  • strconv.Itoa:将整数转换为字符串
  • strconv.ParseIntstrconv.ParseFloat:解析指定进制的字符串为整数或浮点数

示例

import "strconv"

num, err := strconv.Atoi("42")
str := strconv.Itoa(42)

如果输入不合法,这些函数会返回 error

20. 进程信息

在 Go 中,可以使用 os.Args 获取命令行参数。例如,假设编译后的二进制文件名为 command,运行时传入参数 abcd,则 os.Args 会返回一个包含 commandabcd 的切片。

示例

import "os"

func main() {
    fmt.Println(os.Args) // 输出: [command abcd]
}

此外,可以使用 os.Getenv 获取环境变量。