Go - 28 Go 结构体

 结构体的介绍
    Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。
    Golang没有类(class),go语言的结构体(struct)和其他编程语言的类(class)有同等地位,可以理解golang是基于struct来实现OOP特性的;
    Golang面向对象编程非常简洁,去掉了传统OOP语言的继承,方法重载,构造函数和析构函数,隐藏的this指针等;
    Golang任然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其他OOP语言不一样,比如继承,Golang没有extends关键字,继承是通过匿名字段来实现的。
    Golang面向对象(OOP)很优雅,OOP本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活,也就是说在golang中面向接口编程是非常重要的特性;
 
结构体与结构体变量的区别
        将一类事物的特性提取出来,形成一个新的数据类型,就是一个结构体;
        通过这个结构体,我们可以创建多个变量(实例/对象),事物可以是猫类,也可以是Person,Fish 或者某个工具类...
        从某个结构体到变量,就是创建了一个该结构体变量,也可以说是定义一个该结构体变量;
        结构体:是自定义的数据类型,代表一类事物(猫,人);
        结构体变量【实例】是具体的,实际的,代表一个具体的变量;
 
结构体在内存中布局
        结构体即值类型,可以直接修改属性,即变量直接指向空间,而不是指向一个地址,而再通过地址指向某空间;
 
结构体的声明和字段使用陷阱
        type  标识符(结构体名称)  struct {
                field1  type
                field2  type
        }
        示例:
        type Student struct {  // 结构体名字首字母大写既可以跨包使用
            Name  string
            Age      int
            Address string
        }
        字段/属性 基本介绍:
            从概念或叫法上看,结构体字段 = 属性 = field 
            字段是结构体的一个组成部分,一般是基本数据类型,数组,也可以是引用类型;
            注意事项和细节说明:
                在创建一个结构体变量后,如果没有给字段赋值,各字段都对应一个零值(默认值),规则同前面讲的一样:
                布尔类型是 false,整型:0,字符串是:""
                数组类型的默认值和它的元素类型相关,比如 score [3]int  则为[0,0,0]
                指针,slice, 和map的零值都是null即nil,即还没有分配空间,需要先make才能使用。
       示例:
            // 结构体
            type Person struct {     
                    Name string      
                    Age  int      
                    Scores [5]float64     
                    ptr   *int  // 指针     
                    slice []int  // 切片     
                    map1  map[string]string  // map  
            }
            // 实例化一个Person对象
            // 结构体 类似 其他语言的class    
            var p1 Person      
            fmt.Println(p1)  // { 0 [0 0 0 0 0] <nil> [] map[]}     
            if p1.ptr == nil {         
                fmt.Println("ok1")     
            }     
            if p1.slice == nil {         
                fmt.Println("ok2")     
            }     
            if p1.map1 == nil {         
                fmt.Println("ok3")     
            }     
            // 使用slice  一定要 make     
            p1.slice = make([]int, 10)     
            p1.slice[0] = 200     
            fmt.Println(p1.slice)  // [200 0 0 0 0 0 0 0 0 0]     
            // 使用map  一定要make     
            p1.map1 = make(map[string]string)     
            p1.map1["k1"] = "v1"     
            fmt.Println(p1.map1)  // map[k1:v1]     
            // 基本数据类型需要使用 new     
            p1.ptr = new(int)  // new 内置函数对 值类型进行 分配内存 需要传类型参数     
            *p1.ptr = 10     
            fmt.Println(p1.ptr)  // 0xc0000a0838
            fmt.Println(*p1.ptr)  // 10
            fmt.Println(p1)  // { 0 [0 0 0 0 0] 0xc0000a0838 [200 0 0 0 0 0 0 0 0 0] map[k1:v1]}
 
        不同结构体变量的字段是独立的,互不影响,一个结构体变量字段的更改,不影响另外一个。
                示例:
                    type Monster struct {    
                        Name string     
                        Age  int  
                    }
                    var m1 Monster    
                    m1.Name = "袋鼠"     
                    m1.Age = 200     
 
                    m2 := m1    // 完全复制了一份新的空间,然后把值也赋值给 m2 所以 修改m2 不会影响 m1
                    m2.Name = "花袋鼠"     
                    fmt.Println(m1, m2) // {袋鼠 200} {花袋鼠 200}
 
创建结构体实例的四种方式:
    创建结构体变量和访问结构体字段
            方式1:直接声明
                var per Person
            方式2:{}  推荐
                var per  Person = Person{}
                示例:
                    m := Monster{"杀无赦", 100}    
                    fmt.Println(m)    // {杀无赦 100}
            方式3:
                示例:
                    var m1 *Monster = new(Monster)    
                    // 因为m1 是一个指针,因此标准的给字段赋值方式     
                    // (*m1).Name = "老沙" 也可以写成 m1.Name = "沙师弟"     
 
                    // 原因:go的设计者 为了程序员使用方便,底层会对 m1.Name = "沙师弟" 进行处理                           // 会给 m1 加上 取值运算 (*m1).Name = "沙师弟"     
                    (*m1).Name = "老沙"     
                    m1.Name = "沙师弟"     
                    (*m1).Age = 200     
                    m1.Age = 300     
                    fmt.Println(m1)  // &{沙师弟 300}     
                    fmt.Println(*m1)  // {沙师弟 300}
            方式4:类似方式三 都是结构体指针
                示例:
                    // var p *Person = &Person{"smith", 30}   也可以赋初值
                    var p *Person = &Person{}    
                    p.Name = "mary"     
                    p.Age = 30     
                    fmt.Println(*p)  // {mary 30 }
 
结构体使用细节
       结构体的所有字段在内存中是连续的;
        结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字,,个数,和类型)
       示例:
            type A struct {        
                    num int      
            }     
            type B struct {         
                    num int      
            }     
            var a A      
            var b B      
            a = A(b)   // 可以转换,前提:结构体的字段完全一样;
 
        结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转。
        struct的每个字段上,可以写上一个tag(Domain  string `json:"domain"` -->tag),该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化;(结构体的序列化:使用的包:"encoding/json" , 方法:json.Marshal(结构体实例),返回的是[]byte 切片,所以需要强转为str--> string([]byte) ---> 变为字符串)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2020-11-23 21:19  以赛亚  阅读(143)  评论(0编辑  收藏  举报