20240207学习golang_非系统化学习

golang输出helloWorld

package main
import "fmt"
func main() {
    fmt.Println("hello world")
}
关于变量与常量

golang变量声明

a := "abcdefg"
fmt.Printf("a = %s, type of a = %T\n", a, a)
fmt.Println(a)

b := 1.23
fmt.Printf("type of b = %T\n", b)
fmt.Println(b)

golang常量

const pi float64 = 3.1415
fmt.Println(pi)
关于函数

golang函数初学

func main() {
    s := fplus(2, 3)
    fmt.Println(s)
}

func fplus(a int, b int)(c int){
    c = a + b
    return
}

glonag函数返回两个结果

func main() {
    a, b := plusone(5, 6)
    fmt.Println(a)
    fmt.Println(b)
}

func plusone(a int, b int)(int, int){
    return a + 1, b + 1
}
导包与init函数

图片说明 import导包与init函数
file
这也就是说如果文件里有导包,会先初始化导入的包里的 常量,变量, 执行init函数
注意:init函数的执行会早于main函数

关于go mod注意
如果开启并使用了go mod 在导入自己写的包的时候
import module名/包名 这个包名其实是是文件夹名 同时也是文件里声明的包名

package main
import (
    "fmt"
    //study是go mod init的时候输入的module名
    //lib1  lib2 其实是文件夹名  也是文件里的package名
    "study/lib1"
    "study/lib2"
)

func main() {
    fmt.Println("abc")
    //函数名 首字母大写才能外部调用
    lib1.Test()
    lib2.Test()
}

import匿名导入 其实就是给包起了个别名 _

package main
import (
    "fmt"
    //如果只想调用包里的init方法 需要匿名导入
    _ "study/lib1"
    _ "study/lib2"
)

func main() {
    fmt.Println("abc")
}

import导入的包也可以起个别名

package main

import (
    "fmt"
    //这里为导入的包起个别名,下面可以用别名来调用 
    abc "study/lib1"
    def "study/lib2"
)

func main() {

    fmt.Println("abc")
    abc.Test()
    def.Test()
}
关于指针

golang指针初学

func main() {
    a := 10
    //&a 代表 取地址 引用
    changeValue(&a)
    fmt.Println(a)
}

//*int 代表指针类型 引用类型
func changeValue(p *int){
    //*p 代表取内存地址里的值 也叫解引用
    *p = 100
}
关于defer

defer关健字 像个析构函数
注意:defer比return还要晚执行

func main() {
    //defer关健字 最后执行的语句 像是PHP里的析构函数
    defer fmt.Println("defer  ..")

    fmt.Println("a")
    fmt.Println("b")
}
关于数组与slice

固定长度数组使用(不好用)
注意:不建议使用 建议用slice切片(动态数组)
由于长度固定,传给函数当参数都不好传
数组作为函数参数的话是值传递

func main() {
    //定义一个固定长度的数组
    var myArray1 [10]int

    //定义并初始化一个固定长度的数组 后面5个元素由于没有给数据 是0
    myArray2 := [10]int{1, 2, 3, 4, 5}

    //遍历
    for i := 0; i < len(myArray1); i++ {
        fmt.Println(myArray1[i])
    }

    //另一种方法遍历
    for index, value := range myArray2 {
        fmt.Println("index=", index)
        fmt.Println("value=", value)
    }
}

slice 切片 动态数组
注意 切片作为参数传递给函数的话 是地址传递 也叫引用传递
用法跟数组是一样的,只是高级一点

func main() {
    //定义一个切片
    mySlice := []int{1,2,3}
    //遍历
    for _, value := range mySlice{
        fmt.Println(value)
    }
}

另一种定义切片的方式

    //声明一个切片 有3个容量
    mySlice1 := make([]int, 3)
    fmt.Println(mySlice1)   

切片的长度和容量

    //切片的长度 和 容量
    numbers := make([]int, 3, 10)
    fmt.Printf("len = %d, cap = %d\n", len(numbers),  cap(numbers))

向slice里用append添加元素

func main() {
    //切片的长度 和 容量 当前容量为5
    numbers := make([]int, 3, 5)
    //向slice里添加元素
    numbers = append(numbers, 4)
    numbers = append(numbers, 6)
    //当添加的元素超过容量了,会自动扩容
    numbers = append(numbers, 7)
    fmt.Printf("len = %d, cap = %d\n", len(numbers), cap(numbers))
}

切片的截取 用下标截取
注意:截取前与截取后的是同一个切片 改变任何一个 另一个也会变
如果不想这样 可以用copy函数 拷贝一份再截取

func main() {
    //切片的截取
    mySlice := make([]int, 2)
    mySlice[0] = 2
    mySlice[1] = 3
    mySlice = append(mySlice, 4)
    //截取    s := arr[startIndex:endIndex]
    newSlice := mySlice[1:2]
    fmt.Println(newSlice)
}
关于map

注意:map作为函数参数的话 是 引用传递
map简单使用

func main() {
    //map第一种声明方式
    //map声明
    var myMap1 map[string]string
    //make容量
    myMap1 = make(map[string]string, 3)
    myMap1["one"] = "php"
    myMap1["two"] = "java"
    myMap1["three"] = "python"
    //容量用完了会自动扩容
    myMap1["four"] = "rust"

    fmt.Println(myMap1)

    //map第二种声明
    test2 := make(map[string]string)
    test2["one"] = "php"
    test2["two"] = "golang"
    test2["three"] = "java"
    fmt.Println(test2) //map[one:php two:golang three:java]

    //map第三种声明
    test3 := map[string]string{
        "one" : "php",
        "two" : "golang",
        "three" : "java",
    }
    fmt.Println(test3) //map[one:php two:golang three:java]

    //复杂一点的map value又是个map
    language := make(map[string]map[string]string)
    language["php"] = make(map[string]string, 2)
    language["php"]["id"] = "1"
    language["php"]["desc"] = "php是世界上最美的语言"
    language["golang"] = make(map[string]string, 2)
    language["golang"]["id"] = "2"
    language["golang"]["desc"] = "golang抗并发非常good"

    fmt.Println(language)

}

map的增删改查

func main() {
    language := make(map[string]map[string]string)
    language["php"] = make(map[string]string, 2)
    language["php"]["id"] = "1"
    language["php"]["desc"] = "php是世界上最美的语言"
    language["golang"] = make(map[string]string, 2)
    language["golang"]["id"] = "2"
    language["golang"]["desc"] = "golang抗并发非常good"

    //fmt.Println(language) //map[php:map[id:1 desc:php是世界上最美的语言] golang:map[id:2 desc:golang抗并发非常good]]

    //增删改查
    val, key := language["php"]  //查找是否有php这个子元素
    if key {
        fmt.Printf("%v\n", val)
    } else {
        fmt.Printf("no\n");
    }

    //language["php"]["id"] = "3" //修改了php子元素的id值
    //language["php"]["nickname"] = "啪啪啪" //增加php元素里的nickname值
    //delete(language, "php")  //删除了php子元素
    fmt.Println(language)

}

map增删改查

func main() {
    //声明一个map
    city := make(map[string]string)

    //增加元素
    city["usa"] = "newyork"
    city["japan"] = "tokay"
    city["uk"] = "london"
    //fmt.Println(city)

    //删除 使用健来删除
    delete(city, "uk")
    //fmt.Println(city)

    //修改
    city["japan"] = "abc"
    //fmt.Println(city)

    //查询 遍历
    for key, value :=  range city{
        fmt.Println("key = ", key)
        fmt.Println("value = ", value)
    }
}
面向对象
struct 结构体

struct相当于面向对象语言里的类
注意:用struct构造出来的数据(new出来的对象) 作为函数参数的话是值传递
注意:类名首字母大写表示其它包也可以访问 属性名首字母大写表示可对外访问 方法名首字母大写表示其它包可访问
结构体初体验

// 定义一个结构体
type Book struct {
    title string
    auth  string
}

func main() {
    //用结构体构造一个对象
    var book1 Book
    book1.title = "好好学golang"
    book1.auth = "张三"
    fmt.Println(book1)
}

给结构体加方法

//结构体定义  相当于类
type Hero struct{
    //两个属性
    Name string
    Age  int
}

//类的方法定义
//这里最好加个*号 否则传进来的对象是拷贝
func (this *Hero) Show(){
    fmt.Println("name = ", this.Name)
    fmt.Println("age = ", this.Age)
}

//类的方法定义
//这里最好加个*号 否则传进来的对象是拷贝
func (this *Hero) SetName(name string){
    this.Name = name
}

func main() {
    //用结构体构造一个对象
    hero := Hero{Name: "张三", Age: 20}
    //改一下名字
    hero.SetName("李四")
    //查看一下改成功了
    hero.Show()
}

golang里面的类继承(struct)

//父类
type Human struct{
    Name string
}

//父类的方法
func (this *Human) Eat(){
    fmt.Println("父类的eat方法")
}

//父类的方法
func (this *Human) Walk(){
    fmt.Println("父类的Walk方法")
}

//====================================
//子类
type Superman struct{
    //继承父类,直接把父类写在这里就行
    Human

    //再写个子类的属性
    Age string
}

//子类的方法
func (this *Superman) Run(){
    fmt.Println("子类的Run方法")
}

//也可以重写父类的方法
func (this *Superman) Walk(){
    fmt.Println("子类重写了父类的Walk方法")
}

func main() {
    //用子类来new一个对象
    var superman1 Superman
    superman1.Name = "张三"
    superman1.Age = "100"

    //调用一下父类的方法
    superman1.Eat()

    //调用一下子类的方法
    superman1.Run()

    //调用一下 子类重写的父类方法
    superman1.Walk()

    //再调用一下属性
    fmt.Println(superman1.Name)
    fmt.Println(superman1.Age)

}

interface 空指针 万能类型 类型断言
空指针可以接收任务类型的参数
类型断言能够判断传递过来数据的类型

func myfunc(arg interface{}){
    //类型断言
    _, ok := arg.(string)
    if ok {
        fmt.Println("是string类型")
    }

    _, ok = arg.(int)
    if ok {
        fmt.Println("是int类型")
    }
}

func main() {
    myfunc(11)
}
reflect反射

反射获取变量的类型和值

func myReflect(arg interface{}){
    fmt.Println(reflect.TypeOf(arg))
    fmt.Println(reflect.ValueOf(arg))
} 

func main() {
    myReflect(11)
}

利用reflect获取类的标签

// 带标签的结构体
type Book struct {
    Name string `info:"书名"`
    auth string `info:"作者"`
}

// 写个函数来查询对象的标签
func findTag(str interface{}) {
    //获取对象的元素
    t := reflect.TypeOf(str).Elem()
    for i := 0; i < t.NumField(); i++ {
        infoTag := t.Field(i).Tag.Get("info")
        fmt.Println(infoTag)
    }

}

func main() {
    book1 := Book{}
    findTag(&book1)
}
json

结构体与json互转 会用到结构体标签

import (
    "encoding/json"
    "fmt"
)

// 定义个结构体
type Movie struct {
    Name  string `json:"name"`
    Year  int    `json:"year"`
    Price int    `json:"price"`
}

func main() {
    //定义一个结构体
    movie1 := Movie{"功夫", 2005, 15}
    //结构体转json
    jsonStr, err := json.Marshal(movie1)
    if err != nil {
        fmt.Println("转换json失败")
    }

    fmt.Printf("转换json成功 str=%s\n", jsonStr)

    //再转回来
    movie2 := Movie{}
    err = json.Unmarshal(jsonStr, &movie2)
    if err != nil {
        fmt.Println("转换失败")
    }
    fmt.Println(movie2)
}
goroutine 协程 并发

协程初体验

import (
    "fmt"
    "time"
)

func task() {
    i := 0
    for {
        fmt.Println("协程里执行的=", i)
        i++
        time.Sleep(1 * time.Second)
    }
}

func main() {
    //开一个go程
    go task()

    //主线程
    i := 0
    for{
        fmt.Println("主线程里执行的=", i)
        i ++
        time.Sleep(1 * time.Second)
    }
}
channel 管道 用于协程间通讯

注意:管理分为有缓存和没有缓存两种
没有缓存的管道会有阻塞效果,不管是对于接收还是发送
管道初体验

func main() {
    //定义一个channel
    c := make(chan int)

    go func()  {
        fmt.Println("子协程开始运行...")
        defer fmt.Println("子协程运行结束")

        //子协程结束后发送个信号
        c <- 1

    }()

    //主协程等待接收信号
    num := <- c
    fmt.Println(num)
    fmt.Println("主协程结束....")
}

图解无缓存channel
file
图解有缓存channel
file

带缓存的channel简单应用
带缓存的channel当缓存满了 也会有阻塞效果

import (
    "fmt"
    "time"
)

func main() {
    //带缓存的channel 容量cap为3  长度len为0
    c := make(chan int, 3)
    //fmt.Printf("cap = %d, len = %d", cap(c), len(c))

    go func()  {
        defer fmt.Println("子go程结束")
        for i := 0; i < 30; i++ {
            //向管道中写入数据
            c <- i
            fmt.Println("子go程向管道中写入了数据", i)
        }
    }()

    time.Sleep(1 * time.Second)

    for i := 0; i < 30; i++ {
        num := <- c
        fmt.Println("主go程从管道中读取了数据", num)
    }

    fmt.Println("主go程结束")
}

用close关闭管道的简单应用

func main() {
    //没有缓存的channel
    c := make(chan int)

    go func () {
        for i := 0; i < 5; i++ {
            //向管道写入数据
            c <- i
        }

        //关闭管道
        close(c)
    }()

    for{
        //也可以这样从管道中读取数据
        data, ok := <- c
        if ok {
            fmt.Println("从管道中读取的数据是", data)
        } else {
            fmt.Println("管道关闭")
            break
        }
    }

    fmt.Println("主go程结束")
}

也可以用range来 遍历 不断操作通道

func main() {
    //没有缓存的channel
    c := make(chan int)

    go func () {
        for i := 0; i < 5; i++ {
            //向管道写入数据
            c <- i
        }

        //关闭管道
        close(c)
    }()

    //也可以用range来 不断操作channel
    for data := range c{
        fmt.Println(data)
    }

    fmt.Println("主go程结束")
}
select操作通道

demo

    select {
    case <- chan1:
        // 如果chan1成功读到数据,则进行该case处理语句
    case chan2 <- 1:
        // 如果成功向chan2写入数据,则进行该case处理语句
    default:
        // 如果上面都没有成功,则进入default处理流程
    }

select可以同时监控多路channel的状态

package main

import (
    "fmt"
)

func fibonacci(c, quit chan int) {
    x, y := 1, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)

    go func() {
        for i := 0; i < 6; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()

    fibonacci(c, quit)
}