GO语言基本入手|青训营笔记

76 阅读5分钟

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

这篇笔记主要用来回顾GO语言相关的基础语法 包括 变量简单声明 异常处理 通道 结构体方法定义部分

变量声明基本使用

从简单的Hello Go开始吧!

package main

import "fmt"

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

package声明所在的包 main包中的main方法为程序的主入口 fmt:标准格式化输入输出

可以使用flag来解析传入程序的参数

使用:=进行快速的声明赋值

func main() {
   block := "function"
   {
      block := "inner"
      fmt.Printf("The block is %s.\n", block)
   }
   fmt.Printf("The block is %s.\n", block)
}

注意块级作用域的存在

go语言中的内置数据结构

map-> Key-Value键值对的形式存在

func main() {
   container := map[int]string{0: "zero", 1: "one", 2: "two"}
   fmt.Printf("The element is %q.\n", container[1])
}

也可以使用make创建对应的数据结构

重点:Slice GO中切片的含义 其中length capacity Value中有不同的含义

切片可以共享内存地址 修改另一个引用的切片可能会更改其他的源数据(需要注意)

func main() {
   // 示例1。
   s1 := make([]int, 5)
   fmt.Printf("The length of s1: %d\n", len(s1))
   fmt.Printf("The capacity of s1: %d\n", cap(s1))
   fmt.Printf("The value of s1: %d\n", s1)
   s2 := make([]int, 5, 8)
   fmt.Printf("The length of s2: %d\n", len(s2))
   fmt.Printf("The capacity of s2: %d\n", cap(s2))
   fmt.Printf("The value of s2: %d\n", s2)
   fmt.Println()

   // 示例2。
   s3 := []int{1, 2, 3, 4, 5, 6, 7, 8}
   s4 := s3[3:6]
   fmt.Printf("The length of s4: %d\n", len(s4))
   fmt.Printf("The capacity of s4: %d\n", cap(s4))
   fmt.Printf("The value of s4: %d\n", s4)
   fmt.Println()

   // 示例3。
   s5 := s4[:cap(s4)]
   fmt.Printf("The length of s5: %d\n", len(s5))
   fmt.Printf("The capacity of s5: %d\n", cap(s5))
   fmt.Printf("The value of s5: %d\n", s5)
}

使用append进行追加 返回的是新的地址 所以需要重新赋值

s7 := make([]int, 1024)
fmt.Printf("The capacity of s7: %d\n", cap(s7))
s7e1 := append(s7, make([]int, 200)...)

异常处理

在Go语言中,错误或异常分为error和panic两种,error一般是程序员可预知的,会进行合适的处理,例如检测输入是否合法等。而panic是程序员无法预知的异常,例如空指针或数组越界等。

Go 提供了两种创建error的方法,分别是: errors.New fmt.Errorf

一般在没有recover的情况下panic会导致程序崩溃,panic,defer和recover经常同时出现,用于异常处理.

func main() {
   var m map[string]int

   key := "two"
   elem, ok := m["two"]
   fmt.Printf("The element paired with key %q in nil map: %d (%v)\n",
      key, elem, ok)

   fmt.Printf("The length of nil map: %d\n",
      len(m))

   fmt.Printf("Delete the key-element pair by key %q...\n",
      key)
   delete(m, key)

   elem = 2
   fmt.Println("Add a key-element pair to a nil map...")
   m["two"] = elem // 这里会引发panic。
}

CSP模型

Go中有独特的Channel类型 可以很方便的添加接受数据

使用 <- 即可 很方便的将数据从管道中取出 赋值

func main() {
   ch1 := make(chan int, 3)
   ch1 <- 2
   ch1 <- 1
   ch1 <- 3
   elem1 := <-ch1
   fmt.Printf("The first element received from channel ch1: %v\n",
      elem1)
}

chan构造的时候后一个参数指定通道内可以容纳的数据个数

当chan空或者满的时候 执行对应的取 和 添加 会发生阻塞

func main() {
   // 示例1。
   ch1 := make(chan int, 1)
   ch1 <- 1
   //ch1 <- 2 // 通道已满,因此这里会造成阻塞。

   // 示例2。
   ch2 := make(chan int, 1)
   //elem, ok := <-ch2 // 通道已空,因此这里会造成阻塞。
   //_, _ = elem, ok
   ch2 <- 1

   // 示例3。
   var ch3 chan int
   //ch3 <- 1 // 通道的值为nil,因此这里会造成永久的阻塞!
   //<-ch3 // 通道的值为nil,因此这里会造成永久的阻塞!
   _ = ch3
}

可以通过指定收发类型来创建通道

流程控制Select可以很方便的监听可用通道 执行对应的分支

func example1() {
   // 准备好几个通道。
   intChannels := [3]chan int{
      make(chan int, 1),
      make(chan int, 1),
      make(chan int, 1),
   }
   // 随机选择一个通道,并向它发送元素值。
   index := rand.Intn(3)
   fmt.Printf("The index: %d\n", index)
   intChannels[index] <- index
   // 哪一个通道中有可取的元素值,哪个对应的分支就会被执行。
   select {
   case <-intChannels[0]:
      fmt.Println("The first candidate case is selected.")
   case <-intChannels[1]:
      fmt.Println("The second candidate case is selected.")
   case elem := <-intChannels[2]:
      fmt.Printf("The third candidate case is selected, the element is %d.\n", elem)
   default:
      fmt.Println("No candidate case is selected!")
   }
}

声明方法

使用type 方法名 func(参数列表) 返回值 的方式定义方法

type operate func(x, y int) int

// 方案1。
func calculate(x int, y int, op operate) (int, error) {
   if op == nil {
      return 0, errors.New("invalid operation")
   }
   return op(x, y), nil
}

// 方案2。
type calculateFunc func(x int, y int) (int, error)

func genCalculator(op operate) calculateFunc {
   return func(x int, y int) (int, error) {
      if op == nil {
         return 0, errors.New("invalid operation")
      }
      return op(x, y), nil
   }
}

定义结构体

使用type 类型名 struct{字段}定义 结构体

支持结构体之间的组合

当声明对象时 可以使用 对象名:=结构体名{对应字段} 的方式创建对象

package main

import "fmt"

// 示例1。
// AnimalCategory 代表动物分类学中的基本分类法。
type AnimalCategory struct {
   kingdom string // 界。
   phylum  string // 门。
   class   string // 纲。
   order   string // 目。
   family  string // 科。
   genus   string // 属。
   species string // 种。
}

func (ac AnimalCategory) String() string {
   return fmt.Sprintf("%s%s%s%s%s%s%s",
      ac.kingdom, ac.phylum, ac.class, ac.order,
      ac.family, ac.genus, ac.species)
}

// 示例2。
type Animal struct {
   scientificName string // 学名。
   AnimalCategory        // 动物基本分类。
}

// 该方法会"屏蔽"掉嵌入字段中的同名方法。
func (a Animal) String() string {
   return fmt.Sprintf("%s (category: %s)",
      a.scientificName, a.AnimalCategory)
}

// 示例3。
type Cat struct {
   name string
   Animal
}

// 该方法会"屏蔽"掉嵌入字段中的同名方法。
func (cat Cat) String() string {
   return fmt.Sprintf("%s (category: %s, name: %q)",
      cat.scientificName, cat.Animal.AnimalCategory, cat.name)
}

func main() {
   // 示例1。
   category := AnimalCategory{species: "cat"}
   fmt.Printf("The animal category: %s\n", category)

   // 示例2。
   animal := Animal{
      scientificName: "American Shorthair",
      AnimalCategory: category,
   }
   fmt.Printf("The animal: %s\n", animal)

   // 示例3。
   cat := Cat{
      name:   "little pig",
      Animal: animal,
   }
   fmt.Printf("The cat: %s\n", cat)
}