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

自定义类型和类型别名
byteunit8runeint32
//自定义类型
// 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)
方法和接受者
方法method接受者(Receiver)thisself函数方法
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:结构体+函数

小练习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("---------------")  
        }
    }
}