Go 语言入门指南:基础语法和常用特性解析 | 青训营

67 阅读5分钟

Go 语言入门指南

Go语言简介

Go 语言是一种开源的、静态类型的编程语言,由Google开发。它具有简洁的语法、高效的编译器和强大的并发支持,适用于构建高性能的网络服务和分布式系统,运用在许多如docker、Kubernetes等项目中,距今已有十年历史。

本篇笔记旨在总结Go语言的基础语法和常用特性,同时对比了其他类C语言的常见特性,帮助同学们快速入门与复习。

基础语法

基础语法部分比较简单,和类c语言都大差不差。

因此这里主要模仿go语言规范,帮大家给出具体的简单例子,同学们复习时直接看相应用法即可。

在主函数内,要加入package main,这表示该文件属于main包的一部分,说明是程序的入口包,文件是程序的入口文件。

变量声明与赋值

Go语言使用var关键字声明变量,可指定变量类型或由编译器自动推断类型。变量赋值使用等号(=)进行。

var name string
name = "Zhang san"

var age int = 36

city := "Beijing"

如果要声明常量,可以将var改为const

个人理解:这里的:=就相当于C++里的auto的作用

数据类型

Go语言有基本数据类型,如整数(int)、浮点数(float64)、布尔值(bool)等。还有复合数据类型,如字符串(string)、数组(array)、切片(slice)、映射(map)、结构体(struct)等。

直接看用法:

var num int = 10

var pi float64 = 3.14

var isTrue bool = true

var name string = "Li Si"

var numbers = []int{1, 2, 3, 4, 5}

var person = struct {
    name string
    age  int
}{
    name: "Zhang san",
    age:  36
}

控制流程

Go语言支持常用的控制流程语句,如条件语句(if-else)、循环语句(for)、选择语句(switch)等。

个人理解:Go语言虽然是类c语言,但是为了简洁也与其他语言有许多不同之处,如下面的switch语句,这里不同于c、java等,不需要break语句。

用法如下:

if age >= 36 {
    fmt.Println("年龄大于36")
} 

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

switch day {
case "Monday":
    fmt.Println("It's Monday.")
case "Tuesday":
    fmt.Println("It's Tuesday.")
default:
    fmt.Println("It's another day.")
}

函数定义

Go语言中的函数使用关键字func进行定义,可以有多个参数和返回值。 注意变量类型需要后置。

个人理解:GO语言相比其他语言更加简洁,变量类型后置可以方便我们阅读函数名与返回值

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

result := add(2, 3)
fmt.Println(result) // 输出:5

常用特性解析

并发支持

Go语言内置了轻量级的协程(goroutine)和通信机制(channel),方便编写并发程序。协程可以看作是轻量级的线程,而通信机制则用于协程之间的数据传递。

goroutine

Goroutine是Go语言中的一个重要特性,可以让我们在同一个程序中并发地执行多个函数或方法,从而提高程序的效率和性能。相较于操作系统的线程,Goroutine更容易创建和管理,也更节省资源。每个Goroutine都有自己的栈空间,初始大小为2KB,可以根据需要动态地增长或缩小。

关于Goroutine的调度和同步,Go语言的运行时系统可以自动帮我们完成,我们只需要用go关键字,像调用函数一样启动一个Goroutine协程。具体来说,我们不需要像操作系统的线程那样手动地对线程进行创建、销毁、阻塞、唤醒等操作。Go语言使用M:N调度模型,它可以将M个Goroutine映射到N个操作系统线程上,从而实现高效地利用多核CPU。

个人理解:这里使用Goroutine相当于独立于主线程外新增了一个线程,这样可以增加线程利用率

需要注意的是,由于是并发特性,所以不会等待协程执行完毕,因此,下面的代码顺序是不确定的。

func Hello() {
    fmt.Println("Hello, world!")
}

func main() {
    go Hello() 
    fmt.Println("2")
}

此时结果可能为,首先打印2,然后再输出Hello,world。 最后,Hello函数是否会输出结果也是不确定的,如果Hello函数比较复杂,而main函数又马上结束,那么Hello函数可能不会有任何输出结果。

channel

如果多个Goroutine之间想要相互交流应该如何处理?这里Go语言引入了Channel。Channel是Go语言中的一种通信机制,它可以让一个Goroutine向另一个Goroutine发送和接收信息。

要创建一个Channel,我们可以使用内置的make函数,并指定Channel的类型和可选的容量,如果不为channel默认容量,则创建了一个无缓冲类型的channel。无缓冲Channel要求发送和接收操作必须同时发生,否则会阻塞当前Goroutine 这里我们就选择Channel传递的信息类型为Int

ch := make(chan int) // 创建一个无缓冲的整数类型的Channel
ch := make(chan int, 10) // 创建一个有缓冲(容量为10)的整数类型的Channel

个人理解:Channel有点类似与命令里的管道“|”,通过ch变量,我们可以向Ch发送一个信息(使用<-运算符),然后利用ch传递给相应变量。如

ch <- 42 // 向ch发送42
x := <-ch // 从ch接收42并赋给x

另外,还可以使用for range循环来不断地从一个Channel接收值,如

for x := range ch {
    fmt.Println(x) // 打印从ch接收到的每个值
}

最后,可以使用内置的close函数关闭一个channel。这里要注意不能向已经关闭的channel发送信息,否则会导致异常。

错误处理

Go语言推荐使用错误值(error)来处理异常情况。函数可以返回一个错误值,调用者可以根据错误值进行适当的处理。

个人理解:关于错误处理,就体现了Go语言多返回值的优越性。相比与Java要抛出异常,Go可以直接接受一个nil,并再后面判断nil从而判断是否出错。

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}
result, err := divide(10, 0)
if err != nil {
    fmt.Println("Error:", err)
} else {
    fmt.Println("Result:", result)
}

包管理

Go语言使用使用 import 关键字导入包,可以单个导入或者批量导入。 import导入时,会从GO的安装目录,即GOROOT环境变量设置的目录,和GOPATH环境变量设置的目录中,检索 src/package 来导入包。如果不存在,则导入失败。

GOROOT,就是GO内置的包所在的位置。GOPATH,就是我们自己定义的包的位置。

// 单个导入
import "package"
// 批量导入
import (
  "package1"
  "package2"
  )

总结

本篇笔记总结了Go语言的基础语法和常用特性,并且包含了本人在学习时一些自己的理解。希望同学们可以指出我的错误,在复习go语言特性时可以通过这篇文章快速复习相关知识点。