一、断言
* 只要实现了接口的全部方法就认为这个类型属于接口类型,如果编写一个接口,这个接口中没有任何方法,这时认为所有类型都实现了这个接口.所以Go语言中interface{}代表任意类型
* 如果interface{}作为方法参数就可以接收任意类型,但是在程序中有时需要知道这个参数到底是什么类型,这个时候就需要使用断言
* 断言使用时,使用interface{}变量点括号,括号中判断是否属于的类型
i interface{}
i.(Type)
* 断言的两大作用:
* 判断是否是指定类型
* 把interface{}转换为特定类型
* 断言可以有一个返回值,如果判断结果是指定类型返回变量值,如果不是指定类型则报错
func demo(i interface{}){
result:=i.(int)
fmt.Println(result)
}
func main() {
/*
参数是456时,程序运行正常,输出456
参数是false时报错:
panic: interface conversion: interface {} is bool, not int
*/
demo(456)
}
* 断言也可以有两个返回值,这时无论是否是指定类型都不报错.
* 第一个参数:
* 如果正确:返回值变量值
* 如果错误:返回判断类型的默认值
* 第二个参数:
* 返回值为bool类型,true表示正确,false表示错误
func demo(i interface{}) {
result, ok := i.(int)
fmt.Println(result, ok)
}
func main() {
/*
参数是456时,程序运行正常,输出456 true
参数是字符串"abc"时程序运行正常,输出0 false
*/
demo("abc")
}
二、错误
* 在程序执行过程中出现的不正常情况称为错误
* Go语言中使用builtin包下的error接口作为错误类型,官方源码定义如下
* 只包含了一个方法,方法返回值是string,表示错误信息
type error interface {
Error() string
}
* Go语言中错误都作为方法/函数的返回值,因为Go语言认为使用其他语言类似try...catch这种方式会影响到程序结构
* 在Go语言标准库的errors包中提供了error接口的实现结构体errorString,并重写了error接口的Error()方法.额外还提供了快速创建错误的函数
* 如果错误信息由很多变量(小块)组成,可以借助fmt.Errorf("verb",...)完成错误信息格式化,因为底层还是errors.New()
三、自定义错误
* 使用Go语言标准库创建错误,并返回
func demo(i, k int) (d int, e error) {
if k == 0 {
e = errors.New("初始不能为0")
d=0
return
}
d = i / k
return
}
func main() {
result,error:=demo(6,0)
fmt.Println(result,error)
}
* 如果错误信息由多个内容组成,可以使用下面实现方式
func demo(i, k int) (d int, e error) {
if k == 0 {
e = fmt.Errorf("%s%d和%d", "除数不能是0,两个参数分别是:", i, k)
d = 0
return
}
d = i / k
return
}
func main() {
result, error := demo(6, 0)
fmt.Println(result, error)
}
四、Go语言中错误处理方式
* 可以忽略错误信息,使用占位符
result, _ := demo(6, 0)
* 使用if处理错误,原则上每个错误都应该解决
func main() {
result, error := demo(6, 0)
if error != nil {
fmt.Println("发生错误", error)
return
}
fmt.Println("程序执行成功,结果为:", result)
}
五、defer使用
* Go语言中defer可以完成延迟功能,当前函数执行完成后执行defer功能
* defer最常用的就是关闭连接(数据库连接,文件等)可以打开连接后代码紧跟defer进行关闭,后面在执行其他功能
* 在很多语言中要求必须按照顺序执行,也就是必须把关闭代码写在最后,但是经常会忘记关闭导致内存溢出,而Golang中的defer很好的解决了这个问题.无论defer写到哪里都是最后执行
func main() {
fmt.Println("打开连接")
defer func(){
fmt.Println("关闭连接")
}()
fmt.Println("进行操作")
//输出:打开连接 进行操作 关闭连接
}
* 多重defer采用栈结构执行,先产生后执行
* 在很多代码结构中都可能出现产生多个对象,而程序希望这些对象倒序关闭,多个defer正好可以解决这个问题
func main() {
fmt.Println("打开连接A")
defer func(){
fmt.Println("关闭连接A")
}()
fmt.Println("打开连接B")
defer func(){
fmt.Println("关闭连接B")
}()
fmt.Println("进行操作")
//输出:打开连接A 打开连接B 进行操作 关闭连接B 关闭连接A
}
* defer与return同时存在时,要把return理解成两条执行结合(不是原子指令),一个指令是给返回值赋值,另一个指令返回跳出函数
* defer和return时整体执行顺序
* 先给返回值赋值
* 执行defer
* 返回跳出函数
* 没有定义返回值接收变量,执行defer时返回值已经赋值
func f() int{
i:=0
defer func(){
i=i+2
}()
return i
}
func main() {
fmt.Println(f())//输出:0
}
* 声明接收返回值变量,执行defer时修改了返回值内容.
* 由于return后面没有内容,就无法给返回值赋值,所以执行defer时返回值才有内容
func f() (i int){
defer func(){
i=i+2
}()
return
}
func main() {
fmt.Println(f())//输出:2
}
六、panic
* panic是builtin中函数
* panic有点类似于其他编程语言的throw,抛出异常.当执行到panic后终止剩余代码执行.并打印错误栈信息
* 注意panic不是立即停止程序(os.Exit(0)),defer还是执行的.
七、recover
* recover()表示恢复程序的panic(),让程序正常运行
* recover()是和panic(v)一样都是builtin中函数,可以接收panic的信息,恢复程序的正常运行
* recover()一般用在defer内部,如果没有panic信息返回nil,如果有panic,recover会把panic状态取消
* recover()只能恢复当前函数级或当前函数调用函数中的panic(),恢复后调用当前级别函数结束,但是调用此函数的函数可以继续执行.
* panic会一直向上传递,如果没有recover()则表示终止程序,但是碰见了recover(),recover()所在级别函数表示没有panic,panic就不会向上传递
八、os包结构介绍
* Go语言标准库中os包提供了不依赖平台的操作系统接口
* 设计为Unix风格的,而错误处理是go风格的,失败的调用会返回错误值而非错误码。通常错误值里包含更多信息
* os包及子包功能
-- os 包
--os/exec 包,负责执行外部命令.
--os/signal对输入信息的访问
--os/user 通过名称或ID 查询用户账户
* 在os/user中提供了User结构体,表示操作系统用户
* Uid 用户id
* Gid 所属组id
* Username 用户名
* Name 所属组名
* HomeDir 用户对应文件夹路径
* 在os/user中的Group表示用户所属组
* Gid 组的id
* Name 组的名称
* 整个os/user包中内容比较少,提供了两个错误类型和获取当前用户,查找用户
* 可以获取当前用户或查找用户后获取用户信息
// 获取当前登录用户
// u,_:=user.Current()
// Lookup()参数是用户名,按照用户名查找指定用户对象
// 注意:必须使用完整名称不可以只写zhang
u, _ := user.Lookup(`LAPTOP-M7D47U95\zhang`)
fmt.Println(u.Name)
fmt.Println(u.Gid)
fmt.Println(u.HomeDir)
fmt.Println(u.Uid)
fmt.Println(u.Username)