Go Lang Study Notes 2

Views: 1165
Wrote on Jan. 7, 2021, 5:54 p.m.

匿名函数和闭包

变量的作用域

1. 全局作用域
2. 函数作用域
3. 语句块作用域

defer

defer延迟执行函数

defer先注册后执行(在函数即将要结束但还没有结束的时候执行)

多用来做一些资源回收类的工作。

func testDefer() {
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println("函数马上要结束了...")
}
func main() {
    testDefer()
}

分金币

Method 1:

import (
    "fmt"
    "strings"
)

/*
50枚金币,分配给一下几个人:Matthew,Sarah,Augustus ,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth.
分配规则如下:
a.名字中包含e或者E:1枚金币
b.名字中包含i或者I:2枚金币
c.名字中包含o或者O:3枚金币
d.名字中包含u或者U:4枚金币
写一个程序,计算每个用户分到多少金币,以及最后剩余多少金币?
*/
var (
    coins        = 50
    users        = []string{"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth"}
    distribution = make(map[string]int, len(users))
)

// 根据规则分发金币,返回剩余的金币数量
func dispatchCoin() int {
    sum := 0
    // 1. 遍历名字的切片
    for _, name := range users {
        // 2. 根据规则分发金币
        if strings.Contains(name, "e") || strings.Contains(name, "E") {
            distribution[name] = distribution[name] + 1
            sum++
        }
        if strings.Contains(name, "i") || strings.Contains(name, "I") {
            distribution[name] = distribution[name] + 2
            sum = sum + 2
        }
        if strings.Contains(name, "o") || strings.Contains(name, "O") {
            distribution[name] = distribution[name] + 3
            sum = sum + 3
        }
        if strings.Contains(name, "u") || strings.Contains(name, "U") {
            distribution[name] = distribution[name] + 4
            sum = sum + 4
        }
    }
    return coins - sum
}

func main() {
    left := dispatchCoin()
    fmt.Println("剩下: ", left)
    for k, v := range distribution {
        fmt.Println(k, v)
    }
}

Method 2:

// 根据规则分发金币,返回剩余的金币数量
func dispatchCoin() int {
    sum := 0
    // 1. 遍历名字的切片
    for _, name := range users {
        // 2. 根据规则分发金币
        for _, char := range name {
            switch char {
            case 'e', 'E':
                distribution[name] = distribution[name] + 1
                sum = sum + 1
            case 'i', 'I':
                distribution[name] = distribution[name] + 2
                sum = sum + 2
            case 'o', 'O':
                distribution[name] = distribution[name] + 3
                sum = sum + 3
            case 'u', 'U':
                distribution[name] = distribution[name] + 4
                sum = sum + 4
            }
        }
    }
    return coins - sum
}

func main() {
    left := dispatchCoin()
    fmt.Println("剩下: ", left)
    for k, v := range distribution {
        fmt.Println(k, v)
    }
}

指针

指针和地址有什么区别?

地址:就是内存地址(用字节来描述的内存地址)

指针:指针是带类型的。

&*

&:表示取地址

*:根据地址取值

func main() {
    // var a int
    // fmt.Println(a)
    // b := &a // 取变量a的内存地址
    // fmt.Printf("b=%v\n", b)
    // fmt.Printf("type b :%T\n", b)
    // c := "豪杰"
    // fmt.Printf("&c=%v\n", &c)
    // // b = &c //取c的内存地址不能赋值给b

    // d := 100
    // b = &d
    // fmt.Println(b)
    // // *取地址对应的值
    // fmt.Println(*b)
    // // 指针可以做逻辑判断
    // fmt.Println(b == &d)

    //指针的应用
    a := [3]int{1, 2, 3}
    modifyArray(a) //在函数中复制了数组赋值给了内部的a1
    fmt.Println(a)
    modifyArray2(&a)
    fmt.Println(a)
}

// 定义一个修改数组第一个元素为100的函数
func modifyArray(a1 [3]int) {
    a1[0] = 100 //只是修改的内部的a1这个数组
}

// 定义一个修改数组第一个元素为100的函数
// 接收的参数是一个数组的指针
func modifyArray2(a1 *[3]int) {
    // (*a1)[0] = 100 //只是修改的内部的a1这个数组
    //语法糖:因为Go语言中指针不支持修改
    a1[0] = 100 //只是修改的内部的a1这个数组
}

new和make

new:是用来初始化值类型指针的

make:是用来初始化slicemapchan

    // 以下是错误的写法
    // var a *int //a是一个int类型的指针
    // var b *string
    // var c *[3]int
    // 以上是错误的写法
    var a = new(int) //得到一个int类型的指针
    fmt.Println(a)

    *a = 10
    fmt.Println(a)
    fmt.Println(*a)

    var c = new([3]int)
    fmt.Println(c)
    c[0] = 1
    fmt.Println(*c)

panic和recover

panic:运行时异常 recover:用来将函数在panic时恢复回来,用于做一些资源回收的操作 注意:程序会出现panic一定是不正常的。

func f1() {
    defer func() {
        // recover
        err := recover() //尝试将函数从当前的异常状态恢复过来
        fmt.Println("recover抓到了panic异常", err)
    }()
    var a []int
    a[0] = 100 //panic
    fmt.Println("panic之后")
}

// panic错误
func main() {
    f1()
    fmt.Println("这是main函数")
}

结构体(struct)和方法

type关键字用来在Go语言中定义新的类型。

自定义类型和类型别名

自定义类型

创造一个新类型

// NewInt 是一个新的类型
type NewInt int

// 类型别名:只存在代码编写过程中,代码编译之后根本不存在haojie
// 提高代码的可读性
type haojie = int

// byte uint8

func main() {
    var a NewInt
    fmt.Println(a)
    fmt.Printf("%T\n", a)
    var b haojie
    fmt.Println(b)
    fmt.Printf("%T\n", b)

    var c byte
    fmt.Println(c)
    fmt.Printf("%T\n", c)
}

类型别名(软链)

var MyInt = int

byte: uint8 和 rune:int32是Go语言内置的别名。

类型别名只在代码编写过程中生效,编译完不存在。

结构体的定义

type student struct {
    name   string
    age    int
    gender string
    hobby  []string
}

func main() {

    stu1 := student{
        "琪琪",
        10,
        "女",
        []string{"吃", "喝", "玩", "乐"},
    }

    stu2 := &student{
        name:   "妙妙",
        gender: "女",
    }

    fmt.Printf("%p\n", &stu1)
    fmt.Printf("%p\n", stu2)
}
// 结构体
// 创在新的类型要使用type关键字
type student struct {
    name   string
    age    int
    gender string
    hobby  []string
}

func main() {
    var haojie = student{
        name:   "豪杰",
        age:    19,
        gender: "男",
        hobby:  []string{"篮球", "足球", "双色球"},
    }

    //结构体支持.访问属性
    fmt.Println(haojie)
    fmt.Println(haojie.name)
    fmt.Println(haojie.age)
    fmt.Println(haojie.gender)
    fmt.Println(haojie.hobby)

    // 实例化方法1
    // struct是值类型的
    // 如果初始化时没有给属性(字段)设置对应的初始值,那么对应属性就是其类型的默认值
    var wangzhan = student{}
    fmt.Println(wangzhan.name)
    fmt.Println(wangzhan.age)
    fmt.Println(wangzhan.gender)
    fmt.Println(wangzhan.hobby)

    // 实例化方法2 new(T) T:表示类型或结构体
    var yawei = new(student)
    fmt.Println(yawei)
    // (*yawei).name
    yawei.name = "亚伟"
    yawei.age = 18
    fmt.Println(yawei.name, yawei.age)
    // 实例化方法3
    var nazha = &student{}
    fmt.Println(nazha)
    nazha.name = "沙河娜扎"
    fmt.Println(nazha.name)

    //结构体初始化
    //只填值初始化
    var stu1 = student{
        "豪杰",
        18,
        "男",
        []string{"男人", "女人"},
    }
    fmt.Println(stu1.name, stu1.age)
    //键值对初始化
    var stu2 = &student{
        name:   "豪杰",
        gender: "男",
    }
    fmt.Println(stu2.name, stu2.age, stu2.gender)
}

结构体的实例化

  1. 基本实例化

go var haojie = student{ name: "豪杰", age: 19, gender: "男", hobby: []string{"篮球", "足球", "双色球"}, }

结构体的初始化

结构体的内存布局

了解为主

结构体的字段在内存上是连续的。

//结构体的内存布局
// 内存是以字节为单位的十六进制数
// 1字节 = 8位 = 8bit

func main() {
    type test struct {
        a int16
        b int16
        c int16
    }

    var t = test{
        a: 1,
        b: 2,
        c: 3,
    }
    fmt.Println(&(t.a))
    fmt.Println(&(t.b))
    fmt.Println(&(t.c))
}
//结构体是一个值类型
type student struct {
    name string
    age  int8
}

func main() {
    var stu1 = student{
        name: "豪杰",
        age:  18,
    }

    stu2 := stu1 // 将结构体stu1的值完整的复制一份给了stu2
    stu2.name = "王展"
    fmt.Println(stu1.name)
    fmt.Println(stu2.name)

    stu3 := &stu1 //将stu1对应的地址赋值给了stu3,stu3的类型是一个*student
    fmt.Printf("%T\n", stu3)
    (*stu3).name = "娜扎"
    fmt.Println(stu1.name, stu2.name, stu3.name)
}

构造函数

type student struct {
    name   string
    age    int
    gender string
    hobby  []string
}

//自己实现一个构造函数
func newStudent(n string, age int, g string, h []string) *student {
    return &student{
        name:   n,
        age:    age,
        gender: g,
        hobby:  h,
    }
}

func main() {
    hobbySlice := []string{"篮球", "球"}
    haojie := newStudent("豪杰", 18, "男", hobbySlice)
    fmt.Println(haojie.name)
}

基于函数版学员信息管理系统

获取终端输入

需求分析

func main() {
    var (
        name    string
        age     int
        married bool
    )
    fmt.Println(name, age, married)
    // fmt.Scan(&name, &age, &married)
    // fmt.Scanf("name:%s age:%d married:%t\n", &name, &age, &married)
    fmt.Scanln(&name, &age, &married)
    fmt.Println(name, age, married)

    // 1. 打印菜单
    // 2. 等待用户输入菜单选项
    // 3. 添加书籍的函数
    // 4. 修改书籍的函数
    // 5. 展示书籍的函数
    // 6. 退出 os.Exit(0)
}

代码实现

package main

import (
    "fmt"
    "os"
)

// 需求
// 使用函数实现一个简单的图书管理系统。
// 每本书有书名、作者、价格、上架信息,
// 用户可以在控制台添加书籍、修改书籍信息、打印所有的书籍列表。

// 需求分析
// 0. 定义结构体
type book struct {
    title   string
    author  string
    price   float32
    publish bool
}

// 定义创建新书的构造函数
func newBook(title, author string, price float32, publish bool) *book {
    return &book{
        title:   title,
        author:  author,
        price:   price,
        publish: publish,
    }
}

//定义一个存放book指针的切片,用来存储所有的书籍
var allBooks = make([]*book, 0, 200)

// 1. 打印菜单
func showMenu() {
    fmt.Println("欢迎登录BMS!")
    fmt.Println("1. 添加书籍")
    fmt.Println("2. 修改书籍信息")
    fmt.Println("3. 展示所有书籍")
    fmt.Println("4. 退出")
}

// 2. 等待用户输入菜单选项
// 定义一个专门用来获取用户输入的书籍信息的
func userInput() *book {
    var (
        title   string
        author  string
        price   float32
        publish bool
    )
    //3.1 获取用户输入
    fmt.Println("请根据提示输入相关内容")
    fmt.Print("请输入书名:")
    fmt.Scanln(&title)
    fmt.Print("请输入作者:")
    fmt.Scanln(&author)
    fmt.Print("请输入价格:")
    fmt.Scanln(&price)
    fmt.Print("请输入是否上架[true|false]:")
    fmt.Scanln(&publish)
    fmt.Println(title, author, price, publish)
    //3.2 创建一本新书
    book := newBook(title, author, price, publish)
    return book
}

// 3. 添加书籍的函数
func addBook() {
    book := userInput()
    //3.3 把书添加到allBooks切片中
    // 3.3.1 判断新增的书名是否已经存在
    for _, b := range allBooks {
        if b.title == book.title {
            fmt.Printf("《%s》这本书已经存在了!", book.title)
            return
        }
    }
    allBooks = append(allBooks, book)
    fmt.Println("添加书籍成功!")
}

// 4. 修改书籍的函数
func updateBook() {
    book := userInput()
    // 4.1 遍历所有的书籍根据书名找到要修改的那本书,把信息更新一下
    for index, b := range allBooks {
        // 如果便利到的这本书的书名正好等于book.title 就更新
        if b.title == book.title {
            allBooks[index] = book
            fmt.Printf("书名:《%s》更新成功!\n", book.title)
            return
        }
    }
    fmt.Printf("书名:《%s》不存在!\n", book.title)
}

// 5. 展示书籍的函数
func showBooks() {
    if len(allBooks) == 0 {
        fmt.Println("啥也没有!")
        return
    }
    for _, b := range allBooks {
        fmt.Printf("《%s》 作者:%s 价格:%.2f 是否上架销售:%t\n", b.title, b.author, b.price, b.publish)
    }
}

// 6. 退出 os.Exit(0)

func main() {
    for {
        showMenu()
        // 2. 等待用户输入菜单选项
        var option int
        fmt.Scanln(&option)
        switch option {
        case 1:
            addBook()
        case 2:
            updateBook()
        case 3:
            showBooks()
        case 4:
            os.Exit(0)
        }
    }

}

结构体的嵌套

结构体的匿名字段

//函数是谁都可以调用的。
//方法就是某个具体的类型才能调用的函数
type people struct {
    name   string
    gender string
}

//函数指定接受者之后就是方法
// 在go语言中约定成俗不用this也不用self,而是使用后面类型的首字母的小写
func (p *people) dream() {
    p.gender = "男"
    fmt.Printf("%s的梦想是不用上班也有钱拿!\n", p.name)
}
func main() {
    var haojie = &people{
        name:   "豪杰",
        gender: "爷们",
    }
    // (&haojie).dream()
    haojie.dream()
    fmt.Println(haojie.gender)
}

什么时候应该使用指针类型

1, 需要修改接受者中的值
2, 接受者是拷贝代价比较大的大对象
3, 一般情况下通常都采用指针接受者
// 可以给任意类型追加方法
// 不能给别的包定义的类型添加方法

type MyInt int

func (m *MyInt) sayHi() {
    fmt.Println("Hello MyInt~")
}

func main() {
    var a MyInt
    fmt.Println(a)
    a.sayHi()
}
// 匿名字段
type student struct {
    name string
    string
    int
}

func main() {
    var stu1 = student{
        name: "豪杰",
    }
    fmt.Println(stu1.name)
    fmt.Println(stu1.string)
    fmt.Println(stu1.int)
}
//结构体的嵌套
type address struct {
    province string
    city     string
}
type email struct {
    province string
}
type student struct {
    name    string
    age     int
    address //嵌套了别的结构体
    email   //嵌入匿名结构体
}

func main() {
    var stu1 = student{
        name: "豪杰",
        age:  18,
        address: address{
            province: "河北",
            city:     "雄安",
        },
    }
    fmt.Println(stu1)
    fmt.Println(stu1.name)
    // fmt.Println(stu1.province) //匿名字段支持直接访问
    fmt.Println(stu1.address.province) // 当匿名字段有冲突的时候必须显式调用
    fmt.Println(stu1.email.province)   // 当匿名字段有冲突的时候必须显式调用
}
// 结构体内嵌模拟“继承”
type animal struct {
    name string
}

//定义一个动物会动的方法
func (a *animal) move() {
    fmt.Printf("%s会动~\n", a.name)
}

//定义一个狗的结构体
type dog struct {
    feet int
    animal
}

//定义了一个狗的方法 wangwang
func (d *dog) wangwang() {
    fmt.Printf("%s 在叫:汪汪汪~\n", d.name)
}

func main() {
    var a = dog{
        feet: 4,
        animal: animal{
            name: "旺财",
        },
    }
    a.wangwang() //调用狗的方法
    a.move()     //调用动物的方法
}
import (
    "encoding/json"
    "fmt"
)

//json序列化

//Student 学生
// type Student struct {
//  ID     int
//  Gender string
//  Name   string
// }

// Student 是一个结构体
//定义元信息:json tag
type student struct {
    ID     int    `json:"id"`
    Gender string `json:"gender"`
    Name   string `json:"name"`
}

func main() {
    var stu1 = student{
        ID:     1,
        Gender: "男",
        Name:   "豪杰",
    }
    // 序列化:把编程语言里面的数据转换成 JSON 格式的字符串
    v, err := json.Marshal(stu1)
    if err != nil {
        fmt.Println("JSON格式化出错啦!")
        fmt.Println(err)
    }
    fmt.Println(v)               //[]byte
    fmt.Printf("%#v", string(v)) //把[]byte转换成string

    // str := "{\"ID\":1,\"Gender\":\"男\",\"Name\":\"豪杰\"}"
    //  //反序列化:把满足JSON格式的字符串转换成 当前编程语言里面的对象
    //  var stu2 = &Student{}
    //  json.Unmarshal([]byte(str), stu2)
    //  fmt.Println(stu2)
    //  fmt.Printf("%T\n", stu2)
}

学生管理系统

import (
    "fmt"
    "os"
)

func displayMenu() {
    fmt.Println("*************************")
    fmt.Println("*欢迎使用统爷大学学生管理系统*")
    fmt.Println("*************************")
    fmt.Println("请选择以下操作")
    fmt.Println("【1】添加学生")
    fmt.Println("【2】修改学生")
    fmt.Println("【3】删除学生")
    fmt.Println("【4】显示学生")
    fmt.Println("【5】退出系统")
}

type student struct {
    name  string
    id    int
    age   int
    class string
}

func newStudent(name string, id, age int, class string) *student {
    return &student{
        name:  name,
        id:    id,
        age:   age,
        class: class,
    }
}

func inputStudent() *student {
    var (
        name  string
        id    int
        age   int
        class string
    )
    fmt.Print("学生姓名:")
    fmt.Scanln(&name)
    fmt.Print("学生id:")
    fmt.Scanln(&id)
    fmt.Print("学生年龄:")
    fmt.Scanln(&age)
    fmt.Print("学生班级:")
    fmt.Scanln(&class)
    newStu := newStudent(name, id, age, class)
    return newStu
}

type studentMgr struct {
    allStudent []*student
}

func newStudentMgr() *studentMgr {
    return &studentMgr{
        allStudent: make([]*student, 0, 100),
    }
}

func (s *studentMgr) addStu(stu *student) {
    for _, v := range s.allStudent {
        if v.name == stu.name {
            fmt.Printf("%s同学已经存在\n", v.name)
            return
        }
    }
    s.allStudent = append(s.allStudent, stu)
}

func (s *studentMgr) editStu(stu *student) {
    for k, v := range s.allStudent {
        if v.name == stu.name {
            s.allStudent[k] = stu
            return
        }
    }
    fmt.Printf("%s同学还未被录入", stu.name)
}

func (s *studentMgr) deleteStu(stu string) {
    for k, v := range s.allStudent {
        if v.name == stu {
            s.allStudent = append(s.allStudent[:k], s.allStudent[k+1:]...)
            return
        }
    }
    fmt.Printf("%s同学还未被录入", stu)
}

func (s *studentMgr) listStu() {
    if len(s.allStudent) != 0 {
        for _, v := range s.allStudent {
            fmt.Printf("%s, 年龄:%d, 学号:%d, 班级%s\n", v.name, v.age, v.id, v.class)
        }
        return
    }
    fmt.Println("还没有学生录入")
}

func main() {
    stuMgr := newStudentMgr()
    for {
        displayMenu()
        var userChoice int
        fmt.Print("您要执行的操作是: ")
        fmt.Scanln(&userChoice)
        // fmt.Println(userChoice)
        switch userChoice {
        case 1:
            enteredStu := inputStudent()
            stuMgr.addStu(enteredStu)
        case 2:
            enteredStu := inputStudent()
            stuMgr.editStu(enteredStu)
        case 3:
            var enteredStu string
            fmt.Print("请输入需要删除的学生姓名:")
            fmt.Scanln(&enteredStu)
            stuMgr.deleteStu(enteredStu)
        case 4:
            stuMgr.listStu()
        case 5:
            os.Exit(0)
        }
        fmt.Println()
        fmt.Println()
        fmt.Println()
    }
}