这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记,主要介绍了go语言的基础语法。
go语言
1.数组
- 数组的声明
a :=[3]int{1,23,3}
b:=[...]int{7,6,4,45,2,3,5,}
var c=new([5]int)
c[4]=3
- 驻足的遍历
for i,v :=range(zoom){
fmt.Print(i,v)
}
- 切片
切片是数组的一部分,是对原来数组的复用
a :=[3]int{1,23,3}
c1:=a[1:]
c1=append(c1,5)//可以通过这种方式对切片进行扩容,原数组不扩容,理解复用的意思
c1[0]=4
c1//[4,3,5] 扩容后与原数组无关 ,每次扩容为原来的2倍
a //[1,23,3]
a := [3]int{0, 1, 2}
cl := a[:] //[0,1,2]
c := a[2:]
cl = append(cl, 5)
copy(cl, c)
fmt.Print(cl)//[2,1,2,5] 把后面的拷到前面的位置
copy(cl[1:2],c)//可以指定复制的位置,如果不指定,则从前面开始覆盖
//一般的创建方式 4为length,6为cap,可以不指定cap
aaa:=make([]int,4,6)//这种方式出来的切片有默认值[0,0,0,0]
var aa []int //这种方式出来的切片为空
2.通道
1.通道的使用
- 无缓存通道
func main() {
c := make(chan int) //创建无缓存的通道
var writec chan<- int = c
var readc <-chan int = c
go Write(writec)
Read(readc)
}
func Write(writec chan<- int) {
for i := 0; i < 10; i++ {
writec <- 1
}
}
func Read(readc <-chan int) {
for i := 0; i < 10; i++ {
fmt.Println("我在取数据", <-readc)
}
}
- 有缓存通道
func main() {
var c = make(chan int, 1)
c <- 1
fmt.Println(<-c)
}
3.结构体
接口可以接收所有实现它的类,所有对interface的调用,本质是对实现类的调用
1.实现接口时类的方法为指针类型
type Person interface {
Eat()
Run()
}
type Student struct {
Name string
}
type Teacher struct {
Name string
Uid int64
}
func (t *Teacher) Eat() {
fmt.Println("老师会吃----")
}
func (t *Teacher) Run() {
fmt.Println("老师会跑----")
}
func main() {
var c Person
//所有对interface的调用,本质是(&t).Run()
//go自动处理了指针的问题,但interface变量赋值不会自动处理
t := &Teacher{"张三", 1}
c = t
c.Run()
}
2.实现接口时类的方法为非指针类型
type Person interface {
Eat()
Run()
}
type Student struct {
Name string
}
type Teacher struct {
Name string
Uid int64
}
func (t Teacher) Eat() {
fmt.Println("老师会吃----")
}
func (t Teacher) Run() {
fmt.Println("老师会跑----")
}
func main() {
var c Person
t := Teacher{"张三", 1}
c = t
c.Run()
}
3.泛型和多态
var P Person
type Person interface {
Eat()
Run()
}
type Student struct {
Name string
}
type Teacher struct {
Name string
Uid int64
}
func (t Teacher) Eat() {
fmt.Println("老师会吃----")
}
func (t Teacher) Run() {
fmt.Println("老师会跑----")
}
func (s Student) Eat() {
fmt.Println("老师会吃----")
}
func (s Student) Run() {
fmt.Println("老师会跑----")
}
func test(p Person) {
P = p
}
func main() {
t := Teacher{"张三", 1}
test(t)
P.Eat()
}
4.struct类型名的获取
func main() {
i := 1
fmt.Println("i type name", reflect.TypeOf(i).Name())
fmt.Println("i type kind", reflect.TypeOf(i).Kind())
u := User{Name: "poloxue"}
fmt.Println("u type name", reflect.TypeOf(u).Name())
fmt.Println("u type kind", reflect.TypeOf(u).Kind())
//if reflect.TypeOf(u).Kind() == reflect.Struct {
// fmt.Println("u kind is struct")
//}
//if reflect.TypeOf(u).Kind() == "User" {
// fmt.Println("u type is User")
//}
}
4.goroutine
WaitGroup使主程序在子程序执行完后关闭,优雅的控制结束时间
func main() {
wg := sync.WaitGroup{}
wg.Add(10)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(i)
wg.Done()
}
}()
fmt.Println("我结束啦")
wg.Wait()
}
5.断言
把空接口断言为一个结构体,隐含的意思是每个结构体都实现了空接口。
其实就是将空接口强制类型转换为某个对象的实例
type User struct {
Name string
Age int
}
type Student struct {
Class string
User
}
func (u User) SayName(name string) {
fmt.Println("我是" + name)
}
func check(v interface{}) {
//v.(User).sayName(v.(User).Name)
switch v.(type) {
case User:
fmt.Println("我是User")
fmt.Println(v.(User).Name)
case Student:
fmt.Println("我是Student")
fmt.Println(v.(Student).Class)
}
}
- 断言是使用方式
1.取值函数
func check(inte interface{}) {
t := reflect.TypeOf(inte) //main.User
v := reflect.ValueOf(inte) //{张三 10}
ty := t.Kind() //得到类型
if ty == reflect.Struct {
fmt.Println("我是struct")
}
if ty == reflect.String {
fmt.Println("我是String")
}
if ty == reflect.Int {
fmt.Println("我是Int")
}
//for i := 0; i < t.NumField(); i++ {
// fmt.Println(v.Field(i)) //用来获取值
//}
fmt.Println(t, v)
fmt.Println(v.FieldByName("Class"))
fmt.Println(v.FieldByIndex([]int{1, 0})) //传入一个数组,指示一个层级索引,
// 取第一位个元素的第零位的值
}
2.修改原始数据
func check(inte interface{}) {
//t := reflect.TypeOf(inte) //main.User
v := reflect.ValueOf(inte) //{张三 10}
e := v.Elem()
e.FieldByName("Class").SetString("四年二班")
fmt.Println(inte)
}
func main() {
u := User{"张三", 10}
s := Student{"三年二班", u}
//要修改原来的值,这一定传的是地址值
//&取地址 *取值
check(&s)
fmt.Println(s)
}
3.调用方法
//函数名大写,否则不能通过反射调用
func (u User) SayName(name string) {
fmt.Println("我是" + name)
}
func check(inte interface{}) {
//t := reflect.TypeOf(inte) //main.User
v := reflect.ValueOf(inte) //{张三 10}
m := v.Method(0)
m.Call([]reflect.Value{reflect.ValueOf("大奇妙")})
}
6.并发编程
1.锁(简单)
2.读写锁
读写互斥 ,读不会互斥
func Recure(lock *sync.RWMutex) {
lock.RLock()
fmt.Println("疯狂治疗")
lock.RUnlock()
}
func GuaSha(lock *sync.RWMutex) {
lock.Lock()
fmt.Println("疯狂刮痧")
lock.Unlock()
}
func main() {
lock := sync.RWMutex{}
for i := 0; i < 4; i++ {
go GuaSha(&lock)
}
for i := 0; i < 4; i++ {
go Recure(&lock)
}
for {
}
}
3.有且只执行一次
func main() {
o := sync.Once{}
for i := 0; i < 10; i++ {
o.Do(func() {
fmt.Println(i)
})
}
}
4.安全并发的map
1.并发存取
func main() {
wg := sync.WaitGroup{}
wg.Add(2)
m := &sync.Map{}
go func() {
for {
fmt.Println("我存进去了")
m.Store(1, 1)
}
wg.Done()
}()
go func() {
for {
fmt.Println(m.Load(1))
}
wg.Done()
}()
wg.Wait()
}
2.存后遍历
func main() {
m := sync.Map{}
m.Store(1, 1)
m.Store(1, 2)
m.Store(2, 1)
m.Range(func(key, value any) bool {
fmt.Println(key, value)
return true //如果返回false,则停止,只有得到true的返回值才能继续遍历
})
5.并发池
是个池子,可以从里面存取东西,没了的话,取出nil
func main() {
p := sync.Pool{}
p.Put(1)
p.Put(2)
p.Put(3)
p.Put(4)
p.Put(5)
for i := 0; i < 6; i++ {
fmt.Println(p.Get())
}
}
************运行结果***************
1
5
4
3
2
<nil>
6.Cond 信号量
判断 操作 通知
func Print1(wg *sync.WaitGroup) {
lock.Lock()
for {
if number != 1 {
co1.Wait() //会释放锁
} else {
break
}
}
for i := 0; i < 5; i++ {
fmt.Println("我来打印1")
}
number = 2
co2.Signal()
lock.Unlock()
wg.Done()
}
func Print5(wg *sync.WaitGroup) {
lock.Lock()
for {
if number != 2 {
co2.Wait() //会释放锁
} else {
break
}
}
for i := 0; i < 10; i++ {
fmt.Println("我来打印2")
}
number = 3
co3.Signal()
lock.Unlock()
wg.Done()
}
func Print10(wg *sync.WaitGroup) {
lock.Lock()
for {
if number != 3 {
co3.Wait() //会释放锁
} else {
break
}
}
for i := 0; i < 5; i++ {
fmt.Println("我来打印3")
}
number = 3
co1.Signal()
lock.Unlock()
wg.Done()
}
//第一个程序打印1打印五次之后,再打印2 10次,再打印3 5次
func main() {
wg := sync.WaitGroup{}
wg.Add(3)
go Print1(&wg)
go Print5(&wg)
go Print10(&wg)
wg.Wait()
}