【入门】Go语言面向对象
目录
一、面向对象简介
1.1 什么是面向对象?
- 面向过程:强调的是过程,每一步都是自己亲自实现的。
- 面向对象:强调的是对象,找一个专门做这个事情的,让它来完成,具体怎么实现的,我们不关心。
举个做饭的例子,如果是你自己亲手下厨,一步一步自己做,那这就是面向过程,如果你点外卖,或者去饭馆,那这就是面向对象了,饭怎么做的我们不关心,最终满足我们即可。
1.2 类和对象
- 类:具有相同属性的一类事物的统称,如:老师、汽车、电脑。
- 对象:具体的事物,如,三年级一班的语文老师、朋友的宝马汽车,我的办公电脑。
类是一些列事物的统称,同类事务必定具有相同特征,通过属性和方法(函数)来表述类。
1.3 面向对象编程的好处
- 封装
- 继承
- 多态
二、继承
2.1 继承简介
- 继承是一种类间关系,描述一个类从另一个类获取成员信息的类关系。
- 继承必须发生在两个类之间,参与继承关系的双发称为子类和父类。
- 父类提供成员信息,子类获取父类成员信息。
2.2 匿名字段实现继承及对象创建
举一个简单例子:定义两个结构体student
和public
,student
继承public
类,那么public
是父类,student
是子类,如下:
package main
import "fmt"
type public struct {
id int
name string
age int
}
type student struct {
public // 继承public类
score float64
}
func main() {
// 全部初始化,继承类初始化时需要注意public{1, "zhangsan", 17}这种方法
var t1 student = student{public{1, "zhangsan", 17}, 100}
// 部分初始化
var t2 student = student{score: 100}
// 针对继承类部分初始化
var t3 student = student{public: public{name: "lisi", age: 22}}
fmt.Printf("%v\n%v\n%v\n", t1, t2, t3)
}
代码输出内容:
{{1 zhangsan 17} 100}
{{0 0} 100}
{{0 lisi 22} 0}
2.3 指针类型匿名字段
继承父类时改为指针类型
package main
import "fmt"
type public struct {
id int
name string
age int
}
type student struct {
*public // 继承父类 指针类型
score float64
}
func main() {
// 定义对象时需要主要添加 & 符号取内存地址
var zhangsan student = student{&public{1, "zhangsan", 19}, 80}
// 输出的是父类的内存地址
fmt.Println(zhangsan)
// 输出指定成员
fmt.Println(zhangsan.name)
}
代码输出内容:
{0xc0000523c0 80}
zhangsan
2.4 多重继承
多重继承只,子类继承父类,父类中有继承了父类,多重继承在平常尽量少用,会增加维护难度。
package main
import "fmt"
// 定义public类
type public struct {
name string
age int
}
// 定义public2类继承public类
type public2 struct {
id int
public
}
// 定义student类继承public2类
type student struct {
public2
score float64
}
func main() {
var t1 student
var t2 student
// 以下两种赋值操作等价
t1.public2.public.name = "zhangsan"
t2.name = "lisi"
// 以下两种输出操作等价
fmt.Println(t1.name)
fmt.Println(t2.public2.public.name)
}
代码输出内容:
zhangsan
lisi
三、成员操作
3.1 获取成员值
语法:
对象名.成员名
package main
import "fmt"
type public struct {
id int
name string
age int
}
type student struct {
public
score float64
}
func main() {
// 定义两个对象 分别是 zhangsan、lisi
var zhangsan student = student{public{1, "zhangsan", 19}, 80}
var lisi student = student{public{2, "lisi", 20}, 100}
// 获取zhangsan对象的成绩
fmt.Println(zhangsan.score)
// 获取lisi对象的ID编号,一下两种写法等价
fmt.Println(lisi.public.id)
fmt.Println(lisi.id)
}
代码输出内容:
80
2
2
3.2 修改成员值
语法:
对象名.成员 = 值
拿上面案例举例
package main
import "fmt"
type public struct {
id int
name string
age int
}
type student struct {
public
score float64
}
func main() {
var zhangsan student = student{public{1, "zhangsan", 19}, 80}
var lisi student = student{public{2, "lisi", 20}, 100}
// 以下两种写法等价都是ID+10
zhangsan.id += 10
lisi.public.id += 10
fmt.Println(zhangsan, lisi)
}
代码输出内容:
{{11 zhangsan 19} 80} {{12 lisi 20} 100}
四、方法
4.1 为结构体添加方法
声明语法:
func (对象 结构体类型)方法名(参数列表)(返回值列表) {
代码体
}
调用语法:
对象名.方法名
举个例子:
package main
import "fmt"
type student struct {
id int
name string
age int
score float64
}
// 定义输出方法
func (s student) showPrint() {
fmt.Println(s)
}
// 定义修改方法 注意类型需要是指针才可以修改
func (s *student) editId() {
s.id = 100
}
func main() {
var stu student = student{1, "zhagnsan", 20, 100}
stu.editId()
stu.showPrint()
}
代码输出内容:
{100 zhagnsan 20 100}
4.2 使用方法注意事项
- 接受者类型不一样,这个方法计算是同名,也是不同的方法
- 接受者为指针类型
举个例子验证:接受者类型不一样,这个方法计算是同名,也是不同的方法
package main
import (
"fmt"
)
type Student struct {
id int
name string
age int
}
type Teacher struct {
id int
name string
age int
}
// 创建两个相同名称的方法 名字都叫Show
func (s Student) Show() {
fmt.Println(s)
}
func (t Teacher) Show() {
fmt.Println(t)
}
func main() {
var s Student = Student{1, "张三", 20}
var t Teacher = Teacher{2, "李四", 30}
//接受者类型不一样,这个方法计算是同名,也是不同的方法
s.Show()
t.Show()
}
代码输出内容:
{1 张三 20}
{2 李四 30}
举例说明一下:接受者为指针类型是什么意思
package main
import (
"fmt"
)
type Student struct {
id int
name string
age int
}
func (s *Student) Show() {
fmt.Println(s)
}
func main() {
var s Student = Student{1, "张三", 20}
s.Show() // 思考: 方法类型是指针,调用方法时候为什么不用添加&符号获取内存地址呢?
(&s).Show() // 答: 使用&获取内存地址,和上面等价关系,上面是简写,实际上运行的时候会加上&
}
代码输出内容:
&{1 张三 20}
&{1 张三 20}
4.3 案例练习
需求:
定义一个学生类,有六个属性,分别为姓名、性别、年龄、语文、数学、英语成绩。
定义两个方法:
第一个方法:打招呼的方法:介绍自己叫XX,今年几岁了,是男同学还是女同学。
第二个方法:计算总分与平均成绩。
package main
import (
"fmt"
)
// 第二个方法:计算总分与平均成绩
type Student struct {
name string
gender string
age int
language float64
mathematics float64
english float64
}
func (s *Student) Info(name string, age int, gender string) {
s.name = name
s.age = age
s.gender = gender
if gender != "男" || gender != "女" {
gender = "男"
}
if age < 0 || age > 100 {
age = 19
}
fmt.Printf("我叫%s,今年%d岁了,是%s同学\n", name, age, gender)
}
func (s *Student) AvgScore(language float64, mathematics float64, english float64) {
sum := language + mathematics + english
fmt.Printf("我叫%s 平均:%.2f 总和:%.2f\n", s.name, sum/3, sum)
}
func main() {
var zhangsan Student
zhangsan.Info("张三", 22, "男")
zhangsan.AvgScore(100, 80.5, 100)
}
代码输出内容:
我叫张三,今年22岁了,是男同学
我叫张三 平均:93.50 总和:280.50
4.4 方法继承
子类中可以继承父类中定义的方法,简单举个例子:
package main
import (
"fmt"
)
type Person struct {
name string
gender string
age int
}
type Student struct {
Person
score int
}
func (p *Person) Show() {
fmt.Println(*p)
}
func main() {
var zhangsan Student = Student{Person{"张三", "男", 100}, 100}
zhangsan.Show()
}
代码输出内容:
{张三 男 100}
4.5 方法重写
父类中方法和子类中方法重名了 该怎么办?
如果 父类中方法和子类中方法重名了 那么通过子类对象调用的是子类的
package main
import (
"fmt"
)
type Person struct {
name string
}
type Student struct {
Person
}
func (p *Person) Show() {
fmt.Println("这是父类中的方法")
}
func (p *Student) Show() {
fmt.Println("这是子类中的方法")
}
func main() {
var zhangsan Student
zhangsan.Show() // 默认调用子类中方法
zhangsan.Person.Show() // 调用父类方法
}
代码输出内容:
这是子类中的方法
这是父类中的方法
4.6 方法值与方法表达式
案例:使用方法值调用方法
package main
import (
"fmt"
)
type Student struct {
name string
age int
}
func (s *Student) Show() {
fmt.Println(*s)
}
func main() {
var zhangsan Student = Student{"zhagnsan", 18}
zhangsan.Show() // 传统调用方法
t1 := zhangsan.Show
fmt.Printf("t1的类型是%T\n", t1)
t1() // 方法值调用方法
}
代码输出内容:
{zhagnsan 18}
t1的类型是func(){zhagnsan 18}
案例:使用方法表达式调用方法
package main
import (
"fmt"
)
type Student struct {
name string
age int
}
func (s *Student) Show() {
fmt.Println(*s)
}
func main() {
var zhangsan Student = Student{"zhagnsan", 18}
zhangsan.Show() // 传统调用方法
// 方法表达式,类型必须要和方法类型一致
f := (*Student).Show
f(&zhangsan)
}
代码输出内容:
{zhagnsan 18}
{zhagnsan 18}