//指针 结构体 方法
//1 指针:(地址)
-什么类型的指针,就是在类型前加 *
-取什么变量的地址,就是在变量前加 &
-通过指针得到值(解引用),在变量前加*
-当参数传递,修改会影响原来的值
-一般情况下,引用不需要再去地址了,取地址一般取值类型的地址
//2 结构体
-面向对象当中的类,只有属性 ,没有方法(一系列属性的集合)
type Person struct{ 一堆属性}
-使用结构体:a:=Person{} 按位置,按关键字传参
-取值: . 取值
-结构体是值类型,默认是是属性的默认值
-匿名结构体
-匿名字段
-结构体嵌套(变量提升)
-结构体比较(如果结构体中有引用类型,不支持比较)
//3 方法(特殊的函数)
-func关键字和函数名之间有个特殊类型接收器
-func (a 结构体名字)方法名(参数)(返回值){}
-绑定给结构体,结构体通过 . 方式来调用
-值接收器和指针接收器(值接收器不会修改原来的,指针接收器会修改原来的)
-不论值还是指针,都可以直接调用方法(值接收器类型,指针接收器类型)
-匿名字段的方法(方法提升,类似于面向对象中的继承,多继承)
-非结构体上绑定方法(自己定义的属性)
//go的指针,结构体,匿名结构体,匿名字段,信道,缓冲信道,方法,函数,接口,goroutine,闭包函数,匿名函数
1 接口
//一系列方法的集合,规范了子类的行为,
-python和go都属于鸭子类型,非侵入式接口
-java:侵入式接口
package main
import "fmt"
//1 定义一个接口,(规范行为,没有具体实现)
type DuckInterface interface {
Run()
Speak()
}
//定义一个TDuck结构体
type TDuck struct {
name string
age int
wife string
}
//TDuck实现接口(实现接口中的所有方法)
func (t TDuck)Run() {
fmt.Println("我是唐老鸭,我的名字是",t.name,"我说人话")
}
func (t TDuck)Speak() {
fmt.Println("我是唐老鸭,我的名字是",t.name,"我学人走路")
}
//定义一个RDuck结构体
type RDuck struct {
name string
age int
}
//RDuck实现接口(实现接口中的所有方法)
func (t RDuck)Run() {
fmt.Println("我是普通肉鸭,我的名字是",t.name,"我阿嘎嘎叫")
}
func (t RDuck)Speak() {
fmt.Println("我是普通肉鸭,我的名字是",t.name,"我歪歪扭扭走路")
}
// 5 有名空接口
type EmptyInterface interface {
}
func main() {
//2 实例化得到TDuck和RDuck两个对象
//t:=TDuck{"鸡哥",88,"凤姐"}
r:=RDuck{"普通鸭子",2}
//t.Run()
//t.Speak()
//r.Run()
//r.Speak()
//接口也是一个类型(可以定义一个变量是接口类型)
// 同一类事物的多种形态
//var d DuckInterface
//d=t
////d=r
//d.Speak()
//d.Run()
//想再去t的属性,name,age,wife
//test(t)
//test(r)
// 5 空接口
//-只要一个类型,实现接口所有的方法,就叫实现该接口
//-如果一个接口是空的,一个方法都没有,所有类型都实现了空接口
//-任意类型,都可以赋值给空接口类型
//var a EmptyInterface
//a=1
//a="xx"
//a=[3]int{1,2,3}
//6 接口类型空值(nil)
//var a EmptyInterface
//fmt.Println(a)
//7 匿名空接口
// interface{}
test(1)
test(r)
test(1.2)
}
////3 类型断言
//func test(d DuckInterface) {
// //d.Run()
// //d.Speak()
// //var t TDuck
// //t=d.(TDuck)
// t:=d.(TDuck) //断言d是TDuck类型,如果正确,就会把d转成t
// fmt.Println(t.name)
// fmt.Println(t.wife)
// t.Speak()
// t.Run()
//}
//4 类型选择
//func test(d DuckInterface) {
// switch a:=d.(type) {
// case TDuck:
// fmt.Println(a.wife)
// fmt.Println("你是TDuck类型")
// case RDuck:
// fmt.Println(a.name)
// fmt.Println("你是RDuck类型")
// default:
// fmt.Println("不知道是什么类型")
// }
//}
//7 匿名空接口
//func test(i interface{}) {
func test(i EmptyInterface) {
fmt.Println(i)
}
package main
// 1 实现多个接口
//type DuckInterface1 interface {
// Run()
// Speak()
//}
//
//type HumanInterface interface {
// Drive()
//}
//
////定义一个TDuck结构体
//type TDuck1 struct {
// name string
// age int
// wife string
//}
////TDuck实现接口(实现接口中的所有方法)
//func (t TDuck1)Run() {
// fmt.Println("我是唐老鸭,我的名字是",t.name,"我说人话")
//}
//func (t TDuck1)Speak() {
// fmt.Println("我是唐老鸭,我的名字是",t.name,"我学人走路")
//}
////实现HumanInterface接口
//func (t TDuck1)Drive() {
// fmt.Println("我是唐老鸭,我开车")
//}
// 2 接口嵌套
type DuckInterface1 interface {
Run()
Speak()
}
type HumanInterface interface {
DuckInterface1
//相当于
//Run()
//Speak()
Drive()
}
func main() {
//t:=TDuck1{"鸡哥",18,"凤姐"}
//var d DuckInterface1
//var h HumanInterface
//d=t
//d.Run()
//d.Speak()
//h=t
//h.Drive()
}
2 并发和并行
//并发:假如在他晨跑时,鞋带突然松了。于是他停下来,系一下鞋带,接下来继续跑
//并行:如这个人在慢跑时,还在用他的 iPod 听着音乐
3 go协程
//go协程--》goroutine,并不是真正的协程(线程+协程,语言层面处理了,不需要开发者去关注)
package main
import (
"fmt"
"time"
)
func hello() {
fmt.Println("go go go!")
}
func main() {
fmt.Println("主函数开始")
go hello() //通过go关键字,开启goroutine,并发执行
go hello()
go hello()
go hello()
fmt.Println("主函数结束")
time.Sleep(2*time.Second)
}
4 信道
//goroutine直接通信
//go语言不推崇共享变量方法做通信,而推崇管道通信channel(信道)
// 信道(管道)
package main
import (
"fmt"
"time"
)
func main() {
//1 信道也是一个变量(需要指明运输的类型)
//var a chan int //定义一个int类型信道
//2 信道的0值,nil类型,它是一个引用
//fmt.Println(a)
//3 信道初始化
//var a chan int=make(chan int)
//fmt.Println(a)
//4 信道的放值,和取值
//var a chan int=make(chan int)
//a<-1 //放值,把1放到信道中
////var b int=<-a //取值
//<-a //取值
//5 默认情况下,信道的放值和取值都是阻塞的(一次一个都放不进去)
fmt.Println("开始")
var a chan bool=make(chan bool)
//信道是引用类型
go hello1(a)
<-a
time.Sleep(2*time.Second)
}
func hello1(a chan bool) {
fmt.Println("go go go")
a<-true
fmt.Println("xxx")
}
//信道案例
package main
/*
输入 453 计算每一位的平方和和每一位的立方和的和
squares = (4 * 4) + (5 * 5) + (3 * 3)
cubes = (4 * 4 * 4) + (5 * 5 * 5) + (3 * 3 * 3)
output = squares + cubes
*/
import (
"fmt"
)
func calcSquares(number int, squareop chan int) {
sum := 0
for number != 0 {
digit := number % 10 //% 取余数 453对10取余数--》3--》5--》4
sum += digit * digit
number /= 10 // /除以 453除以10----》45--》5--》0
}
squareop <- sum
}
func calcCubes(number int, cubeop chan int) {
sum := 0
for number != 0 {
digit := number % 10
sum += digit * digit * digit
number /= 10
}
cubeop <- sum
}
func main() {
number := 4535
sqrch := make(chan int)
cubech := make(chan int)
go calcSquares(number, sqrch)
go calcCubes(number, cubech)
squares, cubes := <-sqrch, <-cubech
//squares:= <-sqrch
//cubes := <-cubech
fmt.Println("Final output", squares + cubes)
}
package main
func main() {
//1 死锁
//var a chan int=make(chan int)
//a<-1 //一直阻塞在这,报死锁错误
//<-a //一直阻塞在这,报死锁错误
//2 单向信道(只写或者只读)
//sendch := make(chan int) //定义一个可写可读信道
//go sendData(sendch)
//fmt.Println(<-sendch) //只能往里写值,取值报错
//3 信道的关闭 close
//sendch := make(chan int)
//close(sendch)
}
func sendData(sendch chan<- int) { //转成只写信道
sendch <- 10
//<-sendch //只要读就报错
}
package main
import (
"fmt"
)
func producer(chnl chan int) {
for i := 0; i < 10; i++ {
chnl <- i
}
close(chnl)
}
func main() {
ch := make(chan int)
go producer(ch)
for v := range ch { //如果信道没关闭,一直取值,直到没有值,会报死锁
fmt.Println("Received ",v)
}
}
5 缓冲信道
//有缓冲信道(信道可以放多个值)
package main
import (
"fmt"
"sync"
"time"
)
func main() {
//1 有缓冲信道的定义和死锁问题
//var a chan int =make(chan int,4) //长度为4的有缓冲信道
//a<-1
//a<-2
////a<-3
////a<-4
////管子满了
////a<-5 //报死锁错误
////推断出无缓冲信道 var a chan int =make(chan int,0)
//fmt.Println(<-a)
//fmt.Println(<-a)
////管子没有东西了,再取,报死锁
//fmt.Println(<-a)
//2 信道的容量和长度
//长度是目前管道中有几个值,容量是管道最多能容纳多少值
//var a chan int =make(chan int,4)
//fmt.Println(len(a))
//fmt.Println(cap(a))
//a<-1
//a<-2
//fmt.Println(len(a))
//fmt.Println(cap(a))
//3 小案例(通过信道实现goroutine的同步)
//var a chan int =make(chan int,3)
////b:=<-a
//go test3(a)
//
//fmt.Println(<-a)
//
//fmt.Println(<-a)
//4 通过waitgroup实现同步
no := 3
var wg sync.WaitGroup //值类型,没有初始化,有默认值
for i := 0; i < no; i++ {
wg.Add(1)
go process(i, &wg)
}
wg.Wait() //add了几次,必须有几个wg.Done()对应,否则一直等在这
fmt.Println("All go routines finished executing")
}
func process(i int, wg *sync.WaitGroup) {
fmt.Println("started Goroutine ", i)
time.Sleep(2 * time.Second)
fmt.Printf("Goroutine %d ended
", i)
wg.Done()
}
//func test3(a chan int) {
// a<-1
//
// time.Sleep(time.Second*2)
// fmt.Println("假设我在运算")
//
// a<-2
// close(a)
//
//}
6 异常处理
//异常处理
package main
import "fmt"
//func main() {
// f1()
// f2()
// f3()
//}
//func f1() {
// fmt.Println("f1")
//}
//
//func f2() {
// fmt.Println("f2")
// //如果这个地方出了异常
//}
//func f3() {
// fmt.Println("f3")
//}
//defer:延迟执行,即便程序出现严重错误,也会执行
//panic:主动抛出异常 raise
//recover:恢复程序,继续执行
//func main() {
// //defer fmt.Println("我最后才执行") //先注册,等函数执行完成后,逆序执行defer注册的代码
// //defer fmt.Println("ddddd")
// defer func() {
// fmt.Println("我最后才执行")
// }()
// defer func() {
// fmt.Println("我最后才执行")
// //出异常
// //这个代码执行不到了
// }()
//
// fmt.Println("111")
// fmt.Println("222")
// panic("我出错了") //主动抛出异常
// //var a []int =make([]int,2,3)
// //fmt.Println(a[9])
// fmt.Println("这句话还会执行吗 ?不会了")
//}
//在defer中恢复程序,继续执行
func main() {
f1()
f2()
f3()
}
func f1() {
fmt.Println("f1")
}
func f2() {
defer func() {
//recover() //恢复程序继续执行
if err:=recover();err!=nil{ //err 如果不为nil(空),表示出了异常
fmt.Println(err) //把异常信息打印出来
}
}()
fmt.Println("f2")
//如果这个地方出了异常
panic("我出错了")
fmt.Println("永远执行不到")
}
func f3() {
fmt.Println("f3")
}
// python中
//print('ssss')
//try:
// print('ssss')
// raise("xxxx")
// print('我永远不会执行')
//except Exception as e:
// print(e)
//finally:
// print('我永远会执行')
//go 中
//print('ssss')
//defer func() {
// if err:=recover();err!=nil{
// fmt.Println(err)
// }
// //finally 写在这
// print('我永远会执行')
//}()
//print('ssss')
//panic("xxxx")
//print('我永远不会执行')
补充
1 进程,线程,协程
# python中为什么让你开进程,由于cpython解释器,有GIL锁,同一时刻,只能有一个线程执行
# 开进程--》相当于开了多个线程,重新起了一个cpython解释器,运行代码