面向对象编程
回顾
-
指针
-
指向,特殊的变量,存的是一个地址 &
-
定义指针 *
-
数组指针
-
指针函数
-
指针作为参数 (值传递问题)
-
-
结构体
-
结构体定义 type 名字 struct
-
结构体指针 new(User) 指针对象
-
匿名结构体(没有名字的结构体)
-
匿名字段
-
结构体嵌套(Person Address)
-
OOP思想
==Go语言不是面向对象的语言,只是让大家理解一些面向对象的思想,通过一些方法来模拟面向对象==
物以类聚:随着发展变得越来越庞大,我们需要学会归类。
语句多了:我们把一些功能性代码,或者一些完成相同类型作业的代码聚集到一起。函数就出现了
变量多了:相同类型的变量,功能相近的变量放在一起,组合成一个类,结构体就出现了
方法多了、变量多了、结构体也多了。 于是类(伪概念)就出现了。
类:类:模板 --> 具体的对象 (对象)
开发的时候,先开发类,具体使用的时候才是我们的具体对象实例化。
面向过程思维
-
线性思维
-
1/2/3/4/5
-
-
空天站(复杂,无法使用线性思维完成)
面向对象思维
-
分类的思维模式,首先思考这个问题怎么解决,细分成一些不同的类。然后再对类进行单独思考
-
空天站
-
地面基地
-
军方
-
科研
-
编码
-
软件
-
点火(---一行代码)
-
-
硬件
-
-
-
空天基地
-
-
公司:
1架构师 CTO:拿到业务,分析需求(分类),架构软件(选型),写接口 面向对象解决问题
10程序员:实现这些接口业务(CRUD) 面向过程解决问题
面向对象:可以解决很多复杂的问题,将现实中的问题,拟人化(用人的思维方式去解决),适合多人协作。
面向过程:适合解决简单的问题。个人就可以完成。
如果我们遇到一个复杂的问题,需要多人协作完成的,面向对象的方式来思考。
面向对象思维核心就三个点:封装、继承、多态
继承
生活中的继承:
兔子和羊属于食草动物类,狮子和豹属于食肉动物类。
食草动物和食肉动物又是属于动物类。
所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
继承就是子类继承父类的特征和行为,使得子类具有父类的属性和方法,使得子类具有父类相同的行为。子类会具有父类的一般特性也会具有自身的特性。
Go语言的继承:结构体的嵌套
/* Go语言中结构体嵌套 1、模拟继承 is - a type A struct{ field } type B struct{ A // 匿名字段 B.A field } // B是可以直接访问A的属性的 2、聚合关系 has - a type C struct{ field } type D struct{ c C //聚合关系 field } // D是无法直接访问C中的属性的,必须使用D.c.c_field */
案例:
package main import "fmt" // 定义一个父类 type Person struct { name string age int } // 定义一个子类,Student拥有了父类所有的属性,还有自己的特性 type Student struct { Person // 匿名字段,实现了继承。 school string } func main() { // 1、 创建父类对象 p1 := Person{name: "kuangshen", age: 18} fmt.Println(p1) fmt.Println(p1.name, p1.age) // 2、创建子类 s1 := Student{Person: Person{name: "小明", age: 3}, school: "学相伴"} fmt.Println(s1) fmt.Println(s1.Person.name, s1.Person.age, s1.school) // 3、创建子类 var s2 Student s2.Person.name = "张三" s2.Person.age = 3 s2.school = "北大" fmt.Println(s2) // 概念:提升字段, 只有匿名字段才可以做到 // Person 在Student中是一个匿名字段, Person中的属性 name age 就是提升字段 // 所有的提升字段就可以直接使用了,不同在通过匿名字段来点了 var s3 Student s3.name = "李四" s3.age = 4 s3.school = "清华" fmt.Println(s3) fmt.Println(s3.name, s3.age) }
继承就是子类继承父类的特征和行为,使得子类具有父类的属性和方法,使得子类具有父类相同的行为。子类会具有父类的一般特性也会具有自身的特性。
匿名字段 + 提升字段:Go语言中的匿名字段。
方法
Go语言中同时拥有函数和方法。一定要和其他只有方法的语言区分开
类:结构体->属性、方法?
Go语言实现方法
方法:需要指定调用者,约定这个方法属于谁的. 对象.方法()
函数:不需要指定调用者,定义了函数就可以直接函数名()调用
package main import "fmt" // 方法:可以理解为函数多了一个调用者 // 方法可以重名,不同的对象,调用的结果是不一样的 type Dog struct { name string age int } // 方法定义, func 方法调用者 方法名() // 1、方法可以重名,只需要调用者不同 // 2、如果调用者相同,则不能重名 func (dog Dog) eat() { fmt.Println("Dog eating...") } func (dog Dog) sleep() { fmt.Println("Dog sleep...") } type Cat struct { name string age int } func (cat Cat) eat() { fmt.Println("Cat eating...") } func (cat Cat) sleep() { fmt.Println("Cat sleep...") } func main() { // 创建一个对象 dog := Dog{ name: "旺财", age: 2, } fmt.Println(dog) // 方法的调用,通过对应的结构体对象来调用 dog.eat() cat := Cat{name: "喵喵", age: 1} cat.eat() }
方法:
-
某个类的行为功能,需要指定调用者
-
一段独立的代码功能,必须要使用调用者来调用
-
多个类的方法可以重名,单个类不行
-
方法是某个类的动作
函数:
-
一段独立的代码功能,可以直接调用
-
命名完全不能冲突
-
函数是一个特殊的类型
方法的重写
需要和继承集合
子类可以重写父类的方法 override
子类还可以新增自己的方法
子类可以访问呢父类中的属性和方法
package main import ( "fmt" ) // 方法重写,建立在父类和子类结构上的 type Animal struct { name string age int } func (animal Animal) eat() { fmt.Println(animal.name, " 正在吃....") } func (animal Animal) sleep() { fmt.Println(animal.name, " 正在sleep....") } // 子类 type Dog struct { Animal } // 子类自己的方法 func (dog Dog) wang() { fmt.Println("wangwangwanwg~~~") } type Cat struct { Animal color string // 子类可以定义自己的属性 } // 子类重写父类的方法 , 子类的方法名和父类同名,即可重写父类的方法 func (cat Cat) eat() { fmt.Println(cat.name, " 正在吃cat....") } func main() { // 定义一个子类,使用父类方法 dog := Dog{Animal{name: "旺财", age: 3}} dog.eat() // 调用父类的方法 dog.wang() // 调用自己扩展的方法 cat := Cat{Animal{name: "煤球", age: 3}, "黑色"} cat.eat() // 如果重写了父类的方法就是调用子类自己的方法 fmt.Println(cat.color) // 调用子类自己的属性 // 子类可以操作父类的方法,父类可以操作子类的吗?不可以 // 父类不能调用子类自己的扩展的方法 a := Animal{name: "大黄", age: 3} a.eat() a.sleep() }
接口
package main import ( "fmt" ) // 接口: USB、typec、插座 // 1、Go语言提供了接口数据类型。 // 2、接口就是把一些共性的方法集合在一起定义。 // 3、如果有实现类将接口定义的方法全部实现了,那么就代表实现了这个接口 // 4、隐式实现 Go ,假设A实现了B接口中的所有方法,不需要显示声明 // 5、接口是方法的定义集合,不需要实现具体的方法内容。名字约束 // 接口的定义 interface 来定义,方法太多了,要归类,方法的集合 type USB interface { // 接口,方法的集合 input() // 输入方法 output() // 输出方法 } // 结构体 type Mouse struct { name string } // 结构体实现了接口的全部方法就代表实现了这个接口,否则不算实现这个接口 func (mouse Mouse) output() { fmt.Println(mouse.name, "鼠标输出") } func (mouse Mouse) input() { fmt.Println(mouse.name, "鼠标输入") } // 接口嗲用测试 func test(u USB) { u.input() u.output() } func main() { // 通过传入接口实现类来进行调用 m1 := Mouse{name: "罗技"} // test 参数 USB 类型,如果一个结构体实现了这个接口所有的方法,那这个结构体就是这个接口类型的 test(m1) k1 := KeyBoard{name: "雷蛇"} test(k1) // 定义高级类型 k1就升级了 KeyBoard --> USB 向上转型 var usb USB usb = k1 fmt.Println(usb) // 接口是无法使用实现类的属性的 //fmt.Println(usb.name) } // 结构体 type KeyBoard struct { name string } // 结构体实现了接口的全部方法就代表实现了这个接口,否则不算实现这个接口 func (key KeyBoard) output() { fmt.Println(key.name, "键盘输出") } func (key KeyBoard) input() { fmt.Println(key.name, "键盘输入") }
模拟多态
多态:一个事务有多种形态
父类:动物 子类:猫 子类:狗 猫和狗是多态的,他们既可以是自己,也可以是动物,这个就是多态,一个事务有多种形态
package main import "fmt" // 定义接口 type Animal interface { eat() sleep() } type Dog struct { name string } func (dog Dog) eat() { fmt.Println(dog.name, "--eat") } func (dog Dog) sleep() { fmt.Println(dog.name, "--sleep") } // 多态 func main() { // Dog 两重身份:1、Dog 2、Animal ,多态 dog1 := Dog{name: "旺财"} dog1.eat() dog1.sleep() // Dog 也可以是 Animal test2(dog1) // 定义一个类型可以为接口类型的变量 // 实际上所有实现类都可以赋值给这个对象 var animal Animal // 模糊的 -- 具体化,将具体的实现类赋值给他,才有意义 animal = dog1 test2(animal) } // Animal 接口 func test2(a Animal) { a.eat() a.sleep() }
接口的实现类都拥有多态特性:除了自己本身还是他对应接口的类型。
空接口
不包含任何方法
因此:所有的结构体都默认实现了空接口
因此空接口可以存储任何的类型
interface{} == any
package main import "fmt" type A interface{} type Dogg struct { name string } type Catt struct { name string } func testNow(a A) { fmt.Println(a) } // 可以指定定义空接口 // // any is an alias for interface{} and is equivalent to interface{} in all ways. //type any = interface{} func testNow2(temp interface{}) { } func main() { var a1 A = Catt{name: "喵喵"} var a2 A = Dogg{name: "旺财"} var a3 A = 1 var a4 A = "xuexiangban" fmt.Println(a1) fmt.Println(a2) fmt.Println(a3) fmt.Println(a4) // map map1 := make(map[string]interface{}) map1["name"] = "qinjiang" map1["age"] = 18 fmt.Println(map1) // slice s1 := make([]any, 0, 10) s1 = append(s1, 1, "12312", false, a1, a2) fmt.Println(s1) var arr [4]interface{} fmt.Println(arr) }