Go语言学习笔记——面向对象

面向对象

结构体

结构体和结构体变量的区别和联系

  1. 结构体是自定义的数据类型,代表一类事物
  2. 结构体变量(实例)是具体的,实际的,代表一个具体变量

结构体变量内存布局

type Cat struct{
    
    
    Name string
    Age int
    Color string
    Hobby string
}

var cat1 Cat
// 结构体是一个值类型

在这里插入图片描述

结构体声明

/* type 结构体名称 struct{
    field1 type
    field2 type
} */
// 例子
type Student struct {
    
    
    Name string
    Age int
    Score float32
}

字段

  1. 结构体字段 == 属性 == field
  2. 字段是结构体的一个组成部分,一般是基本数据类型,数组,也可以是引用类型
  3. 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值,使用slice和map时必须先make
  4. 不同结构体变量的字段是独立的,互不影响,一个结构体变量字段的更改,不影响另外一个

创建结构变量

// 方式一:直接声明
var person Person
// 方式二:类型推导
person := Person{
    
    }
// 方式三:用new
var person *Person = new(Person)
// 因为person是一个指针,因此给字段赋值方式
person.Name = "smith"// 实质写法是(*person).Name = "smith"
// 方式四:用&符
var person *Person = &Person{
    
    }
// 因为person是一个指针,因此给字段赋值方式
person.Name = "smith"// 实质写法(*person).Name = "smith"

内存分配机制

结构体使用注意事项

  1. 结构体中所有的字段在内存中是连续的

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

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

    type Student struct{
          
          
        Name string
        Age int
    }
    type Stu Student
    
  4. struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化

抽象

将一类事物的共有的属性(字段)和行为(方法)提取出来,形成一个物理模型

// Account.go
package main

import "fmt"

type Account struct {
    
    
	AccountNo string
	Pwd       string
	Banlance  float64
}

func (account *Account) Deposite(money float64, pwd string) {
    
    
	if pwd != account.Pwd {
    
    
		fmt.Println("密码错误!")
		return
	}
	if money <= 0 {
    
    
		fmt.Println("输入的金额错误")
		return
	}
	account.Banlance += money
}

func (account *Account) WithDraw(money float64, pwd string) {
    
    
	if pwd != account.Pwd {
    
    
		fmt.Println("密码错误!")
		return
	}
	if money <= 0 || money > account.Banlance {
    
    
		fmt.Println("输入的金额错误")
		return
	}
	account.Banlance -= money
}

func (account *Account) Query(pwd string) {
    
    
	if pwd != account.Pwd {
    
    
		fmt.Println("密码错误!")
		return
	}
	fmt.Printf("你的账号为%v,余额%v\n", account.AccountNo, account.Banlance)
}

func main() {
    
    
	// 测试
	account := Account{
    
    
		AccountNo: "yfq111111",
		Pwd:       "666666",
		Banlance:  100,
	}
	account.Query("666666")
	account.Deposite(100, "666666")
	account.WithDraw(50, "666666")
	account.Query("666666")
}

封装

把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作

好处

  1. 隐藏实现细节
  2. 可以对数据进行验证,保证安全合理

如何体现封装

  1. 对结构体中的属性进行封装
  2. 通过方法,包实现封装

实现步骤

  1. 将结构体,字段(属性)的首字母小写(不能导出了,其他包不能使用,类似private)
  2. 将结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数
  3. 提供一个首字母大写的Set方法(类似其他语言的public),用于对属性判断并赋值
  4. 提供一个首字母大写的Get方法(类似其他语言的public),用于获取属性的值
// personal.go
package model

import "fmt"

type person struct {
    
    
	Name string
	age  int
	sal  float64
}

// 工厂模式的函数,相当于构造函数
func NewPerson(name string) *person {
    
    
	return &person{
    
    
		Name: name,
	}
}

// 为了访问age和sal,编写一堆SetXxx的方法和GetXxx的方法
func (p *person) SetAge(age int) {
    
    
	if age > 0 && age < 150 {
    
    
		p.age = age
	} else {
    
    
		fmt.Println("年龄范围不正确..")
	}
}

func (p *person) GetAge(age int) int {
    
    
	return p.age
}

继承

继承可以解决代码复用,提高了代码的扩展性和维护性,让编程更加靠近人类思维。Go是利用匿名结构体来实现继承的。

语法

type Goods struct{
    
    
    Name string
    Price int
}

type Book struct{
    
    
    Goods // 这里就是嵌套匿名结构体Goods
    Writer string
}

细节

  1. 结构体可以使用嵌套匿名结构体所有的字段和方法
  2. 匿名结构体字段访问可以简化
  3. 当结构体和匿名结构体有相同的字段或者方法是,编译器采用就近访问原则访问,,如希望访问匿名结构体的字段和方法,可以通过匿名机构提名来区分
  4. 结构体嵌入两个或多个匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字
  5. 如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方式时,必须带上结构体的名字
  6. 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值

多重继承

如果一个struct嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承

  1. 如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分
  2. 为了保证代码的简洁性,建议不要使用多重继承

猜你喜欢

转载自blog.csdn.net/weixin_45976751/article/details/127565656