只要实现了接口中的方法的变量,都属于该接口的类型。
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()函数执行顺序Go语言包会从main包开始检查其导入的所有包,每个包中又可能导入了其他的包。Go编译器由此构建出一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包的代码。
在运行时,被最后导入的包会最先初始化并调用其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的调度是随机的。
可增长的栈