前言
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVGwRsQZ-1681290726009)(../images/Pasted%20image%2020230411223336.png)]](https://img-blog.csdnimg.cn/a6580b8e36f541dbb101055d6c87799c.png)
Go也有面向对象
面向对象引入:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6CIRyUG-1681290726009)(../images/Pasted%20image%2020230411223356.png)]](https://img-blog.csdnimg.cn/5179bc99180a420c968823cb1b0e7cb2.png)
用面向对象好啊
结构体定义
GO中的结构体和其他语言中的class是同一个等级的
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0uv76RMy-1681290726009)(../images/Pasted%20image%2020230411223747.png)]](https://img-blog.csdnimg.cn/251cb95e24154aceb742bae8f5f60f31.png)
这个就懒得写了 , 直接贴一个
内存分析
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SVvJ4k8g-1681290726010)(../images/Pasted%20image%2020230411223916.png)]](https://img-blog.csdnimg.cn/0aa0fb0fc10e46ab902c555408390957.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)]](https://img-blog.csdnimg.cn/2a2e9dbb86884bf6862912a94cf2b6c9.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)]](https://img-blog.csdnimg.cn/019f242541d4422eb589062e59ae373f.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)]](https://img-blog.csdnimg.cn/5adb99cf77cf4d659c0df09bd62cdd0c.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)]](https://img-blog.csdnimg.cn/b4f8c0348db34a19bacba17d51290973.png)
类似Java中的toString方法
在定义结构体的时候,可以把这个方法定义好,常用来作为输出结构体信息的方法
跨包创建结构体示例
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l7v4oOTz-1681290726012)(../images/Pasted%20image%2020230411234838.png)]](https://img-blog.csdnimg.cn/71e814e693bf40299e53b6b128d7190a.png)
这里的结构体名首字母大写,字段名首字母也要大写,否则没法被外部包访问.
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wncSHKcn-1681290726013)(../images/Pasted%20image%2020230411234936.png)]](https://img-blog.csdnimg.cn/e3cc539e3d424cd8a7388b73bd9ec505.png)
工厂模式
上面我们知道,要外部包访问结构体,结构体名首字母必须大写,但是如果小写了,我还是想访问,怎么办?
这里就出现了工厂模式
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMP6ug4P-1681290726014)(../images/Pasted%20image%2020230411235633.png)]](https://img-blog.csdnimg.cn/160132787f894966b6ae4382fe86b464.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KETbZFCE-1681290726014)(../images/Pasted%20image%2020230411235619.png)]](https://img-blog.csdnimg.cn/ce56007793a84c51b64764f62e974df2.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)]](https://img-blog.csdnimg.cn/25f58c1550144f01b1d3ba3c3499afb9.png)
继承:
多个结构体存在相同属性,可以吧这些结构体中抽象出一个结构体,该结构体中定义这些相同的字段,其他结构体就可以不用重复写这些字段了.
在GO中,其他结构体不用重新定义这些属性和方法了,只需要嵌套一个匿名结构体就行.
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NLATnRUq-1681290726014)(../images/Pasted%20image%2020230412154541.png)]](https://img-blog.csdnimg.cn/edbe9f33718041be99c5b55dba105e01.png)
这些继承,父类,子类被Go语言弱化了,但是写代码过程中思维要清楚
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UGJ9Ls2Q-1681290726015)(../images/Pasted%20image%2020230412154855.png)]](https://img-blog.csdnimg.cn/47f93247eb934ce59547f7a272e856cb.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)]](https://img-blog.csdnimg.cn/c7bb1c0690584414b5bd6bb69b0e30fe.png)
注意事项:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TQxnyBqa-1681290726015)(../images/Pasted%20image%2020230412160226.png)]](https://img-blog.csdnimg.cn/8399582693654383b8a71adf51279d3f.png)
其实都是比较好理解的,就不做code了
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ODHZclfy-1681290726015)(../images/Pasted%20image%2020230412160356.png)]](https://img-blog.csdnimg.cn/2d605a596ba9438bb3e59fc2fc608480.png)
第五个如果不做区分是会报错的
第六个
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hK9BbGeT-1681290726015)(../images/Pasted%20image%2020230412160657.png)]](https://img-blog.csdnimg.cn/ab9b8f8c82fd4b649a0f2962c42c8d43.png)
组合类型
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YwMisTA1-1681290726015)(../images/Pasted%20image%2020230412160948.png)]](https://img-blog.csdnimg.cn/5a6a9dab3990422695c002a810b2b500.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)]](https://img-blog.csdnimg.cn/fc1d6b478e174456b6a6a86613871ef8.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)]](https://img-blog.csdnimg.cn/40ce62a679864ceaa5403778cbef46e7.png)
-
多态数组
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bemwcyeH-1681290726016)(../images/Pasted%20image%2020230412165214.png)]](https://img-blog.csdnimg.cn/07d8b3cf65f5447cb973eaeeb7272a9e.png)
断言
什么是断言:
Go语言里面有一个语法,可以直接判断是否是该类型的变量:value , ok = element.(T), 这里的value就是变量的值, ok是一个bool类型, element是interface变量, T是断言的类型


















