• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

GO语言结构体

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

类型别名与自定义类型

自定义类型

Go语言中可以使用type关键字来定义自定义类型。

//NewInt是一种新的类型,具有int类型的特性。
type NewInt int
func main()  {
    var a NewInt
    fmt.Println(a)  //初始值为 0
    fmt.Printf("%T\n",a)    //main.NewInt  类型是自己定义的NewInt
}

类型别名

类型别名是Go1.9版本添加的新功能。
类似于软链,本质上与type是同一个类型。
类型别名只在代码编写过程中生效,编译完不存在。

//类型别名,ares就是bool,编译完后不存在ares类型
type ares = bool
func main()  {
var b ares  //定义b为ares类型
fmt.Println(b)  //bool类型默认只false
fmt.Printf("%T\n",b)    //bool
}

rune和byte其实就是类型别名。

type byte = uint8
type rune = int32

结构体

Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。
Go语言中通过struct来实现面向对象。

结构体定义

语法:

type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
    …
}

类型名:标识自定义结构体的名称,在同一个包内不能重复。
字段名:表示结构体字段名。结构体中的字段名必须唯一。
字段类型:表示结构体字段的具体类型。
示例:

type student struct {
    name string
    id ,age int //相同类型可以写在一行
    hobby []string
    sex  string
}

结构体实例化

结构体必须实例化才可以使用结构体的字段(分配内存)。

var 结构体实例 结构体类型

基本实例化

示例:

func main()  {
    //实例化方法1,使用键值对初始化
    var ares = student{
        name: "ares",
        id: 1,
        age: 28,
        sex: "man",
        hobby: []string{"自行车","爬山"},
    }
    fmt.Println(ares)   //{ares 1 28 [自行车 爬山] man}
    //结构体支持使用.来访问属性
    fmt.Println(ares.name)  //ares
    fmt.Println(ares.age)   //28
    fmt.Println(ares.hobby) //[自行车 爬山]
    //实例化方法2
    var ares1 student
    ares1.name = "ares1"
    ares1.id = 2
    fmt.Println(ares1)  //{ares1 2 0 [] }
        type ares struct {
        age  int
        id   int
        name string
    }
    //方法三
    p1 := ares{}
    p1.name = "ares"
    fmt.Println(p1)    //{0 0 ares}
    //方法4
    p2 := ares{name: "ares", age: 1}
    fmt.Println(p2)    //{1 0 ares}
}

如果初始化没有赋值,那么就为默认值!

指针型结构体

使用new关键字对结构体进行实例化,得到的是结构体的地址。

//指针型结构体
    var ares2 = new(student)
    ares2.id = 3
    ares2.name = "ares2"
    (*ares2).age = 18   //等同于ares2.age = 18
    fmt.Printf("%T\n",ares2)    //*main.student 指针类型
    fmt.Println(ares2)  //&{ares2 3 18 [] }
    //结构体指针支持使用.来访问属性
    fmt.Println(ares2.id)   //3
    fmt.Println(ares2.age)  //18

取结构体的地址初始化

使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。

//取结构体地址初始化
    var ares3 = &student{} //ares3 := &student{}
    fmt.Printf("%T\n",ares3)    //*main.student
    ares3.name = "ares3"
    fmt.Println(ares3)  //&{ares3 0 0 [] }

初始化结构体简写

初始化结构体的时候可以不写键,但是必须初始化结构体所有字段并且顺序一致,不能与键值对初始化混用!

//初始化键值对简写
    ares4 := &student{
        "ares4",
        1,
        25,
        []string{"play"},
        "man",
    }
    fmt.Println(ares4)  //&{ares4 1 25 [play] man}

结构体内存布局

结构体在内存上是占用一块连续的内存。

type con struct {
    a int8
    b int8
    c int8
    d string
    e string
}
n := con{
        10, 21, 35, "a","b",
    }
    fmt.Printf("n.a %p\n", &n.a)    //n.a 0xc0000ae000
    fmt.Printf("n.b %p\n", &n.b)    //n.b 0xc0000ae001
    fmt.Printf("n.c %p\n", &n.c)    //n.c 0xc0000ae002
    fmt.Printf("n.d %p\n", &n.d)    //n.d 0xc0000ae008
    fmt.Printf("n.e %p\n", &n.e)    //n.e 0xc0000ae018

构造函数

指针是值类型,如果结构体比较复杂,值拷贝性能开销会比较大,所以可以使函数返回结构体指针类型。

type student struct {
    name string
    id ,age int //相同类型可以写在一行
    hobby []string
    sex  string
}
//自己实现一个构造函数
func newStudent(name ,sex string, id ,age int,hobby []string) student  {
    return student{
        name : name,
        sex:sex,
        id:id,
        age:age,
        hobby:hobby,
    }
}

//自己实现一个返回指针的构造函数
func newStudentp(name ,sex string, id ,age int,hobby []string) *student  {
    return &student{
        name : name,
        sex:sex,
        id:id,
        age:age,
        hobby:hobby,
    }
}

func main()  {
    ares := newStudent("ares","男",1,28,[]string{"play"})
    ares1 := newStudentp("ares1","男",1,28,[]string{"play"})
    fmt.Println(ares)   //{ares 1 28 [play] 男}
    fmt.Println(ares.name)  //ares
    fmt.Println(ares1)  //&{ares1 1 28 [play] 男}
    fmt.Println(ares1.name) //ares1
}

方法和接收者

函数是谁都可以调用的。
方法就是某个特定的类型才能调用的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。
格式:

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
    函数体
}

接收者变量:在go语言中约定成俗不用this也不用self,而是使用后面类型的首字母的小写

值接收者

当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。

//定义一个persion结构体
type persion struct {
    name string
    age int
    sex string
}
//定义一个newPersion的构造函数
func newPersion(name ,sex string,age int) persion {
    return persion{
        name:name,
        age:age,
        sex:sex,
    }
}
//定义一个值类型接收者方法
func (p persion) Dream()  {
    p.age = 18  //无法修改接受者变量本身
    fmt.Printf("%s 的梦想是有钱!\n",p.name)
}
func main()  {
    var ares = persion{
        name:"ares",
        age:28,
        sex:"男",
    }
    ares.Dream()    //ares 的梦想是有钱!
    fmt.Println(ares.age)   //28
}

指针类型接收者

指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的.

//定义一个指针类型接收者方法
func (p1 *persion) Dream1()  {
    p1.age = 18
    fmt.Printf("%s 的梦想是有大钱!\n",p1.name)
}
func main()  {
    var ares = persion{
        name:"ares",
        age:28,
        sex:"男",
    }
    ares.Dream1()   //ares 的梦想是有大钱!相当于(&ares).Dream1()
    fmt.Println(ares.age)   //18
}

结构体默认是值拷贝,除非使用指针

func main() {
    type Person struct {
        name string
        age  int
        id   int
    }
    p1 := Person{"Ares", 18, 1}
    //值拷贝,开辟新内存地址,不会修改原结构体
    p2 := p1
    p2.name = "ares1"
    fmt.Println(p1) //{Ares 18 1}
    fmt.Println(p2) //{ares1 18 1}
    //指针拷贝,拷贝内存地址,原结构体也会修改
    p3 := &p1
    p3.name = "ares2"
    fmt.Println(p1) //{ares2 18 1}
    fmt.Println(*p3)    //{ares2 18 1}
}

什么场景需要使用指针类型

  1. 需要修改接收者中的值。
  2. 接收者是拷贝代价比较大的大对象。
  3. 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
    ###任意类型添加方法
    在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。
    可以为任意类型追加方法,但不能给别的包定义的类型添加方法。
//可以为任意类型追加方法,不能给别的包定义的类型添加方法
//将int类型定义为自定义的MyInt类型
type MyInt int

//为MyInt添加一个sayhi方法
func (m MyInt) sayhi()  {
    fmt.Println("hihihi")
}
func main()  {
    var m1 MyInt
    m1.sayhi()  //hihihi
    m1 = 10
    fmt.Println(m1) //10
    fmt.Printf("%T\n",m1)       //main.MyInt
}

结构体的匿名字段

结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。

//定义一个匿名persion结构体
type persion1 struct {
    //name string
    string  //没有字段名的就是匿名字段,匿名字段类型不能重复
    int
}
func main()  {
    ares := persion1{
        //name:"ares",    //不能混用
        "男",
        18,
    }
    fmt.Println(ares.string)    //男
    fmt.Println(ares.int)       //18
}

匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。

结构体的嵌套

一个结构体中可以嵌套包含另一个结构体或结构体指针。

//People结构体
type People struct {
    name    string
    age     int
    sex     string
    Address Address //嵌套Address结构体
}

type People1 struct {
    name    string
    age     int
    sex     string
    Address *Address //嵌套Address结构体指针
}

//Address结构体
type Address struct {
    city     string
    province string
}

func main() {
    ares := People{
        name: "ares",
        age:  28,
        Address: Address{
            city:     "北京",
            province: "beijing",
        },
    }
    fmt.Println(ares)                  //{ares 28  {北京 beijing}}
    fmt.Println(ares.name)             //ares
    fmt.Println(ares.Address.province) //beijing
    //拷贝一份结构体
    ares1 := ares
    ares1.Address.province = "bj"
    fmt.Println(ares)  //{ares 28  {北京 beijing}}  值拷贝,开辟新内存,不会修改老结构体
    fmt.Println(ares1) //{ares 28  {北京 bj}}
    p2 := People{
        name: "ares",
        age:  28,
        Address: Address{
            city:     "北京",
            province: "beijing",
        },
    }
    fmt.Println(p2) //{ares 28  {北京 beijing}}
    //复制结构体内存地址
    p3 := &p2
    fmt.Println(p3) //&{ares 28  {北京 beijing}}
    p3.Address.province = "bj" //因为是一块内存,所以原结构体也会修改
    fmt.Println(p2)  //{ares 28  {北京 bj}}
    fmt.Println(*p3) //{ares 28  {北京 bj}}
}

匿名结构体

没有名字的结构体即为匿名结构体。创建结构体结构时同时创建对象。

func main() {
    p1 := struct {
        name string
        id   int
        age  int
    }{"Ares", 1, 18}
    fmt.Println(p1) //{Ares 1 18}
}

当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
如果嵌套多个匿名结构体,打印时需明确指出是哪个匿名结构体中字段。

//People结构体
type People struct {
    name string
    age int
    sex string
    Address  //嵌套Address匿名结构体
}

只有匿名结构体,匿名字段才支持直接访问!
当匿名字段有冲突时必须显示调用!

结构体继承

类似于其他语言中面向对象的继承。

//结构体模拟继承
//定义一个animal结构体
type animal struct {
    name string
}
//定义一个动物会动的方法
func (a *animal) move()  {
    fmt.Printf("%s会动\n",a.name)
}
//定义一个狗的结构体
type dog struct {
    feet int
    animal
}
//定义一个狗会叫的方法
func (d *dog) wnag()  {
    fmt.Printf("%s会叫。\n",d.name)
}
func main()  {
    var jinmao = dog{
        feet:4,
        animal:animal{
            name:"旺财",
        },
    }
    fmt.Println(jinmao) //{4 {旺财}}
    jinmao.move()   //旺财会动
    jinmao.wnag()   //旺财会叫。
}

结构体字段的可见性

结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。

结构体与结构体tag与JSON序列化

tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。

//json序列化
//定义stu的结构体
type stu struct {
    ID int
    Name string
    Sex  string
}
//默认序列化为,字段还是大些,如果需要序列化为小写,需要定义元信息:json tag
type stu11 struct {
    ID int          `json:"id"`
    Name string     `json:"name"`
    Sex  string     `json:"sex"`
}

func main()  {
    var stu1 = stu{
        ID:1,
        Name:"ares",
        Sex:"男",
    }
    //序列化,把编程语言中的数据转换成JSON格式的字符串
    v,err := json.Marshal(stu1)
    if err != nil{
        fmt.Println("JSON序列化失败!")
        fmt.Println(err)
    }
    fmt.Println(v)  //[123 34 73 68 34 58 49 44 34 78 97 109 101 34 58 34 97 114 101 115 34 44 34 83 101 120 34 58 34 231 148 183 34 125]
    fmt.Printf("v的类型是:%T\n",v)  //v的类型是:[]uint8  []byte类型
    //把[]byte类型转换成string
    fmt.Printf("%v\n",string(v))        //{"ID":1,"Name":"ares","Sex":"男"}
    fmt.Printf("%#v\n",string(v))       //"{\"ID\":1,\"Name\":\"ares\",\"Sex\":\"男\"}"

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

    var stu22 = stu11{
        ID:2,
        Name:"ares",
        Sex:"男",
    }
    stu222,err := json.Marshal(stu22)
    fmt.Println(stu222)
    //此时序列化后为小写
    fmt.Println(string(stu222)) //{"id":2,"name":"ares","sex":"男"}
}

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
vscode安装设置go发布时间:2022-07-10
下一篇:
GO基础发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap