Go语法基础(上)| 青训营笔记

86 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

简介

主要介绍一些go语言的关键字和内置函数使用

1.make

make(Type, len, cap)

make主要负责内存分配,有三个主要参数Type、len和cap

  • Type:数据类型,必要参数,Type 的值只能是 slice、 map、 channel 这三种数据类型。
  • len:数据类型实际占用的内存空间长度,map、 channel 是可选参数,slice 是必要参数。
  • cap:为数据类型提前预留的内存空间长度,可选参数。所谓的提前预留是当前为数据类型申请内存空间的时候,提前申请好额外的内存空间,这样可以避免二次分配内存带来的开销,大大提高程序的性能。
package main
import "fmt"
func main() {
	arr := make([]int, 0)
	n := 20
	for i := 0; i < n; i++ {
		arr = append(arr, 1)
		fmt.Printf("len=%d,cap=%d\n", len(arr), cap(arr))
	}
}

make() 使用的是一种动态数组算法,一开始先向操作系统申请一小块内存,这个就是 cap,等 cap 被 len 占用满以后就需要扩容,扩容就是动态数组再去向操作系统申请当前长度的两倍的内存,然后将旧数据复制到新内存空间中。

package main
import (
   "fmt"
)
func main() {
   slice2 := make([]float32, 3, 5)       // [0 0 0] 长度为3容量为5的切片
   fmt.Println(len(slice2), cap(slice2)) // 3 5

   slice2 = append(slice2, 1, 2, 3, 4)   // [0, 0, 0, 1, 2, 3, 4]
   fmt.Println(len(slice2), cap(slice2)) // 7 12
}

对于上述案例,应该slice2扩容之后的内存容量应为10,实际结果却为12。

2.defer

Java 等语言中有 try...catch 机制,在 try 中捕获各种类型的异常,在 catch 中定义异常处理的行为。Go 语言也提供了类似的机制 defer 和 recover

defer语句被用于预定对一个函数的调用。可以把这类被defer语句调用的函数称为延迟函数

defer作用:

  • 释放占用的资源 defer coon.Close() //延迟连接关闭,资源回收,防止出现panic无法执行
  • 捕捉处理异常
  • 输出日志

recover使当前的程序从运行时panic的状态中恢复并重新获得流程控制权只会在defer修饰的函数中使用且只可以在panic时触发

package main

import (
   "fmt"
)

func get1(index int) (ret int) {
   defer func() {
      if r := recover(); r != nil {
         fmt.Println("Some error happened!", r) //r为错误信息
         ret = -1                               //如果没有指明,会直接返回0
      }
   }()
   arr := [3]int{2, 3, 4}
   return arr[index]
}

func get2(index int) (ret int) {
   func() {
      if r := recover(); r != nil {
         fmt.Println("Some error happened!", r)
         ret = -1
      }
   }()
   arr := [3]int{2, 3, 4}
   return arr[index]
}

func main() {
   fmt.Println(get1(5))    //Some error happened! runtime error: index out of range [5] with length 3
                           //-1
   fmt.Println(get2(5))    //panic: runtime error: index out of range [5] with length 3
   fmt.Println("finished") //不会执行
}

  • 在 get 函数中,使用 defer 定义了异常处理的函数,在协程退出前,会执行完 defer 挂载的任务。因此如果触发了panic,控制权就交给了 defer。
  • defer的处理逻辑中,使用recover,使程序恢复正常,并且将返回值设置为 -1,在这里也可以不处理返回值,如果不处理返回值,返回值将被置为默认值 0

3.struct

结构体类似于其他语言中的 class,可以在结构体中定义多个字段,为结构体实现方法,实例化等

package main

import (
   "fmt"
)

type Student struct {
   name string
   age  int
}

func (stu *Student) hello(person string) string {  //此时hello为stu实例的方法
   return fmt.Sprintf("hello %s, I am %s", person, stu.name)
}

func main() {
   stu := &Student{
      name: "Tom",
   }
   msg := stu.hello("Jack")
   fmt.Println(msg) // hello Jack, I am Tom
}

4.interface

接口定义了一组方法的集合,接口不能被实例化,一个类型可以实现多个接口

package main

import (
   "fmt"
)

type Person interface {
   getName() string
}

type Student struct {
   name string
   age  int
}

func (stu *Student) getName() string { //Student的成员方法
   return stu.name
}

func main() {
   var p Person = &Student{  //实例化 Student后,强制类型转换为接口类型 Person
      name: "Tom",
      age:  18,
   }

   fmt.Println(p.getName()) // Tom
}