golang 结构体struct

1.golang语言面向对象编程说明

1)golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言,所以我们说golang支持面向对象编程特效是比较准确的。

2)golang没有类(class),Go语言的结构体(struct)和其他编程语言的类(class)有同等的地位,可以理解golang是基于struct来实现OOP特性的。

3)golang面向对象编程非常简洁,去掉了传统OOP语言中的继承,方法重载,狗仔函数和解析函数,隐藏this指针等等

4)golang仍然有面向对象编程的继承,封装和多态的特性,只是实现方式和其他OOP语言不一样,比如继承没有extends关键字,继承是通过匿名字段来实现的

5)golang面向对象(OOP)很优雅,OOP本身就是语言类型系统的一部分,通过接口(interface)关联,耦合性低,也非常灵活,也就是说golang中面向接口编程是非常重要的特性。

6)结构体是值类型

2.张老太养了两只猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年100岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名字错误,则显示 张老太没有这只猫

//使用map的缺点,子map的数据类型map[srting]string 只能定义一种数据类型,而年龄和花色是不同的数据类型。
//使用变量和数组都不利于数据管理和维护。因为名字,年龄,颜色都是属于一只猫,但是这里是分开保存的。
//如果希望对一只猫的属性(名字,年龄,颜色)进行操作(绑定方法),也不好处理
map实现
复制代码
func cxcat(cat map[string]map[string]string, name string) {
    if name != "小白" && name != "小花" {
        println("错误,张老太没有这只猫")
    } else {
        fmt.Println(cat[name])
    }
}
func main() {
    //张老太养了两只猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年100岁,花色。
    //请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。
    //如果用户输入的小猫名字错误,则显示 张老太没有这只猫

    //使用map的缺点,子map的数据类型map[srting]string 只能定义一种数据类型,而年龄和花色是不同的数据类型。
    //使用变量和数组都不利于数据管理和维护。因为名字,年龄,颜色都是属于一只猫,但是这里是分开保存的。
    //如果希望对一只猫的属性(名字,年龄,颜色)进行操作(绑定方法),也不好处理
    var cat map[string]map[string]string = make(map[string]map[string]string)
    cat["小白"] = make(map[string]string, 2)
    cat["小白"]["age"] = "3"
    cat["小白"]["花色"] = "白色"
    cat["小花"] = make(map[string]string, 2)
    cat["小花"]["age"] = "100"
    cat["小花"]["花色"] = "花色"
    var name string
    fmt.Println("请输入小猫的名字:")
    fmt.Scanln(&name)
    cxcat(cat, name)
}
复制代码

 

 结构体实现

复制代码
type Cat struct {
    Name  string
    Age   int
    Color string
}

func main() {
    var cat1 Cat
    cat1.Name = "小白"
    cat1.Age = 3
    cat1.Color = "白色"

    fmt.Printf("猫猫的信息如下:\n 名字为:%v\n 年龄为%v\n 颜色为:%v\n", cat1.Name, cat1.Age, cat1.Color)
}
复制代码

 2.结构体声明

type  结构体名称 struct {

  fieldname1 fieldtype1

  fieldname2 fieldtype2

}

1)从概念或叫法上看:结构体字段 = 属性 = field 。如上面field1name 是字段名称 fieldtype是字段类型

2)字段是结构体的一个组成部分,一般是基本数据类型、数组,也可以是引用类型。

3)在创建一个结构体变量后,如果没有给字段赋值,都应对应一个零值,指针、slice和map的零值都是nil,即还没有分配空间,需要先make再使用。

4)不同结构体变量的字段是独立的,互不影响,一个结构体变量字段的更改,不影响另一个。结构体是值类型,默认为值拷贝。

复制代码
type Person struct {
    Name   string
    Age    int
    Scores [5]float64
    ptr    *int
    slice  []int
    map1   map[string]string
}

func main() {
    var p1 Person
    fmt.Println(p1)
    //使用slice,一定要先make
    p1.slice = make([]int, 10)
    p1.slice[0] = 100
    //使用map,一定要先make
    p1.map1 = make(map[string]string)
    p1.map1["key1"] = "tom~"
    fmt.Println(p1)
}
复制代码
复制代码
type Monster struct {
    Name string
    Age  int
}

func main() {
    var monster1 Monster
    monster1.Name = "牛魔王"
    monster1.Age = 36
    monster2 := monster1
    monster1.Name = "青牛怪"
    fmt.Println(monster1)
    fmt.Println(monster2)
}
复制代码

 3.创建结构体实例

1)方式1-直接声明

var person Person

2) 方式2-{}

var person Person = Person{}

p2 := Person{}

3)方式3-&   new出来的实例是指针

var person *Person = new (Person) 

p2 := new(Person)

赋值

(*p2).num = 10 也可以写成p2.num = 10,golong的设计者为了简化操作在底层会对p2.num = 10进行处理,会给P2加上取值运算

fmt.Println(*p2)

4)方式4-{}

var person *Person = &Person{"Marry",60}

//person是一个指针

(*person).Name = "scott" 也可以写成person.Name = "scott",与上面同样设计者会在底层加取值运算

 4.结构体的使用细节

1)结构体的所以字段在内存中是连续存在的

复制代码
type Point struct {
    x int
    y int
}
type Rect struct {
    leftUp, rigntDown Point
}

func main() {
    r1 := Rect{Point{1, 2}, Point{3, 4}}
    //r1有四个int,在内存中是连续分布的
    fmt.Printf("r1.leftUP.x的地址=%v,r1.leftUP.y的地址=%v,rigntDown.x的地址=%v,rigntDown.y的地址=%v",
        &r1.leftUp.x, &r1.leftUp.y, &r1.rigntDown.x, &r1.rigntDown.y)
} //r1.leftUP.x的地址=0xc00001e020,r1.leftUP.y的地址=0xc00001e028,rigntDown.x的地址=0xc00001e030,rigntDown.y的地址=0xc00001e038
复制代码

当struct为指针类型时,指针本身的地址是连续的,但是指针指向的地址不一定是连续的

复制代码
type Rect2 struct {
    leftUp, rigntDown *Point
}

func main() {
    //r2有两个*Point类型,这两个*Point类型本身的地址是连续的
    //但他们指向的地址不一定是连续的
    r2 := Rect2{&Point{1, 2}, &Point{3, 4}}
    fmt.Printf("r2.leftUP本身的地址=%v, r2.rigntDown本身的地址=%v\n",
        &r2.leftUp, &r2.rigntDown)
    fmt.Printf("r2.leftUP指向的地址=%p, r2.rigntDown指向的地址=%p\n",
        r2.leftUp, r2.rigntDown)
}//r2.leftUP本身的地址=0xc000010250, r2.rigntDown本身的地址=0xc000010258  r2.leftUP指向的地址=0xc000020090, r2.rigntDown指向的地址=0xc0000200a0
复制代码

2)结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段名(名字,个数和类型)

复制代码
type A struct {
    Num int
}
type B struct {
    Num int
}

func main() {
    var a A
    var b B
    a = A(b)
    fmt.Println(a, b)
}
复制代码

3)结构体进行type重新定义(相当于取别名),golang认为是新的数据类型,但是可以相互之间强转换

复制代码
type Student struct {
    Name string
    Age  int
}
type Stu Student

func main() {
    var stu1 Student
    var stu2 Stu
    stu2 = Stu(stu1) //可以强转
    fmt.Println(stu1, stu2)
}
复制代码

4)struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列号和反序列化。

对代码说明:将struct变量进行json处理

问题:json处理后的字段也是首字母大写,这样如果我们将json后的字符串返回给其他程序使用,如query,php等,那么可能他们不习惯这个命名方式,怎么办?

解决方案

1)将Monster的字段首字母小写,这个行不通,引用处理后,返回的是空字符串,因为json.Marshal相当于是在其他包访问Monster结构体,字段的首字母小写,就不能在其他包访问

2)使用tag标签来解决

复制代码
type Monster struct {
    Name  string `json:"name"`
    Age   int8   `json:"age"`
    Skill string `json:"skill"`
}

func main() {
    //1.创建一个Monster变量
    monster := Monster{"牛魔王", 80, "牛头拳"}
    //2.将monster变量序列号为json格式字符串
    //encoding.json.Marshal返回v的json编码
    jsonStr, err := json.Marshal(monster)
    if err != nil {
        fmt.Println("json处理错误", err)
    }
    fmt.Println("jsonStr:", string(jsonStr))
}//jsonStr: {"name":"牛魔王","age":80,"skill":"牛头拳"}
复制代码

 

posted @   潇潇暮鱼鱼  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示