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

105 阅读3分钟

Go 的优点

1 高并发支持

Go语言并行设计的牛逼之处就在于 goroutinechannel,基于这俩实现简单的并发编程

  • goroutine 协程
    • 一个进程可以拥有多个线程,一个线程可以拥有多个协程
    • 协程由程序控制(用户态),而不是由操作系统控制
    • 轻量级,没有内核切换的开销大,上下文切换很快

关于协程的底层原理和设计可以看看 GMP

  • Channel 管道
    • 一种协程间的异步通信方式
      • goroutine Agoroutine B 可以通过 Channel 完成通信,遵循先进先出
    • Don’t communicate by sharing memory, share memory by communicating

2 丰富标准库

标准库功能
fmt格式化操作
net网络库:Socket、HTTP、RPC...
html网页
math数学
strings字符串
time时间
......

ps:没有有序map

3 完整工具链

内置了很多性能调优、测试的工具,比如CPU分析器pprof、Uber开源的用于生成火焰图的go-torch、流量拷贝工具goreplay等

4 快速编译

  • 跨平台
    • Go程序是通过自己的 runtime 库实现与操作内核系统交互。
  • 静态链接
    • 在编译时会将依赖库一起编译进二进制文件
  • 快速编译
    • 使用了import的引用管理方式
    • 没有模板的编译负担

5 垃圾回收

垃圾回收(GC)是在后台运行一个守护线程,它的作用是在监控各个对象的状态,识别并且丢弃不再使用的对象来释放和重用资源

关于GO GC的底层原理和设计可以看看 三色标记法

基础语法

1 Hello World

package main

import (
  "fmt"
)

func main() {
  fmt.Println("Hello World!")
}

2 var

声明:var name type,var 是关键字,name 是变量名,type 是变量类型

简短声明:name := val,name 是变量名,val 是表达式

// 声明一个
var a int
// 声明且初始化
var a int = 100

// 声明多个
var a, b int    
// 声明多个不同类型
var (
    a int
    b string
    c []float32
    d func() bool
    e struct {
        x int
    }
)
// 简短声明
a := 1

a, b := 6.0, "666"

// 静态变量
const a = 100   // 无须设置类型

匿名变量:不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。

a, _ = Get2num()    // 这个下划线就是匿名变量

3 if else

必须大括号

if a:=1; a == 1 {
    a := 1
} else if a == 0 {
    a := 2
} else {
    a := 3
}

4 for

for {
    // 死循环
}

for i := 1; i <= 10; i++{

}

for i <= 10 {
    // 循环 10 次
}

5 switch

  • case 只要执行了就不会再选择了
  • 条件可以随便写,也就是无需选择某一变量
a := "6"
switch a {
case "1":
  fmt.Println("1")
case "2":
  fmt.Println("2")
default:
  fmt.Print("6")
}
switch {    // 比 if else 嵌套更清晰
case getuid() > 6:
  fmt.Println("1")
case getuid() < 6:
  fmt.Println("2")
default:
  fmt.Print("6")
}

6 array & slice

数组 array 的下标从 0 开始

var a [5][5]int
a[1][1] = 666
fmt.Println(a)

切片 slice

  • 声明
// 直接定义一个不定长数组
var arr []int   
// 使用 make 定义一个切片
var slice []int = make([]int, 816) // 第三个参数为提前预留的大小
// 简化写法
slice := make([]int, 8)
  • 初始化
arr := []int{1, 2, 3}

a := 1
b := 2
  • 截取(用截取做删除操作)
// 截取 [a,b) 中的内容
brr := arr[a:b]
// 不写, 就是默认从第一个元素到最后一个元素
  • 获取长度
len(arr)  // 实际使用长度
cap(arr)  // 加上预留的长度
  • 增加元素
arr = append(arr, 1, 2, 3)  // 可以添加多个元素

7 map

  • 声明
cnt := make(map[string]int, 6) // map[key]val, 预留空间6, 可以不写

cnt := map[string]int { // 创建多个
  "123": 666,
  "555": 777,
  "777": 888,
}
  • 获取键值对
v := cnt["123"]     // 获取 val
k, v := cnt["123"]  // 获取 key val

8 range

用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素

for _, v := range cnt { // 善用匿名变量
  fmt.Println(v)
}

9 结构体函数

// 定义结构体
type T struct {
  a int
  b int
  c string
}

// 结构体 T 的一个函数
func (u T) ADD() int {
  return u.a + u.b
}

// 初始化 与 使用
x := T{1, 2, "666"}
var y T
fmt.Println(x.ADD(), y)

10 字符串处理

import (
  "strings"
)
  • 是否含有子串
strings.Contains(s, "substr")
  • 子串出现的次数
strings.Count(s, "substr")
  • 串是否以 pre 开始
strings.HasPrefix(s, "pre")
  • 串是否以 suf 结束
strings.HasSuffix(s, "suf")
  • 还有一些,后面写吧

11 JSON处理

  • 结构体 -> json

    1. u 一个带内容结构体
    u := &T{
      1,
      []int{1, 2, 3},
      true,
    }
    
    1. 结构体(u) -> json(uJson)
    uJson, err := json.Marshal(u) // 转换
    if err != nil {
        fmt.Println("错误")
    }
    
    1. 输出
    fmt.Println(string(uJson)) // 输出需要 string()
    
  • json -> 结构体

    1. nu 一个空的结构体
    nu := &T{}
    
    1. json(uJson) -> 结构体(nu)
    err2 := json.Unmarshal(uJson, nu)
    if err2 != nil {
      fmt.Println("错误")
    }
    
    1. 输出
    fmt.Println(nu)
    

12 时间处理

13 错误处理

  • Go 直接提供一个错误类型 error
// 一个函数
func DIV(a int, b int) (int, error) {
  if b == 0 {
    return -1, errors.New("g")
  }
  return a / b, errors.New("")
}

// 使用
a, msg := DIV(10, 2)

if msg == errors.New("") {
  fmt.Println(a, msg)
} else {
  fmt.Println(a, msg)
} 

14 进程信息