Go语言基础语法和常用特性解析 | 青训营

55 阅读5分钟

简述

Go 语言是一门静态类型、编译型、并发型,并具有垃圾回收功能的编程语言。
Go 语言的语法接近 C 语言,支持垃圾回收,同时也支持面向对象编程范式, 但在语法上并不支持类和继承等面向对象的概念,可以通过结构体和方法来实现类似的功能。

1. 变量

Go 语言中的变量声明格式为:var 变量名 变量类型

var a int
var b string
var c bool

变量声明和初始化也可以使用一种更为简短的模式,变量名 := 表达式,Go语言支持自动推断类型

a := 1
b := "hello"
c := true

需要注意的是,在Go语言中,变量声明后必须要使用,否则会编译不通过。 如果我们只是想声明一个变量,但暂时不想使用它,可以使用下划线符号_来表示该变量不被使用

var _ int

2. 常量

Go 语言中使用const关键字来声明常量

const a int = 1
const b string = "hello"

3. 流程控制

3.1 条件语句

Go 语言中的条件语句有ifswitch两种

3.1.1 if
if a > 0 {
    fmt.Println("a > 0")
} else if a < 0 {
    fmt.Println("a < 0")
} else {
    fmt.Println("a = 0")
}

功能上和 C 语言中的if语句类似, 只是语法上略有不同,比如if后的条件不需要使用括号包裹

3.1.2 switch
switch a {
case 1:
    fmt.Println("a = 1")
case 2:
    fmt.Println("a = 2")
default:
    fmt.Println("a != 1 && a != 2")
}

3.2 循环语句

3.2.1 for

Go 语言中的循环语句只有forrange两种

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

其中,for的三个语句都是可选的,可以省略其中的任意一个或多个, 类似于 C 语言中的while(true)可以写成:

    for {
        fmt.Println("hello")
    }
3.2.2 range

Go 语言中的range关键字可以用来遍历 数组、切片、字符串、map等数据结构

for i, v := range arr {
    fmt.Println(i, v)
}

例如上例中,i表示数组的下标,v表示数组的值

3.3 break 和 continue

和 C 语言中的用法一致

4. 函数

4.1 函数声明

Go 语言中的函数使用func关键字来声明

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

其中,ab为形参,int是函数的返回值类型

4.2 函数返回

比较有特点的是,Go 语言中的函数可以返回多个值

func swap(a, b int) (int, int) {
    return b, a
}

在调用时,可以同时接受两个返回,也可以使用下划线符号_来表示不需要的返回值

a, b := swap(1, 2)
_, b := swap(1, 2)

5. 指针

Go 语言中的指针和 C 语言中的指针类似, 但是不及C语言中的指针灵活,Go语言中的指针不能进行运算

a := 1
b := &a
fmt.Println(*b) // 1

6. 结构体

Go 语言中的结构体和 C 语言中的结构体类似, 但是不同的是,Go语言中的结构体可以包含方法,也就是更接近于类的概念

type Person struct {
    name string
    age int
}

func (p Person) say() {
    fmt.Println("hello")
}

其中,Person为结构体名,nameage为结构体的成员,还包含了一个say方法

7. 接口

作为补充,Go语言中也存在接口的概念,

type Shape interface {
Area() float64
Perimeter() float64
}

其中,Shape为接口名,AreaPerimeter为接口的方法, 我们的结构体可以通过实现接口的方法来实现接口

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

由于Rectangle结构体实现了Shape接口中的所有方法,因此它被认为是Shape接口的实现类型。

8. 并发

8.1 goroutine

Go 语言中的并发使用goroutine来实现,goroutine即协程, 相较于线程,协程的开销更小,可以同时运行成千上万个协程。

func main() {
    for i := 0; i < 10; i++ {
        go func(i int) {
            fmt.Println(i)
        }(i)
    }
    time.Sleep(time.Second)
}

在上面的例子中,我们使用go关键字来开启一个协程, 使用for循环开启了10个协程,每个协程都会打印出自己的编号, 使用time.Sleep来等待协程执行完毕。

8.2 channel

Go 语言中的协程之间通信使用channel来实现,channel即管道,

func main() {
    ch := make(chan int)
    go func() {
        ch <- 1
    }()
    fmt.Println(<-ch)
}

在上面的例子中,我们使用make函数来创建了一个channel, 然后在协程中向channel中写入了一个值,最后在主协程中读取了这个值。

9. 包

Go 语言中的包使用package关键字来声明,一个包中可以包含多个文件, 但是一个文件只能属于一个包。

包的导入使用import关键字来实现,导入包之后,可以使用包中的函数和变量。 比如我们最早使用的fmt包,就是Go语言中的标准库。

import "fmt"

func main() {
    fmt.Println("hello")
}

10. 错误处理

Go 语言中的错误处理使用error类型来实现,error类型是一个接口类型, 只要实现了Error() string方法的类型都可以作为error类型使用。

type error interface {
    Error() string
}

在Go语言中,我们可以通过errors.New函数来创建一个error类型的值

err := errors.New("hello")

由于Go支持多返回值,因此我们可以在函数中返回一个error类型的值

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

这样,我们就可以在调用函数时,判断函数是否返回了error类型的值 ,来判断函数是否执行成功

if v, err := div(1, 0); err != nil {
    fmt.Println(err)
} else {
    fmt.Println(v)
}

11. defer

defer常被用于处理资源释放的问题

func main() {
    f, err := os.Open("test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer f.Close()
    }

在执行中,多个defer语句的执行顺序是先进后出的

func main() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    // 3
    // 2
    // 1
}