Go面向对象

文章介绍了Go语言中的面向对象特性,包括结构体的定义与实例化(五种方式)、结构体之间的转换、方法的绑定(值方法与指针方法)、接口的定义与实现以及多态的概念。通过示例展示了如何利用接口实现多态性,并提到了封装、继承和工厂模式等概念。
摘要由CSDN通过智能技术生成

前言

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVGwRsQZ-1681290726009)(../images/Pasted%20image%2020230411223336.png)]

Go也有面向对象

面向对象引入:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6CIRyUG-1681290726009)(../images/Pasted%20image%2020230411223356.png)]

用面向对象好啊

结构体定义

GO中的结构体和其他语言中的class是同一个等级的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0uv76RMy-1681290726009)(../images/Pasted%20image%2020230411223747.png)]

这个就懒得写了 , 直接贴一个

内存分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SVvJ4k8g-1681290726010)(../images/Pasted%20image%2020230411223916.png)]

当实例化一个结构体的时候,分配一份内存空间.

结构体实例的创建

package main  
  
import "fmt"  
  
type Teacher struct {  
   Name   string  
   age    int  
   School string  
}  
  
func main() {  
   // 实例化方法一:  
   var t1 Teacher // var a int  
   fmt.Println(t1)  
   // 实例化方法二:  
   var t2 Teacher = Teacher{  
      Name:   "simple",  
      age:    0,  
      School: "ssss",  
   }  
   // 实例化方法三:  
   var t3 Teacher = Teacher{}  
   t3.age = 10  
   //实例化方法四:  
   //t4是一个指针,t4其实指向的就是地址, 应该给这个地址的指向的对象的字段赋值.  
   var t4 *Teacher = new(Teacher)  
   (*t4).Name = "simple"  
   (*t4).age = 24  
   // 为了符合程序员的编程习惯,go提供了简单的赋值方式  
   t4.Name = "sim"  
   t4.age = 25  
   // 实例化方式五:  
   var t5 *Teacher = &Teacher{}  
   (*t5).Name = "simple"  
   (*t5).age = 11  
   t5.School = "ajglajgl"  
}

五种方式的实例化

结构体之间的转换:

  • 结构体是用户自定义的类型, 和其他类型进行转换时需要有完全相同字段(名字,个数, 类型)
package main  
  
import "fmt"  
  
type Student struct {  
   age int  
}  
  
type Person struct {  
   age int  
}  
  
func main() {  
   var s Student = Student{age: 10}  
   var p Person = Person{age: 10}  
   s = Student(p)  
   fmt.Println(s)  
   fmt.Println(p)  
  
}
  • 结构体进行type重新定义(相当于重新取别名),Go认为是新的数据类型,但是相互间可以强转
package main  
  
import "fmt"  
  
type Student struct {  
   age int  
}  
  
type Stu Student  
  
func main() {  
   var s1 Student  
   var s2 Stu  
   s1 = Student(s2)  
   fmt.Println(s1)  
   fmt.Println(s2)  
   }

也就是都要在结构体完全相同的情况下可以进行强转

方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4d78CYou-1681290726010)(../images/Pasted%20image%2020230411231033.png)]

上面的结构体绑定方法是用的值传递
用视频中的例子来解释

package main  
  
type Boy struct {  
   Name string  
}  
  
func (b Boy) tets() {  
   b.Name = "complex"  
   println("方法")  
   println(b.Name)  
}  
  
func main() {  
   var boy Boy  
   boy.Name = "simple";  
   boy.tets()  
   println(boy.Name)  
  
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JdjExHDC-1681290726011)(../images/Pasted%20image%2020230411231706.png)]

在方法中去更改Name的值,只是更改了传给方法的结构体的副本的值, 但是原本的结构体并没有被更改.

结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式.

如果程序员希望在方法中,改变了结构体中字段的值, 原来的结构体中的值也被改变,就需要用引用传递的方式来绑定变量了

其实上面的也就是值方法和指针方法的区别
https://zhuanlan.zhihu.com/p/101363361
下面就将上面的值方法改陈指针方法:

package main  
  
type Boy struct {  
   Name string  
}  
  
func (b *Boy) tets() {  
   b.Name = "complex"  
   println("方法")  
   println(b.Name)  
}  
  
func main() {  
   var boy Boy  
   boy.Name = "simple"  
   boy.tets()  
   println(boy.Name)  
  
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2cPOrj1b-1681290726012)(../images/Pasted%20image%2020230411232321.png)]

指定数据类型的方法

package main  
  
type integer int  
  
func (i integer) print() {  
   print("wuwuwuwu")  
}  
  
func main() {  
   var i integer = 1  
   i.print()  
   }

方法的访问控制规则

是和函数一样的,如果首字母小写,只能在本包进行访问, 如果首字母大写了,就可以在其他包中进行访问.

方法的trick

如果一个类型实现了String()这个方法, 那么fmt.Println默认会调用这个变量的String()进行输出.

package main  
  
import "fmt"  
  
type Animal struct {  
   name string  
   age  int  
}  
  
func (a *Animal) String() string {  
   str := fmt.Sprintf("name = %v , age = %v", a.name, a.age)  
   return str  
}  
func main() {  
   animal := Animal{  
      name: "cat",  
      age:  11,  
   }  
   //传入地址,如果绑定了String方法就会自动调用了  
   fmt.Println(&animal)  
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xab00OOr-1681290726012)(../images/Pasted%20image%2020230411233517.png)]

类似Java中的toString方法
在定义结构体的时候,可以把这个方法定义好,常用来作为输出结构体信息的方法

跨包创建结构体示例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l7v4oOTz-1681290726012)(../images/Pasted%20image%2020230411234838.png)]

这里的结构体名首字母大写,字段名首字母也要大写,否则没法被外部包访问.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wncSHKcn-1681290726013)(../images/Pasted%20image%2020230411234936.png)]

工厂模式

上面我们知道,要外部包访问结构体,结构体名首字母必须大写,但是如果小写了,我还是想访问,怎么办?
这里就出现了工厂模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMP6ug4P-1681290726014)(../images/Pasted%20image%2020230411235633.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KETbZFCE-1681290726014)(../images/Pasted%20image%2020230411235619.png)]

可是无论怎么样,在结构体中,字段名的首字母都要大写.
通过调用函数来返回结构体指针.

封装的引入

什么是封装:

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

封装的好处:

  • 隐藏实现细节
  • 提高对数据的验证,保证安全性

如何实现:

  • 建议将结构体,字段的首字母小写(其他包就不能使用了,类似于private)
  • 给结构体所在的包提供一个工厂模式的函数,首字母小写(类似一个构造函数)
  • 提供一个首字母大写的Set方法(类似其他语言的public),用于对属性的判断和赋值
  • 提供一个首字母大写的Get方法(类似其他语言的public),用于获取属性的值
    Go的封装不是很严格
package student  
  
type person struct {  
   Name string  
   age  int //其他包不能访问  
}  
  
// 定义工厂模式的函数,相当于构造器  
func NewPerson(name string) *person {  
   return &person{  
      Name: name,  
   }  
}  
  
// 定义Set和Get方法对字段进行封装,  
func (p *person) SetAge(age int) {  
   if age > 0 && age < 120 {  
      p.age = age  
  
   } else {  
      print("输入不合法")  
   }  
  
}  
  
func (p *person) GetAge() int {  
   return p.age  
}

这里的person中的两个字段,一个是首字母大写的, 一个是全小写的,Name就可以用工厂模式来弄,而age就只能通过方法来弄了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VvFhwMme-1681290726014)(../images/Pasted%20image%2020230412154320.png)]

继承:

多个结构体存在相同属性,可以吧这些结构体中抽象出一个结构体,该结构体中定义这些相同的字段,其他结构体就可以不用重复写这些字段了.
在GO中,其他结构体不用重新定义这些属性和方法了,只需要嵌套一个匿名结构体就行.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NLATnRUq-1681290726014)(../images/Pasted%20image%2020230412154541.png)]

这些继承,父类,子类被Go语言弱化了,但是写代码过程中思维要清楚
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UGJ9Ls2Q-1681290726015)(../images/Pasted%20image%2020230412154855.png)]

上面就是匿名结构体实现继承的图解释.

代码展示一下:

package main  
  
import "fmt"  
  
type Animal struct {  
   Age    int  
   Weight float32  
}  
  
// Animal的方法:  
func (a *Animal) Shout() {  
   fmt.Println("叫")  
}  
  
func (a *Animal) ShowInfo() {  
   fmt.Println(a.Age, a.Weight)  
}  
  
//定义猫的结构体  
type Cat struct {  
   // 继承:用匿名结构体来做  
   Animal  
}  
  
//Cat 的方法  
  
func (c *Cat) Scratch() {  
   fmt.Println("猫的爪子")  
}  
  
func main() {  
   cat := &Cat{Animal{  
      Age:    10,  
      Weight: 10,  
   }}  
   cat.Animal.Age = 11  
   cat.Animal.Weight = 11  
   cat.Animal.Shout()  
   cat.Animal.ShowInfo()  
   cat.Scratch()  
   }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mwpxImUO-1681290726015)(../images/Pasted%20image%2020230412155533.png)]

注意事项:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TQxnyBqa-1681290726015)(../images/Pasted%20image%2020230412160226.png)]

其实都是比较好理解的,就不做code了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ODHZclfy-1681290726015)(../images/Pasted%20image%2020230412160356.png)]

第五个如果不做区分是会报错的

第六个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hK9BbGeT-1681290726015)(../images/Pasted%20image%2020230412160657.png)]

组合类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YwMisTA1-1681290726015)(../images/Pasted%20image%2020230412160948.png)]

这个组合类型不是继承关系

接口:

package main  
  
import (  
   "fmt"  
)  
  
//定义一个接口:接口一些规则,规范,某种能力  
//这里是抽象理解一个说你好的能力  
type Sayhello interface {  
   //声明一个没有实现的方法  
   Sayhello()  
}  
  
//接口的实现:定义一个结构体  
//中国人:  
type Chinese struct {  
}  
  
//实现接口的方法  
func (c *Chinese) Sayhello() {  
   fmt.Println("你好")  
}  
  
type English struct {  
}  
  
func (e *English) Sayhello() {  
   println("hi")  
}  
  
func main() {  
   chinese := &Chinese{}  
   chinese.Sayhello()  
   english := English{}  
   english.Sayhello()  
}
你好
hi

所以就是如果一个结构体实现了接口中的所有定义的方法,这个结构体就实现了这个接口.

看看这个接口可做什么?

package main  
  
import (  
   "fmt"  
)  
  
// 定义一个接口:接口一些规则,规范,某种能力  
// 这里是抽象理解一个说你好的能力  
type Sayhello interface {  
   //声明一个没有实现的方法  
   Sayhello()  
}  
  
// 接口的实现:定义一个结构体  
// 中国人:  
type Chinese struct {  
}  
  
// 实现接口的方法  
func (c *Chinese) Sayhello() {  
   fmt.Println("你好")  
}  
  
type English struct {  
}  
  
func (e *English) Sayhello() {  
   println("hi")  
}  
  
// 定义一个函数:用来专门向各个国家的人打招呼的函数,接收具备Sayhello接口的能力的变量  
func greet(s Sayhello) {  
   s.Sayhello()  
}  
  
func main() {  
   chinese := &Chinese{}  
   chinese.Sayhello()  
   english := &English{}  
   english.Sayhello()  
   greet(chinese)  
   greet(english)  
  
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ElnXUWTF-1681290726015)(../images/Pasted%20image%2020230412162342.png)]

接口是一个隐式的实现.
要求并不严格
接口的目的是为了让定义规范
其实也和下面的多态有关系

多态

基本介绍:

变量(实例)具有多种形态.面向对象的第三大特征, 在Go中,多态是通过接口来实现的.
可以按照统一的接口来调用不同的实现. 这时接口变量就呈现不同的形态.

案例

就是上面接口中的不同国家的人的打招呼

package main  
  
import (  
   "fmt"  
)  
  
// 定义一个接口:接口一些规则,规范,某种能力  
// 这里是抽象理解一个说你好的能力  
type Sayhello interface {  
   //声明一个没有实现的方法  
   Sayhello()  
}  
  
// 接口的实现:定义一个结构体  
// 中国人:  
type Chinese struct {  
}  
  
// 实现接口的方法  
func (c *Chinese) Sayhello() {  
   fmt.Println("你好")  
}  
  
type English struct {  
}  
  
func (e *English) Sayhello() {  
   println("hi")  
}  
  
// 定义一个函数:用来专门向各个国家的人打招呼的函数,接收具备Sayhello接口的能力的变量  
func greet(s Sayhello) {  
   s.Sayhello()  
}  
  
func main() {  
   chinese := &Chinese{}  
   english := &English{}  
   greet(chinese)  
   greet(english)  
  
}

接口体现多态特征

  • 多态参数:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ys1CHaj4-1681290726016)(../images/Pasted%20image%2020230412164853.png)]

  • 多态数组
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bemwcyeH-1681290726016)(../images/Pasted%20image%2020230412165214.png)]

断言

什么是断言:

Go语言里面有一个语法,可以直接判断是否是该类型的变量:value , ok = element.(T), 这里的value就是变量的值, ok是一个bool类型, element是interface变量, T是断言的类型

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值