Go语言基础 - 基础语法 | 青训营笔记

72 阅读6分钟

Go语言基础 - 基础语法

这是我参与「第五届青训营」伴学笔记创作活动的第1天。青训营的第一次课程中讲解了有关Go语言的相关基础,下面我将就为什么学习Go语言、Go语言基本语法两个方面记录我对本次课程的理解。

为什么学习Go语言

相较于其他语言,Go语言拥有性能良好、标准库完善等等优势。结合课程与自身的实际情况,以下几点是我学习Go语言的主要动机。

  • 容易上手: 相比较于C/C++庞杂的语言体系(学习两年仍未入门),Go语言的学习曲线则十分平缓。在有其他语言背景的基础下,使用一周左右的时间就可以快速入门Go语言。
  • 自动内存管理: 在一个大型的C/C++项目中,用于内存管理所需要的编码以及调试时间成本都是非常显著的,处理各种段错误也是C++使用者的生活常态。Go语言则本身具备垃圾回收功能,这使得开发者能够更专注于业务流程的实现。
  • 语言生态良好: 作为由Google公司背书的语言,Go拥有比较完善的语言生态。在国内外云计算、微服务等领域内,Go语言具有很高的市场占用率,这使得Go具有比较好的实用价值以及学习途径。

Go语言基本语法

在本节中,我将结合课程及自身对Go的理解,讲一讲Go语言的基础语法。

  1. 导入导出: 每一个Go程序都是由包组成的,包的名字由package语句规定。程序从main包开始运行。
package main

import (
	"fmt"
        "math"
)

func main() {
	fmt.Println(math.pi))  // error
        fmt.Println(math.Pi))  // correct
}

包通过import语句导入(包名与导入路径的最后一个元素一致)。并且,如果一个变量或函数名以大写字母开头,则它就是被导出的,即可以被其他包访问。

  1. 变量及函数:
var i int = 1
var j float32 = 2.0
var k *int = &i
k := 3
c, python, java := true, false, "no!"
fmt.Println(i, j, k, c, python, java)

Go语言通过var语句声明一个变量或变量列表,与其他语言不同的是Go语言变量的类型名在变量之后被声明。同时,Go语言可以通过:=的方法在函数内部进行自动类型判断的变量声明。

Go语言支持boolstringint等整型、float32等浮点型、complex64等复数型基本类型。并且,Go语言支持指针类型。

func swap(x int, y int) (int, int) {
	return y, x
}

Go语言的函数声明以func开始,声明方式与其他语言类似,比较独特的是Go语言支持函数的多值返回。

  1. 循环分支语句: Go语言支持ifforswitch三种循环分支语句。
if 1 % 2 == 0 {
    fmt.Println("hello"))
} else {
    fmt.Println("world"))
}

Go语言中的if语句与C/C++语言类似,区别在于条件判断语句不需要加括号。

for {
...
}

for i < n {
...
}

for i = 0; i < n; i++ {
...
}

Go语言使用唯一的for循环语句,并支持死循环、C/C++类型for循环、类while形式三种循环方式。

switch 条件 {
    case 1:
        ...
    case 2:
        ...
    default:
        ...
}

Go语言支持switch分支结构,与C/C++不同的是,Go不用在每个case分支中加入break用于隔离分支。同时,Go的switch语句的条件语句可以为空,这样的switch会从上向下的执行每个case的判断并执行首个符合判断的分支,这样的语句相较于嵌套if else更加清晰

  1. 数组、切片与映射:
primes := [6]int{2, 3, 5, 7, 11, 13}
var s1 []int = primes[1:4]
var s2 []int
s2 = append(s2, 0)
}

Go语言通过[数组大小]数组类型声明数组,并支持{...}的列表初始化方式。具有局限性的是,数组仅支持固定长度的大小。切片则提供了动态大小的数组抽象,在实践中更常被用到。

Go语言通过[]切片类型声明切片,其通过[上界:下界]的方式来界定,可以通过a = b[lo:hi]使得切片a引用切片或数组b的第lo至hi-1元素。

m := make(map[string]int)
m["a"] = 1
m["b"] = 2
r, ok := m["notFound"]
delete(m, "one")

Go语言中支持map映射类型,map[键类型]值类型用于确定一个映射类型。

  • map通过make语句被初始化,并通过map[键] = 值的形式向其插入元素;
  • 通过值, 存在 := map[键]的形式的方式从map中查询键,其第二个返回值示意了map中是否存在该键;
  • 通过delete(map, 键)的形式从map中删除对应的键。
s := []int{1, 2, 3}
m := map[string]int{"a": 1, "b": 2}
for i, val := range s {
    ...
}
for k, v := range m {
    ...
}

Go支持range语句遍历数组、切片以及映射类型,对于数组及切片类型,range语句每次返回数组的索引, 元素值;对于映射类型,range语句每次返回映射的键, 值。值得注意的是,对于数组及切片类型,range语句将按顺序遍历其中元素,而对于映射类型,遍历的顺序则是随机的。

  1. 结构体、方法与接口:
type user struct {
    name     string
    password string
}
func (u user) checkPassword(password string) bool {
    return password == u.password
}
func (u *user) setPassword(password string) bool {
    u.password = password
}

Go语言通过type 结构体名 struct的方式声明一个结构体。并且,可以通过func (a 结构体名) 方法名的方式为结构体声明一个方法,方法通过结构体变量.方法名的方式被调用。

当使用func (a *结构体名) 方法名声明结构体方法时,可以通过该方法改变结构体中的变量。

type MyFloat float64
type Vertex struct {
    X, Y float64
}
func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}
func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
type Abser interface {
    Abs() float64
}
...
var a Abser
f := MyFloat(1.0)
v := Vertex{1,2}
a = f // Myfloat类型支持Abs方法,可以被赋值为Abser
a = v // Vertex类型支持Abs方法,可以被赋值为Abser

接口interface类型为Go语言支持的一种特殊类型,其中存放了一系列的方法。任何实现了接口中方法的结构体类型,均可被存放在对应的接口变量中。

为了方便理解,可以将接口类比为C++中的纯虚基类,重载了纯虚基类中所有方法的继承类即可被存放在纯虚基类的指针中。

在Go语言中,常用的一种接口形式为空接口interface{}。任何类型的变量均可被存放在空接口元素中,空接口可以被用来处理未知类型的值。可以声明函数参数为空接口,使得函数接收任意类型的参数;也可以在结构体中存放某个空接口字段,使得该字段可以存储任意类型的变量。

总结

除了本文的内容外,在青训营课程中还讲解了字符串处理函数、JSON处理函数、时间处理、进程处理函数等常用标准库。

本文主要回顾总结了课程及Go语言中的基本语法,在青训营课程中补充了接口、变量类型等常用基础语法。本文内容参考了Go 语言之旅 (go-zh.org),其由Go语言官方推出,可以作为一个较好的Go语言入门途径。