go语言基本语法 (3)

只要实现了接口中的方法的变量,都属于该接口的类型。

type speaker interface { speak() //方法签名,约束其他调用者需要实现的方法 } type cat struct {} type dog struct {} type person struct {} func (d dog) speak() { fmt.Println("dog") } func (c cat) speak() { fmt.Println("cat") } func main(){ var c1 cat var d1 dog eat(c1) eat(d1) //下面会报错,因为person这个结构体没有实现speak这个方法 //不能称之为speaker类型的变量,因此,就不能被当做参数传进去 //var p1 person //eat(p1) } 接口的实现

属于接口的类型,必须实现了接口中定义的所有方法。

接口保存分为两部分:

值的类型

值本身

![image-20200407221155084](/Users/liuhuan/Library/Application Support/typora-user-images/image-20200407221155084.png)

这样就实现了接口变量能够存储不同的值。

使用值接收者实现接口与使用指针接收者实现接口的区别?

使用值接收者实现接口,结构体类型和结构体指针类型的变量都能存。

使用指针接收者实现接口,只能存结构体指针类型的变量。

同一个结构体可以实现多个接口。

type mover interface{ move() } type eater interface{ eat() } type (c *cat) move(){ pass } type (c *cat) move(){ pass }

接口还可以嵌套。

空接口

没有必要取名字,通常以如下形式定义

interface{}

所有类型都实现了空接口,因此任意类型的变量都能保存到空接口中。

func main(){ m1 := map[string]interface{} }

使用空接口可以实现保存任意值的字典。

func main(){ var m1 map[string]interface{} m1["name"] = "xx" m1["age"] = 18 m1["hobbies"] = ["唱", "跳", "rap"] }

类型断言

方式一:

func assign(a interface{}){ switch t := a.(type){ case string: fmt.Println("是string") case int: fmt.Println("是int") case bool: fmt.Println("是bool") } }

方式二:

func assign(a interface{}){ str, ok := a.(string) if !ok { fmt.Println("猜错了") } else { fmt.Println("是string") } } 关于接口

只有当有两个或两个以上的具体类型,必须以相同方式来进行处理时才需要定义接口。不要为了接口而写接口,这样只会增加不必要的抽象,导致不必要的运行时损耗。

包 标识符

包中的标识符(变量名\函数名\结构体\接口等),如果首字母是小写,表示私有(只能在当前这个包中使用)。

首字母大写的标识符可以被外部的包调用。

go中的路径是以$GOPATH下的src开始的。

package main import ( "fmt" xx "code.golang.com/day05/10calc" //xx是别名 )

按照go语言的规范,包名不能以数字开头,目录名和包名最好保持一致。

go语言中,禁止循环导入包。

导入包不想使用包内的标识符,需要使用匿名导入。

每个包导入的时候会自动执行一个名为init()的函数。

每个包中只有一个init()

init()初始化函数 init()函数介绍

在Go语言程序执行时导入包语句会自动触发包内部init()函数的调用。需要注意的是: init()函数没有参数也没有返回值。 init()函数在程序运行时自动被调用执行,不能在代码中主动调用它。

包初始化执行的顺序如下图所示:

包中的init()执行时机

init()函数执行顺序

Go语言包会从main包开始检查其导入的所有包,每个包中又可能导入了其他的包。Go编译器由此构建出一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包的代码。

在运行时,被最后导入的包会最先初始化并调用其init()函数, 如下图示:

包之间的init()执行顺序

反射

反射给变量赋值一定要加上Elem()函数。

并行与并发

go语言的并发通过goroutine实现,goroutine类似于线程,属于用户态的线程,比操作系统的线程更轻量级。 goroutine是go语言运行时(runtime)调度完成的,而线程是由操作系统调度完成的。

go语言还提供channel在多个grouting间通信,goroutine和channel是go语言秉承的CSP并发模式的重要实现基础。

goroutine什么时候结束?

goroutine对应的函数结束了,goroutine就结束了。

main函数执行完了,由main函数创建的那些goroutine都结束了。

启动多个goroutine

在Go语言中实现并发就是这样简单,我们还可以启动多个goroutine。让我们再来一个例子: (这里使用了sync.WaitGroup来实现goroutine的同步)

var wg sync.WaitGroup func hello(i int) { defer wg.Done() // goroutine结束就登记-1 fmt.Println("Hello Goroutine!", i) } func main() { for i := 0; i < 10; i++ { wg.Add(1) // 启动一个goroutine就登记+1 go hello(i) } wg.Wait() // 等待所有登记的goroutine都结束 }

多次执行上面的代码,会发现每次打印的数字的顺序都不一致。这是因为10个goroutine是并发执行的,而goroutine的调度是随机的。

可增长的栈

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzxfxy.html