zoukankan      html  css  js  c++  java
  • 3.11 Go Struct结构体

    3.11 Go Struct结构体

    Golang支持OOP面向对象编程。

    Go的结构体struct如同python的class

    Go基于struct实现OOP特性,只有组合composition这个特性。

    2. 结构体概念

    1)将一类事务特性提取出一个新的数据类型,就是结构体。

    2)通过结构体可以创建多个实例。

    3)可以是Student结构体、可以是Animal、Person结构体。

    3. 结构体特点

    1)struct用于定义复杂数据结构

    2)struct可以包含多个字段

    3)struct可以定义方法(注意不是函数,是golang的method)

    4)struct可以是值类型

    5)struct类型可以嵌套

    6)Go没有class,只有struct类型

    7)结构体是自定义类型,不得与其他类型强转

    8)可以为struct每一个字段添加tag,这个tag可以反射机制获取,场景如json序列化和反序列化。

    4. 结构体定义

    package main
    
    import "fmt"
    
    type Person struct {
        Name string
        Age  int
    }
    func main() {
        //声明方式
        p1 := Person{"小黑", 18} //有序赋值,并且必须包含所有字段,否则报错
        p2 := Person{Age: 18}  //关键词赋值,未赋值字段有空值
        fmt.Println(p1)
        fmt.Println(p2)
    }
    

    练习struct

    package main
    
    import "fmt"
    
    //声明结构体名称Stu
    type Stu struct {
        Name    string //结构体字段
        Age     int    //如未赋值有默认空值
        Address string
        Score   int
    }
    
    //结构体可以定义复杂的类型
    type Person struct {
        Name  string
        Age   int
        Score [5]float64        //容量为5的数组
        prt   *int              //指针类型
        slice []int             //int类型切片
        map1  map[string]string //map类型字段
        //slice和map默认值是nil,必须make初始化才可使用
    }
    
    //结构体是值类型,不同结构体实例之间互不影响
    type Monster struct {
        Name string
        Age  int
    }
    
    func main() {
        //声明结构体类型变量
        var stu1 Stu
        //结构体可以通过 . 的方式赋值,声明赋值方式一
        stu1.Name = "小黑"
        stu1.Age = 18
        stu1.Address = "沙河"
        stu1.Score = 100
        fmt.Printf("stu1的名字=%v 年纪=%v 住址=%v 分数=%v
    ", stu1.Name, stu1.Age, stu1.Address, stu1.Score)
    
        //声明赋值方式二
        monster1 := Monster{"红孩儿", 18}
        monster2 := Monster{"女妖怪", 999}
        //两个结构体实例,内存地址不一样,确保独立
        fmt.Printf("monster1地址:%p
    ", &monster1)
        fmt.Printf("monster2地址:%p
    ", &monster2)
    
        //声明方式三
        //用来分配内存,主要用来分配值类型,比如int、struct。返回指向类型的 指针
        //此时m1是一个指针
        var m1 *Monster = new(Monster)
        //给m1赋值
        (*m1).Name = "孙悟空" //编译器自动识别 同于 m1.Name="孙悟空"
        (*m1).Age = 9999   //同上
        fmt.Println(*m1)   //此时m1是指针变量,加上*取值
    
        //声明方式四
        m2 := &Monster{
            "猪八戒",
            888,
        }
        fmt.Println(*m2)
        //第三、第四种返回结构体指针,go编译器自动识别,简化程序员心智负担,建议用1、2方法
    }
    

    4.1.1. 匿名结构体

    没有名字的结构体

    package main
    
    import "fmt"
    
    func main() {
        //匿名函数
        func() {
            fmt.Println("我是匿名函数")
        }()
        //匿名结构体
        p1 := struct {
            name string
            age  int
        }{
            name: "张三",
            age:  18,
        }
        fmt.Println(p1)
    }
    

    4.1.2. 匿名字段

    package main
    
    import "fmt"
    
    func main() {
        type student struct {
            string //匿名字段,类型当做字段名
            int
        }
        s1 := student{
            "吴亦凡",
            18,
        }
        fmt.Println(s1.string, s1.int)
    }
    

    4.1.3. 结构体嵌套

    面向对象:聚合关系

    ​ 一个类作为另一个类的属性

    package main
    
    import "fmt"
    
    type Book struct {
        bookName string
        price    float64
        author   string
    }
    
    type Person struct {
        name string
        age  int
        book Book //继承Book结构体的字段 ,模拟聚合关系
    }
    
    func main() {
        //先定义好的book对象
        b1 := Book{"如何找到女朋友", 999.999, "alex金角大王"}
        p1 := Person{"武沛奇", 26, b1} //b1就是Book结构体类型,武沛奇买了一本书
        fmt.Printf("姓名:%s,年纪:%d,书名:%s,价格:%.2f,书的作者:%s
    ", p1.name, p1.age, p1.book.bookName, p1.book.price, p1.book.author)
    
        //声明初始化book对象,一行搞定
        p2 := Person{"萧峰", 25, Book{"如何找到男朋友", 3.58, "超哥著作"}}
        fmt.Printf("姓名:%s,年纪:%d,书名:%s,价格:%.2f,书的作者:%s
    ", p2.name, p2.age, p2.book.bookName, p2.book.price, p2.book.author)
    }
    

    结构体嵌套练习

    学生与书架

    package main
    
    import "fmt"
    
    type Book struct {
        bookName string
        price    float64
    }
    type Student struct {
        name  string
        age   int
        books []*Book
    }
    
    func main() {
        b1 := Book{"霸道总裁爱上我", 120.22}
        b2 := Book{"斗破苍穹", 12.5}
        b3 := Book{"我和师姐的故事", 15.5}
        //定义书架,默认没有书,可以容纳10本书
        //用Book就是值拷贝,*Book就是放入书的内存地址
        bookSlice := make([]*Book, 0, 10)
        //注意需要传入地址
        bookSlice = append(bookSlice, &b1, &b2, &b3)
        //创建一个学生
        s1 := Student{"小猪沛奇", 3, bookSlice}
        fmt.Printf("姓名:%s,年纪:%d
    ", s1.name, s1.age)
        //查看书架上书的信息
        for i := 0; i < len(s1.books); i++ {
            book := s1.books[i]
            fmt.Printf("	第%d本书,书名:%s,书价格:%.2f
    ", i+1, (*book).bookName, book.price)
        }
    
        //创建图书方式二
        //初始化创建时候,必须对切片分配内存
        s2 := Student{"特斯拉车主alex", 46, make([]*Book, 0, 10)}
        //放入书架的书,放入书的内存地址
        s2.books = append(s2.books, &Book{"斗罗大陆", 55.3}, &Book{"python入门到放弃", 1.28}, &Book{"王思聪与三个网红的一天", 999999.99})
        fmt.Printf("学生名:%s,学习年龄:%d
    ", s2.name, s2.age)
        //输入所有s2学生看的书
        for k, v := range s2.books {
            fmt.Printf("	第%d本书,书名:%s,价格:%.2f
    ", k+1, v.bookName, v.price)
        }
    }
    

    4.1.4. 面向对象:继承关系

    ​ 一个类作为另一个类的子类:子类,父类

    继承:面向对象的第二个特征,用于描述两个类的关系

    ​ 子类,派生类,subClass继承父类(超类,基类,superClass)

    ​ 子类可以直接访问父类已有的属性和方法

    ​ 子类可以新增自己的属性和方法

    ​ 子类也可以重写父类已有的方法

    通过匿名字段的方式,进行嵌套,实现继承关系

    package main
    
    import "fmt"
    
    //1.定义父类
    type Person struct {
        name string
        age  int
    }
    
    //2定义子类,匿名字段,Person即是
    type Son struct {
        Person        //模拟继承结构,继承父类的name,age属性
        school string //子类的新增属性
    }
    
    func main() {
        //父类
        p1 := Person{"李靖", 999}
        fmt.Println(p1.name, p1.age)
        //子类赋值方式一,子类直接访问父类属性
        var s2 Son
        s2.name = "娜扎"
        s2.age = 666
        s2.school = "神仙学校"
        fmt.Println(s2, s2.name, s2.age, s2.school)
        //创建方式二,简写方式
        s3 := Son{Person{"木吒", 667}, "神仙学校"}
        fmt.Println(s3, s3.name, s3.age, s3.school)
    
        //创建方式三,基于key-value写
        s4 := Son{Person: Person{name: "金吒", age: 668}, school: "神仙学校"}
        fmt.Println(s4, s4.name, s4.age, s4.school)
    }
    

    4.1.5. 结构体细节

    1. 结构体字段在内存中是连续的
    package main
    
    import "fmt"
    
    type Test struct {
        A int32
        B int32
        C int32
        D int32
    }
    
    func main() {
        var t Test
        fmt.Printf("a addr:%p
    ", &t.A)
        fmt.Printf("b addr:%p
    ", &t.B)
        fmt.Printf("c addr:%p
    ", &t.C)
        fmt.Printf("d addr:%p
    ", &t.D)
    }
    
    1. 结构体由用户自定义,可以类型转换,但必须完全相同字段、个数、类型
    2. 对结构体进行重新定义(重新type),效果同于结构体别名
    3. struct每个字段,可以写一个tag,这个tab可以通过反射获取,用在序列化,反序列化
    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    type User struct {
        UserName string  `json:"姓名"` //反引号括起来的就是struct tag
        Sex      string  `json:"性别"`
        Score    float32 `json:"成绩"`
        Age      int32   `json:"年纪"`
    }
    
    func main() {
        user := &User{
            UserName: "user01",
            Sex:      "男",
            Score:    99.2,
            Age:      18,
        }
        //将user变量序列化为json格式字符串
        data, _ := json.Marshal(user)
        fmt.Printf("json str:%s
    ", string(data))
    }
    

    4.2. 结构体内存分配

    先看代码

    package main
    
    import "fmt"
    
    type Person struct {
        Name string
        Age  int
    }
    
    func main() {
        //p1有自己的结构体内存地址,
        var p1 Person
        p1.Age = 10
        p1.Name = "王大锤"
    
        //定义P2 指针类型,指向p1的内存地址
        var p2 *Person = &p1
        //两种形式一样,go编译器自动识别
        fmt.Println((*p2).Age)
        fmt.Println(p2.Age)
        //修改p2的结构体值,也就是修改了p1的结构体值
        p2.Name = "葫芦娃"
        fmt.Printf("输出结果  p2.Name=%v p1.Name=%v
    ", p2.Name, p1.Name)
        fmt.Printf("输出结果(*p2).Name=%v p1.Name=%v
    ", (*p2).Name, p1.Name)
    
        //查看p1和p2的内存地址
        fmt.Printf("p1内存地址%p
    ", &p1)
        //p2是指针变量,自己也有一块内存地址,p2的值指向
        fmt.Printf("p2内存地址%p p2的值是%v
    ", &p2, p2)
    }
    

    4.3. 结构体内存分布原理图

  • 相关阅读:
    好消息:Dubbo & Spring Boot要来了
    过年回家,程序猿最怕的5件事
    到底什么是分布式系统?
    SLA服务可用性4个9是什么意思?怎么达到?
    漏洞:会话固定攻击(session fixation attack)
    Mybatis传递多个参数的4种方式(干货)
    注意:阿里Druid连接池监控的两个坑
    消息中间件ActiveMQ、RabbitMQ、RocketMQ、ZeroMQ、Kafka如何选型?
    Java程序员必须掌握的常用Linux命令。
    编程词汇
  • 原文地址:https://www.cnblogs.com/open-yang/p/11256795.html
Copyright © 2011-2022 走看看