前面我们已经将GO语言中各种类型,给大家讲解完毕了,那么接下来要给大家讲解的是面向对象编程思想。
在讲解具体面向对象编程之前,先说一下面向过程编程。我们前面学习都是面向过程的一种编程思想,接下来可以从生活中理解面向过程:
如果我们自己来修汽车,应该有哪些步骤呢?
第一步:找工具
第二步:判断问题的原因
第三步:暴力拆卸
这个修理的步骤就是面向过程,所谓的面向过程就是:强调的是步骤、过程、每一步都是自己亲自去实现的。
如果采用面向对象的思想,那么应该怎样修车呢?
找4s店的工作人员来帮我们修车,但是到底怎么修,我们是不用考虑的,也就是说我们不关心步骤与过程。
所谓的面向对象其实就是找一个专门做这个事的人来做,不用关心具体怎么实现的。
所以说,面向过程强调的是过程,步骤。而面向对象强调的是对象,也就是干事的人。
大家可以想一下,在生活中还有哪些事情是面向过程,面向对象的。
比如说,玩游戏,如果是面向过程,就是自己玩,自己打怪升级。如果是面向对象,可以找代练,一切打怪升级都有代练完成,不需要我们自己关心了。
比如说,做饭,面向过程就是自己做,自己买菜,自己洗,自己炒,整个过程都有自己来完成,但是如果是面向对象,可以叫外卖,不用关心饭是怎么做的。
所以通过以上案例,大家能够体会出,面向过程就是强调的步骤,过程,而面向对象强调的是对象,找个人来做。
在面向对象中,还有两个概念是比较重要的,一是对象,二是类。
什么是对象呢?
万物皆对象,例如张三同学是一个对象,李四同学也是一个对象
那么我们在生活中怎样描述一个对象呢?
比如,描述一下张三同学:
姓名:张三
性别:男
身高:180cm
体重:70kg
年龄:22岁
吃喝拉撒睡一切正常 健康
吃喝嫖赌抽
通过以上的描述,可以总结出在生活中描述对象,可以通过特征(身高,体重,年龄等)和行为(爱好等)来进行描述。
那么在程序中,可以通过属性和方法(函数)来描述对象。属性就是特征,方法(函数)就是行为。所以说,对象必须具有属性和方法。虽然说,万物皆对象,但是在描述一个对象的时候,一定要具体不能泛指,例如,不能说“电灯”是一个对象,而是说具体的哪一台“电灯”。
大家可以思考一下,如果我们现在描述一下教室中某一台电灯,应该有哪些属性(特征)和方法(行为)呢?
下面我们在思考一下,下面这道题:
张三(一个学生)\杨老师\邻居售货员张阿姨\李四的爸爸\李四的妈妈
找出这道题中所有对象的共性(所谓共性,指的是相同的属性和方法)。
所以说,我们可以将这些具有相同属性和相同方法的对象进行进一步的封装,抽象出来类这个概念。
类就是个模子,确定了对象应该具有的属性和方法。
对象是根据类创建出来的
例如:上面的案例中,我们可以抽出一个“人”类(都有年龄,性别,姓名等属性,都有吃饭,走路等行为),“张三”这个对象就是根据“人”类创建出来的,也就是说先有类后有对象。
1:GO语言中的面向对象
前面我们了解了一下,什么是面向对象,以及类和对象的概念。但是,GO语言中的面向对象在某些概念上和其它的编程语言还是有差别的。
严格意义上说,GO语言中没有类(class)的概念,但是我们可以将结构体比作为类,因为在结构体中可以添加属性(成员),方法(函数)。
面向对象编程的好处比较多,我们先来说一下“继承”,
所谓继承指的是,我们可能会在一些类(结构体)中,写一些重复的成员,我们可以将这些重复的成员,
单独的封装到一个类(结构体)中,作为这些类的父类(结构体),我们可以通过如下图来理解:
当然严格意义上,GO语言中是没有继承的,但是我们可以通过”匿名组合”来实现继承的效果。
01匿名字段实现继承
package main
import "fmt"
type Person struct {
id int
name string
age int
sex string
}
type Student struct {
//将Person结构体作为student结构体的成员
Person //匿名字段实现继承关系
//p Person //为Person结构体起名字
class string
score int
}
func main1() {
var stu Student
//student继承与父类person可以直接使用父类的结构体成员
stu.Person.name = "xxd"
stu.Person.age = 19
stu.Person.id = 101
stu.sex = "男"
stu.class = "3年级1班"
stu.score = 100
fmt.Println(stu) //{{101 xxd 19 男} 3年级1班 100}
}
func main() {
str := Student{Person{01, "一方", 18, "男"}, "2年级1班", 99}
fmt.Printf("ID:%d\n", str.id) //ID:1
fmt.Printf("姓名:%s\n", str.name) //姓名:一方
fmt.Printf("年龄:%d\n", str.age) //年龄:18
fmt.Printf("性别:%s\n", str.sex) //性别:男
fmt.Printf("班级:%s\n", str.class) //班级:2年级1班
fmt.Printf("成绩:%d\n", str.score) //成绩:99
}
02 匿名字段同名成员
package main
import "fmt"
type person struct {
id int
name string
age int
sex string
}
type student struct {
person
name string
class int
score int
}
func main() {
//对象初始化
//var stu student
//stu.id = 1
//stu.name = "李四"
//stu.age = 18
//stu.sex = "女"
//stu.class = 301
//stu.score = 99
////子类赋值
////子类和父类结构有相同的成员名,默认赋值为子类,采用就近原则
////父类赋值
//stu.person.name = "xxd"
stu := student{person{1, "李二", 18, "女"}, "李四", 301, 99}
fmt.Println(stu)
}
03 匿名字段成员为指针
package main
import "fmt"
type person struct {
id int
name string
age int
sex string
}
type student struct {
*person //指针作为匿名函数
class int
score int
}
func main() {
var stu student
stu.class = 301
stu.score = 99
////stu.person是一个指针类型 默认值为nil 0
////需要对指针进行创建空间 new(person)
//stu.person = new(person)
//stu.name = "李四"
//stu.id = 1
//stu.age = 18
//stu.sex = "男"
//fmt.Println(stu)
stu.person = &person{1, "daong", 18, "男"}
fmt.Printf("ID:%d\n", stu.id) //ID:1
fmt.Printf("姓名:%s\n", stu.name) //姓名:一方
fmt.Printf("年龄:%d\n", stu.age) //年龄:18
fmt.Printf("性别:%s\n", stu.sex) //性别:男
fmt.Printf("班级:%d\n", stu.class) //班级:2年级1班
fmt.Printf("成绩:%d\n", stu.score) //成绩:99
}
04 多重继承
package main
import "fmt"
type person1 struct {
name string
age int
sex string
}
type person2 struct {
id int
addr string
}
type student struct {
//结构体成员为多个匿名字段
person1
person2
class int
score int
}
func main() {
//var stu student
//stu.age = 18
//stu.class = 301
//stu.score = 99
//stu.name = "托儿索"
//stu.addr = "召唤师峡谷"
//stu.id = 1
//stu.sex = "男"
stu := student{person1{"dd", 18, "男"}, person2{1, "召唤师峡谷"}, 301, 99}
fmt.Println(stu) //{{托儿索 18 男} {1 召唤师峡谷} 301 99}
}
05 多重继承-2
package main
import "fmt"
type person1 struct {
name string
age int
sex string
}
type person2 struct {
person1
id int
addr string
}
type student struct {
//结构体成员为多个匿名字段
person2
class int
score int
}
func main() {
//var stu student
//stu.age = 18
//stu.class = 301
//stu.score = 99
//stu.name = "托儿索"
//stu.addr = "召唤师峡谷"
//stu.id = 1
//stu.sex = "男"
stu := student{person2{person1{"托儿索", 18, "男"}, 1, "召唤师峡谷"}, 301, 99}
fmt.Println(stu) //{{{托儿索 18 男} 1 召唤师峡谷} 301 99}
}
06 对象的创建和使用-1
package main
import "fmt"
//1、定义函数类型 定义结构体名称
//2、为已存在的数据类型起别名
type Int int
//对象的方法
//func (对象)方法(参数列表)(返回值列表){
// 代码体
//}
func (a Int) add(b Int) Int {
fmt.Println(a)
fmt.Println(b)
return a + b
}
func main() {
//将源文件编译成可执行程序
//编译过程
//1、预处理 包展开 替换数据类型 去掉注释
//2、编译 如果代码有错会提示 如果没错会编译成汇编文件
//3、汇编 将汇编文件转成二进制文件
//4、链接 将支持的库链接到程序中 变成可执行程序
//类型别名会在编译时进行转换
var a Int = 10
var b Int =20
//var b Int = 20
//包.函数 结构体.成员 对象.方法
//sum:=a.add(b)
//对象.方法
//想要使用方法 必须是相同类型的对象才可以
sum:=b.add(a)
fmt.Println(sum)
//var b int = 20
//fmt.Println(a + 10)
//fmt.Printf("%T\n", a)
}
07 对象方法的创建和使用-2
package main
import "fmt"
type student5 struct {
name string
age int
sex string
score int
addr string
}
//函数
func Print() {
fmt.Println("hello world")
}
//结构体类型可以作为对象类型
func (s student5) Print() {
fmt.Printf("%p\n",&s)
fmt.Printf("姓名:%s\n", s.name)
fmt.Printf("年龄:%d\n", s.age)
fmt.Printf("性别:%s\n", s.sex)
fmt.Printf("成绩:%d\n", s.score)
fmt.Printf("地址:%s\n", s.addr)
}
func main() {
//创建对象
stu := student5{"贾政", 58, "男", 80, "贾府"}
fmt.Printf("%p\n",&stu)
//对象.方法
stu .Print()
fmt.Println(stu)
stu1 := student5{"贾玲", 29, "女", 99, "北京"}
stu1.Print()
//打印错误日志使用函数
//print()
//函数调用
//Print()
//对象的方法名可以和函数名重名 但是相同的对象方法名不能重名
//fmt.Println(stu.Print)
//fmt.Println(stu1.Print)
//打印函数在代码区的地址
fmt.Println(Print)
}
08方法的内存模型
package main
import "fmt"
type student6 struct {
name string
age int
sex string
score int
addr string
}
//对象不同方法名相同 不会冲突
//在方法调用中 方法的接收者为指针类型
//指针类型 和普通类型表示是相同对象的类型
func (s *student6) Print() {
s.name = "黄月英"
s.score = 99
//fmt.Println(*s)
}
func main() {
stu := student6{"貂蝉", 22, "女", 80, "徐州"}
//地址传递(*student6) 值传递(student6)
//一般建议选择地址传递
stu.Print()
fmt.Println(stu)
}
09 方法继承
package main
import "fmt"
type person7 struct{
id int
name string
age int
sex string
}
type student7 struct {
person7
class int
score int
addr string
}
func (p *person7)Print(){
fmt.Printf("编号:%d\n",p.id)
fmt.Printf("姓名:%s\n",p.name)
fmt.Printf("年龄:%d\n",p.age)
fmt.Printf("性别:%s\n",p.sex)
}
func main() {
p:=person7{1001,"孙尚香",32,"女"}
p.Print()
s:=student7{person7{1002,"糜夫人",32,"女"},302,100,"巴蜀"}
//子类继承于父类 可以继承结构体的成员(属性) 也可以继承父类的方法
//父类不能继承于子类
s.Print()
}
10 方法练习
package main
import (
"fmt"
)
/*
记者:我是记者 我的爱好是偷拍 我的年龄是34 我是一个男狗仔
程序员:我叫孙权 我的年龄是23 我是男生 我的工作年限是 3年
*/
//父类
type Human struct {
name string
age int
sex string
}
//记者子类
type Reper struct {
Human
hobby string
}
//程序员子类
type Dever struct {
Human
worktime int
}
//父类方法
func (h *Human) SayHi() {
fmt.Printf("大家好,我叫%s,我今年%d岁,我是%s生", h.name, h.age, h.sex)
}
//记者的方法
func (r *Reper) SayHello() {
//继承父类方法
r.SayHi()
fmt.Printf(",我的爱好是:%s\n", r.hobby)
}
//程序员的方法
func (d *Dever) SayHello() {
d.SayHi()
fmt.Printf(",我的工作年限是:%d年\n", d.worktime)
}
func main() {
r:=Reper{Human{"卓伟",40,"男"},"偷拍"}
r.SayHello()
d:=Dever{Human{"图灵",32,"男"},10}
d.SayHello()
}