关于go基础知识补充

一般情况下我们设置三个环境变量:GOROOT, GOPATH, GOBIN
GOPATH 有什么意义?
参考回答: 环境变量GOPATH 的值可以是一个目录的路径,也可以包含多个目录路径,每个目录都代表Go语言的一个工作区(workspace)。 这些工作区用于存放Go语言的源码文件以及安装(install)后的归档文件(archive file 也就是以.a为扩展名的文件) 和可以执行文件(executable file)

源码安装的结果

产生的归档文件会放在工作区的pkg子目录
产生的可执行文件会放在工作区的bin子目录

归档文件的相对目录与pkg目录之间还有一级目录,叫做平台相关目录,平台相关目录名称由:build(也称“构建”)的目标操作系统,下划线,和目标计算架构目录代号构成

理解构建和安装Go程序的过程

构建使用命令:go build
安装使用命令:go install

如果构建的是库源码文件,那么操作的结果文件只会存在于临时目录中,这里的构建的主要意义在于检查和验证
如果构建的是命令源码文件,那么操作的结果问加你会被搬运到那个源码文件所在的目录中

安装操作会先执行构建,然后还会进行链接操作,并且把结果文件搬运到指定目录
如果安装的是库源码文件,那么结果文件会被搬运到它所在的工作区的pkg目录下的某个子目录中
如果安装的是命令源码文件,那么结果文件会被搬运到它所在的工作区的bin目录中,或者环境变量gobin 所指的目录中

关于go build

在执行go build 命令的时候,默认不会编译目标代码所依赖的那些代码包,如果被依赖的代码包的归档文件不存在,或者源码文件有了变化,那么它还是会被编译

go build -a : 不仅目标代码总是会被编译,它依赖的代码包也总是会被编译,即使依赖的是标准库中的代码包也是如此

go build -i: 不仅会编译依赖的代码包,还会安装他们的归档文件

go build -v: 可以看到go build命令编译的代码包的名称,与-a 标记配合非常有用

命令go get会自动从github 下载目标代码,并且把他们安装到环境变量GOPATH 包含的第一个工作区的相应目录中。如果存在环境变量BOGIN, 那么仅包含命令的源码文件代码包会被安装到GOBIN指向的那个目录。
go get 常用参数:
-u: 下载并安装代码包,不论工作区中是否已经存在
-d: 只下载代码包,不安装代码包
-fix: 下载代码包后先运行一个用于根据当前Go版本修正代码的工具,然后在安装代码包

 

关于错误处理

怎样判断一个错误值具体代表是哪一类错误?

1、对于类型在已知范围内的一系列错误值,一般使用类型断言表达式或类型switch语句来判断

2、对于已有相应变量且类型相同的一系列错误值,一般直接使用判断等操作来判断

3、对于没有相应变量类型未知的一系列错误值,智能使用其错误信息的字符串表示形式来做判断

 

从panic被引发到程序终止运行的大致过程?

某个函数中的某行代码有意或无意地引发了一个panic,这时,初始的panic详情会被建立起来,并且该程序的控制权会立即从代码转移到调用其所属函数的那行代码上

也就是调用栈中的上一级。

这意味着,此行代码所属函数的执行随即终止。 接着又会转移至再上一级的调用代码处,控制权如此一级一级的沿着调用栈的反方向传播到顶端,也就是我们编写的最外层函数那里

这里的最外层函数指的是go函数, 对于主goroutine来说主goroutine来说就是main函数,但是控制权也不会停留在那里,而是被Go语言运行时系统回收

 随后程序崩溃并终止运行,承载程序这次运行的进程也会随之死亡并消失。与此同时,在这个控制权传播的过程中,panic详情也会被逐渐的积累和完善,并会在程序终止之前被打印出来

 

panic 可能是我们在无意间引发的,这类panic 是真正的、在我们意料之外的程序异常,不过除此之外,我们还可以有意的引发panic

Go语言 内减函数panic 是专门用于引发panic的。panic 函数可以让我们在程序运行期间报告异常

这里需要注意的是如果一个panic 发生时,我们不是家任何保护措施,那么导致的直接后果就是程序崩溃,这是非常致命的

通过下面一个简单代码来更好的理解panic发生的问题:

package main

import (
    "fmt"
)


func caller1() {
    fmt.Println("Enter function caller1.")
    caller2()
    fmt.Println("Exit function caller1")
}

func caller2() {
    fmt.Println("Enter function caller2.")
    s1 := []int{0,1,2,3,4}
    e5 := s1[5]
    _ = e5
    fmt.Println("Exit function caller2")
}

func main() {
    fmt.Println("Enter function main.")
    caller1()
    fmt.Println("Exit function main.")
}

 

怎么样施加应对panic的保护措施,从而避免程序崩溃?

Go 语言的内建函数recover专用于恢复panic,或者说平息运行时恐慌, recover 函数无需任何参数,并且会返回一个空接口类型的值

如果用法正确,这个值实际就是即将恢复的panic包含的值,并且,如果这个panic 是因为我们调用panic函数引发的,那么该值同时也会是我们此次调用panic函数时,传入的参数值副本

 

关于recover的错误用法:

package main

import (
    "fmt"
    "errors"
)

func main() {
    fmt.Println("Enter function main.")

    panic(errors.New("something wrong"))
    p := recover()
    fmt.Printf("panic:%s\n",p)
    fmt.Println("Exit function main")
}

上面这段代码其实还是会报错的,想想上面说的,一旦panic发生了, 控制权就会迅速沿着调用栈的反方向传播,所以在panic函数之后的代码根本没有机会执行

看看下面正确的使用方法

recover的正确用法

package main


import (
    "fmt"
    "errors"
)

func main() {
    fmt.Println("Enter function main.")
    defer func() {
        fmt.Println("Enter defer function")
        if p:= recover(); p!= nil {
            fmt.Printf("panic :%s\n", p)
        }
        fmt.Println("Exit defer function")
    }()

    panic(errors.New("something wrong"))
    fmt.Println("Exit function main.")
}

 

defer 就是被用来延迟执行代码的。这个延迟会延迟到该语句所在的函数即将执行结束的那一刻,无论结执行的原因是什么

一个defer语句总是由一个defer关键字和一个调用表达式组成

但是需要注意一些限制: 有些表达式不能出现在这里,包括:针对Go语言内建函数的调用表达式,以及针对unsafe包中函数的调用表达式

 

如果一个函数中有多条defer语句,那么那几个defer函数函数的调用顺序是怎样的?

在同一个函数中,defer函数调用的执行顺序与它们分别所属的defer语句的出现顺序(执行顺序)完全相反,也就是:

当一个函数即将结束执行的时候,其中卸载最下边的defer函数调用会最先执行,其次是卸载它上面的,与它距离最近的那个defer函数调用,一次类推,最上面的defer函数调用会最后一个执行

通过下面这个例子能更好理解这个顺序:

package main

import (
    "fmt"
)

func main() {
    defer fmt.Println("first defer")
    for i:=0;i<3;i++ {
        defer fmt.Printf("defer in for [%d]\n",i)
    }
    defer fmt.Println("last defer")
}

 

posted @ 2018-10-23 10:39  fan-tastic  阅读(167)  评论(0)    收藏  举报