面向对象编程
OOP思想
面向过程的思维模式
面向过程的思维模式是简单的线性思维,思考问题首先陷入第一步做什么,第二部做什么的细节中。这种思维模式适合处理简单的事情,比如:上厕所
面向对象的思维模式
面向对象的思维模式说白了就是分类思维模式。首先思考解决问题,需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
面向对象的三大特点:封装、继承、多态
继承
继承就是子类继承父类的特征和行为,使得子类具有父类的属性和方法,使得子类具有父类相同的行为。子类会具有父类的一般特性也会具有自身的特性。
package main
import "fmt"
/*
Go语言的结构体嵌套
1。模拟继承性: is-a
type A struct{
field
}
type B struct{
A
B可以直接访问A中的属性
}
2。模拟聚合关系 :has-a
type C struct{
field
}
type D struct{
c C
}
D就不能直接访问c当中的属性,而是需要通过D.c.xx 访问
*/
// 1。定义一个父"类"结构体
type Person struct {
name string
age int
}
// 2。定义一个子"类"结构体
type Student struct {
Person // 匿名变量 继承
school string //子类自己的书写字段
}
func main() {
p1 := Person{name: "zuo", age: 18}
fmt.Println(p1.name)
s1 := Student{Person{name: "jiang", age: 20}, "sz"}
fmt.Println(s1.Person.name)
//提升字段
//对于Student来说,Person是匿名字段,Person中的属性name、age就叫做提升字段
//提升字段可以通过名字直接访问,不需要再同构结构体名Person
fmt.Println(s1.name)
var s3 Student
s3.name = "zhao"
s3.school = "北大"
fmt.Println(s3)
}
方法
方法
- 某个类别的行为功能,需要指定的接受者调用
- 一段独立的功能代码,可以直接调用
函数
- 命名不能冲突
- 一段独立的功能代码,可以直接调用
//函数所有人都可以调用 func funName(param list)(return list){} //方法,多了一个接受者,并且只有这歌接受者类型才可以调用 func (t type) funName(param list)(return list){} //接受者 可以是这个类型或者这个类型的指针 func (接受者) funName(param list)(return list){}
package main
import "fmt"
type Dog struct {
name string
age int
}
type Cat struct {
name string
age int
}
// 方法定义
func (dog Dog) eat() {
fmt.Println("Dog,eating")
}
func (cat Cat) eat() {
fmt.Println("Cat,eating")
}
func (dog Dog) sleep() {
fmt.Println("dog,sleep")
}
func main() {
dog := Dog{name: "jiang", age: 10}
dog.eat()
dog.sleep()
cat := Cat{name: "喵喵", age: 5}
cat.eat()
}
方法重写
子类可以重写父类方法override
子类可以新增自己的属性和方法
子类可以直接访问父类的属性和方法
package main
import "fmt"
type Animal struct {
name string
age int
}
type Dog struct {
Animal
}
type Cat struct {
Animal
Color string
}
func (animal Animal) eat() {
fmt.Println(animal.name, "正在吃")
}
func (animal Animal) sleep() {
fmt.Println(animal.name, "正在睡觉")
}
// 子类重写父类的方法
func (cat Cat) eat() {
fmt.Println(cat.name, "正在吃~~~~~~~")
}
// 子类扩展自己的方法
func (cat Cat) maio() {
fmt.Println(cat.name, "miao~~")
}
func main() {
dog := Dog{Animal{name: "旺财", age: 1}}
dog.eat()
cat := Cat{Animal{name: "喵喵", age: 2}, "blue"}
cat.eat()
cat.maio()
}
接口实现
把所有的具有共性的方法定义在一起,任何其他类型只有实现接口定义的全部方法就是实现了这个接口
- go语言不需要显示的实现接口
- 实现接口中的方法,就算实现了接口
- go语言中,接口和实现类的关系,是非侵入式的
package main
import "fmt"
type USB interface {
oInput()
oOutput()
}
type Mouse struct {
name string
}
func (mouse Mouse) oInput() {
fmt.Println(mouse.name, "鼠标输入")
}
func (mouse Mouse) oOutput() {
fmt.Println(mouse.name, "鼠标输出")
}
func test(u USB) {
u.oInput()
u.oOutput()
}
type KeyBored struct {
name string
}
func (k KeyBored) oInput() {
fmt.Println(k.name, "键盘输入")
}
func (k KeyBored) oOutput() {
fmt.Println(k.name, "键盘输出")
}
func main() {
//通过传入接口实现类,来进行具体方法的调用
m := Mouse{name: "罗技"}
test(m)
k := KeyBored{name: "雷蛇"}
test(k)
var usb USB
usb = k
fmt.Println(usb)
}
多态
一个事务拥有多种形态。是面向对象中很重要的一个特点
go语言通过接口来模拟多态
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 test1(a Animal) {
fmt.Println("test1")
a.eat()
}
func main() {
dog := Dog{name: "jiang"}
dog.eat()
dog.sleep()
test1(dog)
var animal Animal
animal = dog
fmt.Println(animal)
}
空接口
不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值
package main
import "fmt"
type Dog struct {
name string
}
type Cat struct {
name string
color string
}
// 空接口
type A interface {
}
func test3(a A) {
fmt.Println(a)
}
func test4(a interface{}) {
fmt.Println(a)
}
func main() {
var a1 A = Cat{
name: "毛毛",
color: "蓝色",
}
var a2 A = Dog{
name: "大黄",
}
var a3 A = "haha"
var a4 A = 200
fmt.Println(a1)
fmt.Println(a2)
fmt.Println(a3)
fmt.Println(a4)
test3(a1)
test3(a2)
test3(a3)
test3(a4)
fmt.Println("=========map==========")
//map key string v obj
map1 := make(map[string]interface{})
map1["name"] = "zuo"
map1["age"] = 20
map1["dog"] = Dog{name: "小鲳鱼"}
fmt.Println(map1)
fmt.Println("=========切片=========")
s1 := make([]interface{}, 0, 10)
s1 = append(s1, a1, a3, a4, 200, "sada")
fmt.Println(s1)
}
接口嵌套
接口可以继承,还可以多继承
package main
import "fmt"
type A interface {
test1()
}
type B interface {
test2()
}
type C interface {
A
B
test3()
}
type Dog struct {
}
func (dog Dog) test1() {
fmt.Println("test1")
}
func (dog Dog) test2() {
fmt.Println("test2")
}
func (dog Dog) test3() {
fmt.Println("test3")
}
func main() {
var dog Dog = Dog{}
dog.test1()
dog.test2()
dog.test3()
//在接口嵌套中,嵌套的接口默认继承了被嵌套接口的所有方法
var ia A = dog
ia.test1()
var ib B = dog
ib.test2()
var ic C = dog
ic.test1()
ic.test2()
ic.test3()
}
接口断言
检查接口类型变量的值是否实现了期望的接口,就是检查当前接口类型的值有没有实现指定的接口。
把接口类型变量的值转换为其他类型或其他接口,go语言空interface()可以保存任何类型的变量,当程序中需要使用该变量的时候,需要把接口类型变量的值转化为具体类型,可以通过接口类型断言
假如接口类型保存的值是数字,当前的值要参与数学运算,就必须转为int类型才可以参加运算,这就是利用接口断言来实现类型转化的例子
被断言的对象必须是接口类型,否则会报错
t := i.(T)
func assertString(i interface{}) {
s := i.(string)
fmt.Println(s)
}
- 断言成功,则t为T类型的接口值
- 断言失败则报错,panic
v, ok := i.(T)
func assertInt(i interface{}) {
s, ok := i.(int)
if ok {
fmt.Println("接口变量i是int类型")
fmt.Println(s)
} else {
fmt.Println("接口变量i不是int类型")
}
}
type-switch
func test5(i interface{}) {
switch i.(type) {
case string:
fmt.Println("string类型")
case int:
fmt.Println("int类型")
case I:
fmt.Println("I类型")
case nil:
fmt.Println("nil类型")
default:
fmt.Println("未知类型")
}
}
补充:type别名
package main
import "fmt"
// 通过type关键字的定义,DiyInt就是一种新的类型,它具有int的特性
type DiyInt int
func main() {
var a DiyInt = 20
var b int = 10
c := int(a) + b
fmt.Println(c)
//定义int类型的别名为myInt
//myInt类型只会在代码中存在,编译完成时并不会有myInt类型
type myInt = int
var q myInt = 30
var d int = 40
e := d + q
fmt.Println(e)
}