结构体

go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的数据类型明显就无法满足需求了,go原因提供了一种自定义的数据类型,.可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct.也就是我们通过struct来定义自己的类型了

go语言中通过struct来面向对象

结构体基本

package main

import "fmt"

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

func main() {
    var p person
    p.name = "zhoulin"
    p.age = 9000
    p.gender = "男"
    p.hobby = []string{"篮球", "足球", "双色球"}
    fmt.Println(p) //{zhoulin 9000 男 [篮球 足球 双色球]}
    //访问变量p的字段
    fmt.Println(p.name)   //zhoulin
    fmt.Printf("%T\n", p) //main.person
    //匿名结构体,多用于临时场景
    var s struct {
        x string
        y int
    }
    s.x = "嘿嘿"
    s.y = 100
    fmt.Println(s)        //{嘿嘿 100}
    fmt.Printf("%T\n", s) //struct { x string; y int }
}

结构体指针,和结构体初始化

package main

import "fmt"

type person struct {
    name   string
    gender string
}

func f(x person) {
    x.gender = "女"
}
func f2(x *person) {
    (*x).gender = "女" //根据内存地址找到原变量,修改的就是原来的变量
    x.gender = "女"    //go无法修改指针,这种写法和上面的写法作用一样(语法糖)
}
func main() {
    var p person
    p.name = "周林"
    p.gender = "男"
    f(p)
    fmt.Println(p.gender) //打印结果还是为男,因为内部修改的是局部的变量
    f2(&p)                //
    fmt.Println(p.gender) //女
    //结构体指针1
    var p2 = new(person)
    p2.name = "理想"         //语法糖相当于    (*p2).name="理想"
    fmt.Printf("%T\n", p2) //*main.person
    //2结构体指针2
    //使用key-value初始化
    var p3 = person{
        name:   "元帅",
        gender: "男",
    }
    fmt.Printf("%#v\n", p3) //main.person{name:"元帅", gender:"男"}
    //使用列表形式初始化
    //值的顺序要和结构体定义时字段的顺序一致
    p4 := person{
        "王子",
        "男",
    }
    fmt.Printf("%#v\n", p4) //main.person{name:"王子", gender:"男"}
}

结构体和接收者

package main

import "fmt"

type dog struct {
    name string
}

//
func newDog(name string) dog {
    return dog{
        name: name,
    }
}

//方法是作用于特定类型的函数
//函数前面的是接收者
//接收者表示的是调用改类型方法的具体变量,多用类型名首字母表示
func (d dog) wang() {
    fmt.Printf("%s:汪汪汪\n", d.name)
}
func main() {
    d1 := newDog("zhoulinn")
    d1.wang()
}

方法接收者

go语言中的方法是一种工作用于特定类型变量的函数.这种特定类型变量叫做接收者.接收者的概念就类似于其他语言中的this或者self

方法的定义格式如下:

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

接收者类型:接收者类型和参数类似,可以是指类型和非指针类型

方法名,参数列表,返回参数:具体格式与函数定义相同

标识符

go语言中如果标识符首字母是大写的,就表示对外部包可见(暴露的,共有的)

值接收者和指针接收者的区别

package main

import "fmt"

type dog struct {
    name string
}
type person struct {
    age    int
    name   string
    gender string
}

//
func newDog(name string) dog {
    return dog{
        name: name,
    }
}
func (p person) guonian() {
    p.age++
}
func (p *person) zhenguonian() {
    p.age++
}

//方法是作用于特定类型的函数
//函数前面的是接收者
//接收者表示的是调用改类型方法的具体变量,多用类型名首字母表示
func (d dog) wang() {
    fmt.Printf("%s:汪汪汪\n", d.name)
}
func main() {
    d1 := newDog("zhoulinn")
    d1.wang()
    var p1 person
    p1.guonian()
    fmt.Println(p1.age) //还是0没有改变
    p1.zhenguonian()    //相当于把内存地址传入了
    fmt.Println(p1.age) //值为1 成功的改变了
}

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

  • 需要修改接收者的值
  • 接收者是拷贝代价比较大的打对象
  • 保证一致性,如果有某个方法

任意类型方法

在go语言中接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法.

package main

import "fmt"

type myInt int

func (i myInt) hello() {
    fmt.Println("我是一个int")
}
func main() {
    var m myInt
    m = 10    //不能写成m:=10
    m.hello() //我是一个int
}

结构体的传递

go语言中对象的赋值是直接将整个对象进行赋值的,而不是像其他语言一样赋值对象的内存地址

package main

import "fmt"

type obj struct {
    one string
    two int
}

func main() {
    var o obj
    o.one = "name"
    c := o
    o.one = "name2"
    fmt.Println(o, c) //{name2 0} {name 0}
}

匿名字段

相同类型只能出现一次

package main

import "fmt"

type person struct {
    string
    int
}

func main() {
    p1 := person{
        "666",
        666,
    }
    fmt.Println(p1.string, p1.int) //666 666
}

嵌套结构体

package main

import "fmt"

type person struct {
    name string
    age  int
}
type company struct {
    size int
    ren  person
}

func main() {
    p1 := person{
        name: "周琳",
        age:  9000,
    }
    c1 := company{
        size: 2,
        ren: person{ //也可以直接写p1
            name: "name1",
            age:  5,
        },
    }
    fmt.Println(p1)
    fmt.Println(c1)
}

继承与匿名结构体

go语言使用结构体也可以实现其他语言的继承

package main

import "fmt"

type animal struct {
    name string
}

//给animal实现一个移动的方法
func (a animal) move() {
    fmt.Printf("%s会动\n", a.name)
}

//狗
type dog struct {
    feet   uint8
    animal //这样就实现了继承
}

func (d dog) wang() {
    fmt.Println("旺旺", d.name) //如果没有找到d.name这个属性会去dog中的animal中找
}

func main() {
    d1 := dog{
        animal: animal{name: "zzz"},
        feet:   4,
    }
    d1.animal.move() //也可以这样用
    d1.move()        //效果一样
    fmt.Println(d1)  //{4 {zzz}}
}

结构体与json

1序列化 把go语言中的结构体变量->json格式的字符串

2反序列化 json格式的字符串->gio语言中能够识别的结构体变量

package main

import (
    "encoding/json"
    "fmt"
)

type person struct {
    Name string //格式化的功能使用json包中的Marshal方法做的,所以需要被拿到必须大写
    Age  int    `json:"age"` //如果用json解析的时候就设置成小写
}

func main() {
    p1 := person{
        Name: "zl",
        Age:  9000,
    }
    b, err := json.Marshal(p1) //序列化
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(b))
    //饭序列化
    str := `{"Name":"zl","Age":9000}`
    var p2 person
    json.Unmarshal([]byte(str), &p2) //需要让函数修改必须传入指针,是为了让函数内部去修改p2的值
    fmt.Println(p2)                  //{zl 9000}

}
Last modification:April 22, 2022
如果觉得我的文章对你有用,请随意赞赏