golang学习笔记第三部分:12.golang的面向对象编程1
23、go的面向”对象“编程
1、结构体
自定义数据类型,支持方法定义。go语言面向对象编程特性的实现。 结构体是值类型。 声明: type 名称 struct { field1 type field2 type }
- 名称首字母大写,可以被其他包引用,小写是私有
- 字段可以是基础类型,也可以是引用类型
- 字段类型是指针、切片、map时,默认值是nil,使用前需要先make,因为结构体是值类型,字段是引用类型时,默认并没有分配内存空间
- 不同结构体变量的字段是独立的,相互不影响
- 结构体的所有字段在内存中是连续的,可以通过数据类型长度加减直接操作内存读取数据
- 结构体是用户自定义的类型,和其他类型转换时需要有完全相同的字段(名称、数量、类型)
- 结构体进行type重新定义,golang认为是新的数据类型,拥有相同的字段信息
- 在结构体的每个字段上,可以写一个tag,该tag可以通过反射机制获取,常见的场景是序列化和反序列化,比如json ;还有在gorm等库中,也是利用到tag
2、方法
- 方法是数据的行为
- golang的方法是作用在指定的数据类型上的,因此自定义类型都可以有方法,而不仅仅是struct
- 方法的声明 func (receiver type) methodName(参数列表) (返回值列表){ 方法体 return 返回值 }
- receiver方法和type类型绑定,receiver类似python的self,java的this,实际开发中常规写法(cat Cat)
- 方法的调用 var a1 A // 实例化 a1.test() // 只能通过A类型的实例调用
- 方法的调用、传参机制和函数基本一样,区别是方法调用时,会将调用方法的实例,当做实参也传递给方法,注意结构体类型的方法是值传递
- 在开发中,为了避免值传递,提高效率,方法常绑定结构体指针
- 如果一个类型实现了String()方法,那么fmt.Println打印时,会默认输出String()方法
- 声明方法时的绑定方式,决定了是值传递还是引用传递(receiver type);(receiver *type)
package main
import "fmt"
type Account struct {
//属性
AccountNo string
Pwd string
Balance float64
}
func (account *Account) Deposit(money float64, pwd string) {
//行为:存款
if pwd != account.Pwd {
fmt.Println("密码输入错误!")
return
}
if money <= 0 {
fmt.Println("输入的金额错误!")
return
}
account.Balance += money
fmt.Printf("存款%v成功!\n", money)
}
func (account *Account) WithDraw(money float64, pwd string) {
//行为:取款
if pwd != account.Pwd {
fmt.Println("密码输入错误!")
return
}
if money <= 0 || money > account.Balance {
fmt.Println("输入的金额错误!")
return
}
account.Balance -= money
fmt.Printf("取款%v成功!\n", money)
}
func (account *Account) Query(pwd string) {
//行为:查询余额
if pwd != account.Pwd {
fmt.Println("密码输入错误!")
return
}
fmt.Printf("账号%v的余额为%v\n", account.AccountNo, account.Balance)
}
func main() {
Tangshan := Account{
AccountNo: "1001",
Pwd: "123",
Balance: 100.00,
}
Tangshan.Deposit(100, "123")
Tangshan.WithDraw(200, "123")
Tangshan.Query("123")
}
3、go面向对象编程一
- 声明结构体,确定名称
- 编写字段
- 编写结构体的方法
- 创建结构体时指定字段值:
1)有几种方式,
var a type = type{按顺序写入每个字段值};
var b type = type{ field1 : value1, field2 : value2, } // 注意逗号
2)建议使用第二种方式,理由是第一种方式在结构体字段修改后,很容易由于顺序改变而出现类型错误 - 返回结构体指针:var a = &type{参考上面2种写法};a := &type{xxx}
package main
import "fmt"
type PUpi1 struct {
Name string
Age int
Score int
}
func (u *PUpi1) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 成绩%v\n", u.Name, u.Age, u.Score)
}
// SetScore 嵌入匿名结构体,称为继承,如果命名,则称为组合
func (u *PUpi1) SetScore(score int) {
u.Score = score
}
// testing
func (u *PUpi1) testing() {
fmt.Println("1 stu testing")
}
// GUtil 嵌入匿名结构体
type GUtil struct {
PUpi1
Skill string
}
// testing 即使方法名相同,那也是2个不同结构体的,相互不影响
func (u *GUtil) testing() {
fmt.Println("2 stu testing")
}
func main() {
var pupil = PUpi1{
Name: "james",
Age: 10,
}
pupil.testing()
pupil.SetScore(90)
pupil.ShowInfo()
var g1 = GUtil{}
g1.Name = "tom" // 等同于gutil.PUpil.Name
g1.Age = 20
g1.testing()
g1.SetScore(90)
g1.ShowInfo()
pupil.testing()
var g2 = GUtil{
PUpi1: PUpi1{ // 嵌入匿名结构体赋值时,需要带上引用
Name: "g2",
Age: 18,
Score: 99,
},
Skill: "song",
}
g2.ShowInfo()
}