Golang——结构体struct

Go语言中没有“类”的改变,不支持类的“继承”等面向对象概念。Go语言中通过结构体的内嵌再配合接口比面向对象更具有更高的扩展性和灵活性。

自定义类型和类型别名

自定义类型:新类型,可以基于内置的基本类型定义,也可以通过stuct定义;
类型别名:只存在代码编写过程中,编码编译字后就不存在,增加提高代码可读性;例如,byteunit8的类型别名,runeint32的类型别名。

//自定义类型
// NewInt 是一个新类型,具有int的特性
type NewInt int

//类型别名
type MyInt = int

func main() {
    var a NewInt
    fmt.Println(a)          //0
    fmt.Printf("%T\n", a)   //main.NewInt

    var b MyInt
    fmt.Println(b)          //0
    fmt.Printf("%T\n", b)   //int
} 

结构体

结构体struct是一种自定义类型,可以封装多个基本数据类型。
结构体是值类型。
结构体在内存中是连续在一块的。
结构体中字段大写字母开头表示可公开访问小写字母表示私有(仅在定义当前结构体的包中可访问)。

//创建新的类型需要用type关键字
type student struct {
    name string
    age int
    gender string
    hobby []string
}

func main() {

    var stu = student{
        name : "张三",
        age : 20,
        gender : "男",
        hobby : []string{"篮球", "足球", "乒乓球"},
    }
    fmt.Println(stu)   // {张三 20 男 [篮球 足球 乒乓球]}

    //采用.属性方式访问
    fmt.Println(stu.name)
    fmt.Println(stu.age)
    fmt.Println(stu.gender)
    fmt.Println(stu.hobby)

    //struct是值类型,如果初始化没有设置值,则属性是默认值
    var lisi = student{}
    fmt.Println(lisi)  //{ 0  []}

    //使用new关键字进行声明(实例化),得到结构体指针
    var wangwu = new(student)
    // (*wangwu).name
    wangwu.name = "王五"
    wangwu.age = 18
    fmt.Println(wangwu)  //&{王五 18  []}

    //声明(实例化),得到结构体指针
    var aaa = &student{}
    fmt.Println(aaa)     //&{ 0  []}
    
    //初始化
    //只用值的,需要所有属性都要初始化
    var bbb = student{
        "bbb",
        18,
        "男",
        []string{"a", "b"},
    }
    fmt.Println(bbb)   //{bbb 18 男 [a b]}

    //初始化,得到结构体指针
    //key-value格式,不需要所有的属性都初始化
    var ccc = &student{
        name : "ccc",
        age : 18,
    }
    fmt.Println(ccc)   //&{ccc 18  []}
    
}

同样类型的字段可以写在一行

type student struct {
    id int
    name,city string
}

自己实现构造函数

//创建新的类型需要用type关键字
type student struct {
    name string
    age int
    gender string
    hobby []string
}

//实现一个构造函数,习惯上new加类型名
func newStudent(name string, age int, gender string, hobby []string) *student {
    return &student{
        name: name,
        age: age,
        gender: gender,
        hobby: hobby,
    }
}

func main() {

    hobby := []string{"a", "b"}
    stu := newStudent("zhang", 18, "男", hobby)
    fmt.Println(stu.name)
    fmt.Println(stu.age)
    fmt.Println(stu.gender)
    fmt.Println(stu.hobby)
    
}

匿名结构体

    var user struct{Name string; Age int}
    user.Name = "张三"
    user.Age = 18
    fmt.Printf("%#v\n", user)

方法和接受者

Go语言中的方法method是一种作用于特定类型变量的函数。这种特定类型变量叫做接受者(Receiver),类似于其它语言中的this或者self
函数是谁都可以调用的。
方法就是某个具体的类型才能调用的函数。

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

其中:

  • 接受者变量:参数变量名在命名时候,官方建议使用接受者类型名的第一个小写字母,而不是self、this
  • 接受者类型:可以是指针类型和非指针类型,一般情况下都用指针类型;如果修改接受者中的值,必须用指针类型;

使用指针类型的接受者场景

  • 需要修改接受者中的值
  • 接受者是拷贝代价比较大对象
  • 保持一致性,如果有某个方法使用了指针接受者,那么其他的方法也应该使用指针接受者

注意:

  • 不能给系统类型添加方法
  • 不能给别的包定义的类型添加方法
package main

import "fmt"

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

//非指针类型接受者
func (s student) methodTest() {
    //非指针类型是不能改值
    s.age = 20
    fmt.Printf("学生的姓名是%s\n", s.name)
}

//指针类型接受者
func (s *student) methodTestUpdate() {
    s.age = 20
    fmt.Printf("学生的姓名是%s\n", s.name)
}

func main() {
    s := student{
        name: "aaa",
        age: 18,
    }
    s.methodTest()  //学生的姓名是aaa
    // 不能改值
    fmt.Printf("学生的年龄是%d\n", s.age)   //学生的年龄是18

    s2 := &student{
        name: "bbb",
        age: 18,
    }
    s2.methodTestUpdate()   //学生的姓名是bbb
    fmt.Printf("学生的年龄是%d\n", s2.age)  //学生的年龄是20
}

匿名字段

没有变量名,只有类型的字段
不推荐匿名字段的写法

package main

import "fmt"

//匿名字段,每个类型只能用一次,不建议写
type student struct {
    name string
    string
    int
}

func main() {

    s := student{
        name: "aaa",
        string: "222",
    }
    fmt.Println(s.name)
    fmt.Println(s.string) 
    fmt.Println(s.int) 

}

结构体嵌套

package main

import "fmt"

type address struct {
    province string
    city string
}

type student struct {
    name string
    age int
    addr address
}

func main() {

    s := student{
        name: "aaa",
        age: 20,
        addr : address{
            province: "山东",
            city: "烟台",
        },
    }
    fmt.Println(s)               //{aaa 20 {山东 烟台}}
    fmt.Println(s.name)          //aaa
    fmt.Println(s.addr.province) //山东
    fmt.Println(s.addr.city)     //烟台
}

嵌套匿名结构体

匿名字段支持简写,可以不加类型名
匿名字段冲突时,需要加上类型名,不能省略

package main

import "fmt"

type address struct {
    province string
    city string
}

type email struct {
    province string
    test string
}

type other struct {
    province string
    tmp string
}

type student struct {
    name string
    age int
    addr address
    //匿名字段
    email
    other
}

func main() {

    s := student{
        name: "aaa",
        age: 20,
        addr : address{
            province: "山东",
            city: "烟台",
        },
        email: email{
            province: "1",
            test: "2",
        },
        other: other{
            province: "3",
            tmp: "4",
        },
    }
    fmt.Println(s)               //{aaa 20 {山东 烟台} {1 2} {3 4}}
    fmt.Println(s.name)          //aaa
    fmt.Println(s.addr.province) //山东
    fmt.Println(s.addr.city)     //烟台

    //可省略类型名
    fmt.Println(s.test)          //2
    //匿名字段冲突时,需要加上类型名,不能省略
    // fmt.Println(s.province)   //ambiguous selector s.province
    fmt.Println(s.email.province) //1
    
}

结构体的“继承”

利用结构体嵌套、匿名字段,实现结构体的“继承”

package main

import "fmt"


type person struct {
    name string
}

func (p *person)dinner() {
    fmt.Printf("%s 要吃饭!\n", p.name)
}

type student struct {
    number int
    *person
}

func (s *student)study() {
    fmt.Printf("%s的学号是%d\n", s.name, s.number)
}

func main() {

    stu := student {
        number: 100,
        person: &person{
            name: "张三",
        },
    }
    fmt.Println(stu.name)
    fmt.Println(stu.number)
    stu.study()
    stu.dinner()
}

结构体与JSON序列化

序列化:把编程语言里面的数据转换成JSON格式的字符串
反序列化:把JONS格式的字符串转成编程语言中的对象

package main

import (
    "fmt"
    "encoding/json"
)

type Student struct {
    Id int `json:"id"` //通过tag实现json序列化该字段时的key
    Name string `json:"name"`
}


func main() {

    stu1 := &Student{
        Id: 1,
        Name: "张三",
    }

    fmt.Println("序列化:把编程语言里面的数据转换成JSON格式的字符串")
    v, err := json.Marshal(stu1)
    if err != nil {
        fmt.Println("JSON格式化出错!")
        fmt.Println(err)
        return
    }
    fmt.Println(v)                 //[]byte
    fmt.Println(string(v))         //把[]byte转换成string
    fmt.Printf("%#v\n", string(v)) //看真实的格式

    fmt.Println("反序列化:把JONS格式的字符串转成编程语言中的对象") 
    str := string(v)
    // str := "{\"Id\":1,\"Name\":\"张三\"}"
    var stu2 = &Student{}
    json.Unmarshal([]byte(str), stu2)
    fmt.Println(*stu2) 
    fmt.Printf("%T\n", stu2)  
}

输出结果是:
序列化:把编程语言里面的数据转换成JSON格式的字符串
[123 34 105 100 34 58 49 44 34 110 97 109 101 34 58 34 229 188 160 228 184 137 34 125]
{"id":1,"name":"张三"}
"{\"id\":1,\"name\":\"张三\"}"
反序列化:把JONS格式的字符串转成编程语言中的对象
{1 张三}
*main.Student

结构体tag

tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。例如定义json序列化时的tag。

type Student struct {
    Id int `json:"id"` //通过tag实现json序列化该字段时的key
    Name string `json:"name"`
}

小练习

小练习1:结构体+函数

package main

import (
    "fmt"
    "os"
)

//使用函数实现一个简单的图书管理系统
//每本书都有书名、作者、价格、上架信息
//用户可以在控制台添加书籍、修改书籍信息、打印所有的书籍列表、退出操作

type book struct {
    title string
    author string
    price float32
    publish bool
}

//定义一个book指针的切片

// var bookList = make([]book, 0, 200)
var bookList = make([]*book, 0, 200)


// 获取用户输入
func userInput() (title string, author string, price float32, publish bool){
    fmt.Print("请输入标题:")
    fmt.Scanln(&title)
    fmt.Print("请输入作者:")
    fmt.Scanln(&author)
    fmt.Print("请输入价格:")
    fmt.Scanln(&price)
    fmt.Print("请输入出版信息[true|false]:")
    fmt.Scanln(&publish)
    return
}

// 添加书籍
func addBook(){
    var (
        title string
        author string
        price float32
        publish bool
    )
    title, author, price, publish = userInput()

    newBook := book{title, author, price, publish}
    // bookList = append(bookList, newBook)
    bookList = append(bookList, &newBook)
    fmt.Println("添加书籍成功!")
}

//修改书籍
func updateBook(){
    var (
        updateTitle string
        existsFlag bool
        title string
        author string
        price float32
        publish bool
    )

    fmt.Print("请输入要修改的原书籍名称:")
    fmt.Scanln(&updateTitle)

    existsFlag = false
    for _, bookInto := range bookList{
        if bookInto.title == updateTitle {
            existsFlag = true
        }
    }
    if !existsFlag {
        fmt.Println("书籍不存在!")
        return
    }


    title, author, price, publish = userInput()


    // newBook := book{title, author, price, publish}
    // for index, bookInto := range bookList{
    //  if bookInto.title == updateTitle {
    //      bookList[index] = newBook
    //      fmt.Println("书籍修改成功!")
    //  }
    // }

    for _, bookInto := range bookList{
        if bookInto.title == updateTitle {
            p := bookInto
            p.title = title
            p.author = author
            p.price = price
            p.publish = publish
            fmt.Println("书籍修改成功!")
        }
    }
    
}


//展示书籍列表
func displayBook(){
    if len(bookList) == 0 {
        fmt.Println("无书籍!")
        return 
    }
    for index, bookInto := range bookList{
        fmt.Printf("序号:%v\n", index+1)
        fmt.Printf("标题:%s\n", bookInto.title)
        fmt.Printf("作者:%s\n", bookInto.author)
        fmt.Printf("价格:%0.2f\n", bookInto.price)
        fmt.Printf("标题:%t\n", bookInto.publish)
    }
}

func showMenu(){
    fmt.Println("1. 添加书籍")
    fmt.Println("2. 修改书籍信息")
    fmt.Println("3. 展示所有书籍")
    fmt.Println("4. 退出")
}

func main() {

    for {
        showMenu()
        fmt.Print("请输入操作序号:")
        var opt string
        fmt.Scanln(&opt)
        switch opt {
        case "1":
            fmt.Println("--添加书籍操作--")
            addBook()
            fmt.Println("---------------")  
        case "2":
            fmt.Println("--修改书籍操作--")
            updateBook()
            fmt.Println("---------------")  
        case "3":
            fmt.Println("--展示书籍操作--")
            displayBook()
            fmt.Println("---------------")  
        case "4":
            fmt.Println("--离开操作--")
            os.Exit(0)
        default :
            fmt.Println("错误的操作选项!")
            fmt.Println("---------------")  
        }
    }
    
} 

小练习2:结构体+方法

package main

import (
    "fmt"
    "os"
)
//使用方法实现一个简单的学生管理系统
//每本书都有学号、姓名、年龄、班级
//用户可以在控制台添加学生、修改学生、删除学生、展示学生列表、退出操作

type Student struct {
    id int
    name string
    age int
    class string
}

//构造函数
func NewStudent(id int, name string, age int, class string) *Student {
    return &Student{
        id: id,
        name: name,
        age: age,
        class: class,
    }
}

type StuMng struct {
    stulist []*Student
}

func NewStuMng() *StuMng {
    return &StuMng{
        stulist: make([]*Student, 0, 100),
    }
}

//添加学生
func (s *StuMng)addStudent(stu *Student) {
    s.stulist = append(s.stulist, stu)
    fmt.Println("添加学生成功!")

}

//判断学号是否存在
func (s *StuMng)stuIdIsExists(id int) bool {
    existsFlag := false
    for _, value := range s.stulist {
        if value.id == id {
            existsFlag = true
        }
    }
    return existsFlag
}

//修改学生
func (s *StuMng)updateStudent(stu *Student) {
    if !s.stuIdIsExists(stu.id) {
        fmt.Println("该学生学号不存在!")
        return
    }
    for index, value := range s.stulist {
        if value.id == stu.id {
            s.stulist[index] = stu
        }
    }
    fmt.Println("修改学生成功!")

}

//删除学生
func (s *StuMng)deleteStudent(id int) {
    if !s.stuIdIsExists(id) {
        fmt.Println("该学生学号不存在!")
        return
    }
    for index, value := range s.stulist {
        if value.id == id {
            s.stulist = append(s.stulist[:index], s.stulist[index+1:]...)
        }
    }
    fmt.Println("删除学生成功!")

}

//展示所有学生
func (s *StuMng)displayStudentList() {
    if len(s.stulist) == 0 {
        fmt.Println("无")
        return
    }
    for _, stu := range s.stulist {
        fmt.Printf("学号:%d,姓名:%s,年龄:%d,班级:%s \n", stu.id, stu.name, stu.age, stu.class)
    }
}

//操作菜单
func showMenuOpt(){
    fmt.Println("1. 添加学生")
    fmt.Println("2. 修改学生")
    fmt.Println("3. 删除学生")
    fmt.Println("4. 展示所有学生")
    fmt.Println("5. 退出")
}

// 获取用户输入
func userInputContent() *Student{
    var (
        id int
        name string
        age int
        class string
    )
    fmt.Print("请输入学号:")
    fmt.Scanln(&id)
    fmt.Print("请输入姓名:")
    fmt.Scanln(&name)
    fmt.Print("请输入年龄:")
    fmt.Scanln(&age)
    fmt.Print("请输入班级:")
    fmt.Scanln(&class)
    return NewStudent(id, name, age, class)
}

func main() {

    sg := NewStuMng()
    
    for {
        showMenuOpt()
        fmt.Print("请输入操作序号:")
        var opt string
        fmt.Scanln(&opt)
        switch opt {
        case "1":
            fmt.Println("--添加学生操作--")
            stu := userInputContent()
            sg.addStudent(stu)
            fmt.Println("---------------")  
        case "2":
            fmt.Println("--修改学生操作--")
            stu := userInputContent()
            sg.updateStudent(stu)
            fmt.Println("---------------")  
        case "3":
            fmt.Println("--删除学生操作--")
            var id int
            fmt.Print("请输入学号:")
            fmt.Scanln(&id)
            sg.deleteStudent(id)
            fmt.Println("---------------")  
        case "4":
            fmt.Println("--查询所有学生操作--")
            sg.displayStudentList()
            fmt.Println("---------------")  
        case "5":
            fmt.Println("--离开操作--")
            os.Exit(0)
        default :
            fmt.Println("错误的操作选项!")
            fmt.Println("---------------")  
        }
    }
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注