【入门】Go语言面向对象

一、面向对象简介

1.1 什么是面向对象?

  • 面向过程:强调的是过程,每一步都是自己亲自实现的。
  • 面向对象:强调的是对象,找一个专门做这个事情的,让它来完成,具体怎么实现的,我们不关心。

举个做饭的例子,如果是你自己亲手下厨,一步一步自己做,那这就是面向过程,如果你点外卖,或者去饭馆,那这就是面向对象了,饭怎么做的我们不关心,最终满足我们即可。

1.2 类和对象

  • 类:具有相同属性的一类事物的统称,如:老师、汽车、电脑。
  • 对象:具体的事物,如,三年级一班的语文老师、朋友的宝马汽车,我的办公电脑。

类是一些列事物的统称,同类事务必定具有相同特征,通过属性和方法(函数)来表述类。

1.3 面向对象编程的好处

  • 封装
  • 继承
  • 多态

二、继承

2.1 继承简介

  • 继承是一种类间关系,描述一个类从另一个类获取成员信息的类关系。
  • 继承必须发生在两个类之间,参与继承关系的双发称为子类和父类
  • 父类提供成员信息,子类获取父类成员信息。

2.2 匿名字段实现继承及对象创建

举一个简单例子:定义两个结构体studentpublic,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}
posted @ 2023-04-09 11:33  乱七八糟博客备份  阅读(184)  评论(0编辑  收藏  举报