Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

go学习第四篇(常用关键字)

2018-09-01
go

defer

用于资源释放,会在函数返回之前调用。如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用。

注意:defer表达式可能会在设置函数返回值之后,在返回到调用函数之前,修改返回值,使最终的函数返回值与你想象的不一致。

  • defer表达式中变量的值在defer表达式被定义时就已经明确

package main

import (
	"fmt"
)

func main() {
	i := 0
	defer fmt.Println(i)
	i++
	return
}


上面的这段代码,defer表达式中用到了i这个变量,i在初始化之后的值为0,接着程序执行到defer表达式这一行,表达式所用到的i的值就为0了,接着,表达式被放入list,等待在return的时候被调用。所以,后面尽管有一个i++语句,仍然不能改变表达式 fmt.Println(i)的结果。

所以,程序运行结束的时候,输出的结果是0而不是1。

  • defer表达式的调用顺序是按照先进后出的方式

panic&recover

  • panic 是用来表示非常严重的不可恢复的错误的。假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行。返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行,直到goroutine整个退出,并报告错误
package main

import (
	"fmt"
)

func writeLog() {
	fmt.Println("收尾工作")
}

func main() {

	fmt.Println("执行第一次")
	fmt.Println("执行第二次")
	defer writeLog()
	panic("阻止继续执行")
	defer writeLog()

	fmt.Println("执行第三次")

}


结果:

panic: 阻止继续执行

goroutine 1 [running]:
main.main()
	/Users/nivellefu/golearn-gotype/learn0901926.go/valueTest.go:16 +0xd9
执行第一次
执行第二次
收尾工作

  • 用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为,也可以可以获取通过panic传递的error。
package main

import (
	"fmt"
)

func main() {

	fmt.Println("执行第一次")
	fmt.Println("执行第二次")
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("---error----", err)
		}
	}()
	panic("阻止继续执行")
	//defer writeLog()

	fmt.Println("执行第三次")

}


结果:


执行第一次
执行第二次
---error---- 阻止继续执行
成功: 进程退出代码 0.


struct

结构:

var 结构变量 struct{
    字段1 字段1类型
    字段2 字段2类型
    ...
}


特征:

  • Go中的struct与C中的struct非常相似,并且Go没有class,代替了class的位置,但并没有代替class的功能
  • 使用type
  • 支持指向自身的指针类型成员,支持匿名结构,可作为成员或定义成员变量
  • 支持 == 和 != 不支持< 或则 >
  • 支持匿名字段,本质上是定义了以某个类型名为名称的字段
  • 可以使用匿名字段指针
  • 可以使用字面值对结构进行初始化

字面值初始化

package main

import (
	"fmt"
)

type Animal struct { // 定义了一种类型,这种类型可以作为其他类型的属性
	color string
	hands int
}

func main() {
	a := Animal{
		color: "red",
		hands: 2,
	}

	fmt.Println(a)

}


结构体传值是值传递


package main

import (
	"fmt"
)

type Animal struct { // 定义了一种类型,这种类型可以作为其他类型的属性
	color string
	hands int
}

func main() {
	a := Animal{
		color: "red",
		hands: 2,
	}

	fmt.Println(a)
	copyValue(a)
	fmt.Println(a)

}

func copyValue(animal Animal) {
	animal.hands = 3
	fmt.Println("copyValue", animal)
}



结果:

{red 2}
copyValue {red 3}
{red 2}

  • 匿名结构
package main

import (
	"fmt"
)

func main() {
	a := struct {
		color string
		hands int
	}{
		color: "green",
		hands: 8,
	}

	fmt.Println(a.hands)

}


结果:

8

  • 匿名字段
package main

import (
	"fmt"
)

type Animal struct {
	string //匿名字段,在进行字面值初始化的时候,必须严格按照字段声明的顺序
	int
}

func main() {

	a := Animal{"dog", 4}

	fmt.Println(a)

}


{dog 4}
成功: 进程退出代码 0.

reflection

特征:

  • 反射使用TypeOf和ValueOf函数从接口中获取目标对象信息
  • 反射会将匿名字段作为独立字段
  • 利用反射修改对象状态,前提是interface.data是settable 也就是pointer-interface
  • 通过反射可以动态调用反射
package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) Hello() {
	fmt.Println("Hello world!")
}
func Info(o interface{}) {
	t := reflect.TypeOf(o)         //反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息
	fmt.Println("Type:", t.Name()) //调用t.Name方法来获取这个类型的名称

	v := reflect.ValueOf(o) //打印出所包含的字段
	fmt.Println("Fields:")
	for i := 0; i < t.NumField(); i++ { //通过索引来取得它的所有字段,这里通过t.NumField来获取它多拥有的字段数量,同时来决定循环的次数
		f := t.Field(i)               //通过这个i作为它的索引,从0开始来取得它的字段
		val := v.Field(i).Interface() //通过interface方法来取出这个字段所对应的值
		fmt.Printf("%6s:%v =%v\n", f.Name, f.Type, val)
	}
	for i := 0; i < t.NumMethod(); i++ { //这里同样通过t.NumMethod来获取它拥有的方法的数量,来决定循环的次数
		m := t.Method(i)
		fmt.Printf("%6s:%v\n", m.Name, m.Type)

	}
}
func main() {
	u := User{1, "Jack", 23}
	Info(u)
}



结果:

Type: User
Fields:
    Id:int =1
  Name:string =Jack
   Age:int =23
 Hello:func(main.User)
成功: 进程退出代码 0.



评论