Go语言简短入门 | 青训营笔记

128 阅读7分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第 1 篇笔记

  • Go 程序组织结构
  • 安装
  • 开发环境
  • 基础语法

Go 程序组织结构

  1. 包声明
    golang 以包 package 为单位来组织代码,每个以 .go 结尾的 go 语言源代码开头一般都为包声明,包声明指明当前源代码位于哪个包中
    包声明是必须的
package main
  1. 注释
    package main 上可以写注释来指明该包的作用,go 中有两种注释形式:单行注释 // 和多行注释 /*[注释的内容]*/
    这两种注释都是可以的,不过在 go 中一般推荐使用单行注释,即 //
// main 包是 main 函数的所在,可用于生成可执行程序
package main
# src 存放源代码
src/ 
    # 这里的 main 只是文件夹的名称,习惯上包名和文件夹名一致 (当然可以不同)
    main/ 
        # 位于包中的文件,需要加上 package [package_name] 来指定包名,
        # 这里 main.go 和 ui.go 如果都位于 main 包中,则应该在其中指明
        # package main,假如 ui.go 位于别的包中,如 ui 包,那其应该声明
        # package ui
        # 虽然 go 依靠包名组织源代码,但一个良好项目结构是必不可少的,所以还是
        # 保持尽可能包名和文件名相同吧
        - main.go
        - ui.go
    util/
        - util.go
  1. main 函数

golang 中,函数通过 func 来声明。与主流编程语言不同,go 的类型声明是后置的,而且形参的类型声明如果相同,则只用声明一个即可,形参间用逗号隔开
以下是一个完整的 go 源代码:main.go

// main 包可生成可执行文件
package main

// import 将导入 main 包外的包,当导入多个包的时候,可以用括号包裹
// 注意!go 不允许导入包但是不使用!
import (
    "fmt"
)

// main 是程序开始的地方
// go 中多用单行注释,多行注释并不常用
// 在各种包、函数、类型之上的注释一般以其对应的名称开头,注释内容和注释符号之间以一个空格分隔
// 这种写法不是强制性的,只是方便使用 godoc 工具生成文档
func main() {    // 这个左括号必须写在这里,不能另起一行,这有失灵活,
                 // 但却在一定程度增强了代码的可读性
    
    // go 中不使用 `;` 作为语句的终结,原因是 go 会自动在特定语句后自动添加分号
    var a, b int   // 自动初始化为 0
    a = 1
    b = 2
    
    // go 的 `简短声明` ,可自动执行类型推断,这里 sum 和 add 的返回值类型相同,为 int
    sum := add(a, b)
    
    // Println 是包 fmt 中的函数,调用前需要先写包名,所以在命名包的时候尽量做到言简意赅,
    // 简写包名要注意
    // 在这一点上千万别学 Java !
    fmt.Println(sum)
    
    // main 不需要 return 0
    
}

// add 返回两个数相加的结果,由于 a, b 类型相同,所以前面的 a 可以省略类型声明
// 在返回值这里还有一部分知识点,但对于快速上手有点无关紧要所以我不写了 [摆]
// 像 c 和 python,你需要在函数调用前声明,但 go 无所谓,只要能找到就行
func add(a, b int) int {
    return a + b
}

安装

go.dev/
studygolang.com/dl
goproxy.cn/

开发环境

  • VScode 免费,丰富的插件
  • Goland 30 days 试用
  • SpaceVim 免费,上手难度较高

各有各的优点√ (Goland完胜x)

基础语法

  1. 变量

变量分为值类型和引用类型 --- go 是按值传递
注意!go 不允许声明了变量而不用

    var a = "Hello, world!"    // go 中字符串是不可变的
    var b, c int = 1, 2
    var f = true
    var double float64    // go 中没有 double,转而是 float32 和 float64
    number := 0xFF    // 简短声明

  1. 常量
    const NumOfPeople uint = 2000    // 常量一般使用驼峰命名法,uint 是无符号 int
                                     // int 的位数与平台有关,同时,go 中也提供
                                     // int32, uint32, int64, uint64
    const str = "Hello, world!"

  1. 条件
    if a > b {    // if 的条件只能为 `true` 或 `false`
        // ...
    }

    if a <= b && a >= c {
        // ...
    } else if c == 0 || c == 1 {
        // ...
    } else {
        // ...
    }
    
    if a >= b {
        // ...
    } else {
        // ...
    }
    
    // go 中的 switch,相比 C,case 后默认 break
    switch flag {
    case true:
        // ...
    case false:
        // ...
        fallthrough    // `fallthrough` 相当于使 break 失效,
                       // 执行下一个 case/default
    default:
        // ...
    }
    


  1. 循环
    // 在 Go 语言中只有一个循环关键字 `for`
    
    for i := 0; i < len(str); i++ {    // len() 是 go 的内置函数,这里是返回
                                       // 字符串 str 的长度
        cnt++    // go 只允许这一种自加方式,同时这种方式为一条语句,即不能这样写
                 // array[idx++] 这是不允许的; ++idx 也是不存在的
        // ...
    }
    
    // while 类型
    for canRun(x) {
        // ...
    }
    
    // for...range 返回一个可迭代对象的索引和值
    for idx, value := range BigArray {
        // ...
    }
    
    // 因为 go 不允许声明了变量不用,所以可以用下划线 `_` 来将不需要的值丢弃,例如
    for _, value := range array {
        // ...
    }

  1. 函数
    // 函数名大写表示包外可见
    func SayHi() string {
        return "Hi~"
    }
    
    // 包外无法调用
    func add(a int, b int) int {
        return a + b
    }
    
    // 简写
    func add(a, b int) int {
        return a + b
    }
    
    func OpenFile(path string) ([]byte, err) {    // go 中可以有多个返回值 
                                                  // []byte是切片,引用类型
        // ...
    }    
    
    function0 := func() {
        // ...
    }

  1. 数组 / 切片
    /* 数组 */
    var arr [4]int    // 一个容量为 4 的 int 型数组,声明即自动初始化为 0
                      // bool 类型为 false,引用类型为 nil
    var arr0 [4]int{1, 2, 3, 4}
    var arr1 [...]int{1, 2, 3, 4, 5, 6, 7}    // 不指定容量,编译器自动推断
    
    /* 切片 --- 引用类型 */
    var slice0 []int   // slice0 默认为 nil
    var slice1 = make([]int, 5, 10)   // 内置 make 函数,
                                      // slice1 容量(cap) = 10, 大小(len) = 5
                                      // 前 5 位被初始化为 0
    slice0 = append(slice0, 10)   // 向slice0 添加元素,cap 不足则自动扩容
    
    fruit := []string{"apple", "banana", "peach"}    // string 类型的切片
                                                     // 其 len == cap 为 3

  1. 指针

go 提供指针,但是不允许指针的加减


    var ptr *int
    
    var a, b = 1, 2
    swap(&a, &b)   // 通过 & 取地址
    
    func swap(a, b *int) {
        *a, *b = *b, *a    // 通过 * 解引用,没错!这就是简单的变量交换!
    }


  1. map
    m := make(map[string]int)    // 可以指定容量,不写为 0,这里的容量
                                 // 指的是通过调用 len 得到的,而不是 cap
                                 // cap(m) 会报错
    m["one"] = 1
    m["two"] = 2
    
    var m0 = map[string]uint{"one": 1, "two": 2, "three": 3}
    
    // 删除键值对
    delete(m["one"])
    
    // 判断键是否存在
    // 如果直接访问一个 map 不存在的键不会报错!
    if _, ok := m0["four"]; !ok {    // 如果不存在
        log.Panic("invaild key!")    // 调用 log 包中的函数
    }
   


  1. 结构体
// 结构体名称大写,包外可见,字段同理,与 C++/Java 相比省去了访问修饰符
type User struct {
    Name          string
    Age           uint
    Addr          string
    accountPasswd string    // 包外不可访问
    qqAccount     uint64
    
    // ...
}

var user  = User{}    // 字段将被初始化
var user0 = &User{}   // 这里是获取指向空结构的指针
var user1 = User{
    Name: "张三",
    Age:  32,
    Addr: "地球",    // 最后这个逗号不能省,除非最后一个字段后是右大括号
}

user2 := &User{Name: "李四", Age: 45}


// 结构体方法,相当于对象的方法,虽然 go 没有显式地提供面向对象,但它也提供了
// 不错的抽象,你可以自己来模拟面向对象编程的模式,同样大小写限定了访问的权限
func (u *User) QQAccount() uint64 {    // 这里相当于 getter 不过按照习惯
                                       // go 不使用类似 "GetXXX" 的方式命名
    return u.qqAccount    // 这里的 `u`,相当于 this 指针,但在 go 中
                          // 它有另一个名字,叫 receiver,这里是指针接收者
}

// 同样,还有值接收者
func (u User) QQAccount0() uint64 {
    return u.qqAccount
}

// 这两者有一定的差异,它们之间的异同要彻底说明白不容易,我们可以按照官方推荐的
// 做法 "如果你搞不明白,用指针接收者"

原地址:go.dev/doc/faq#met…

简书翻译版:www.jianshu.com/p/da264d9b1…