第十二章、面向“对象”编程
一、结构体
1.1 概念
一个程序就是一个世界,有很多对象(变量)
1.2 golang语言面向对象编程说明
a、golang也支持面向对象编程(oop),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言,所以我们说golang支持面向对象编程特性是比较准确的。
b、golang没有类(class),go语言的结构体(struct)和其他编程语言的类(class)有同等的地位,可以理解golang 基于struct来实现OOP特性的。
c、golang面向对象编程非常简介,去掉了传统语言的继承、方法重载、构造函数和析构函数,隐藏的this指针等等
d、golang仍然有面向对象编程的继承、封装和多态特性,只是实现的方式和其他OOP语言不一样,比如继承:golang没有extends关键字,是通过匿名字段来实现的
e、golang面向对象(OOP)很优雅,OOP本身就是语言类型系统的一部分,通过接口(interface)关联,耦合性低,也非常灵活。
1.3 结构体
对上图说明:
a、将一类事务的特性提取出来(比如猫),形成一个新的数据类型,就是一个结构体
b、通过这个结构体,我们可以创建多个变量(实例/对象)
c、事务可以是人类、商品、工具类...
1.3 结构体和结构体变量的区别联系
a、结构体是自定义的数据类型,代表一类事物
b、结构体变量是具体的、实际的、代表一个具体变量
1.4 结构体在内存的布局
声明未赋值:
结构体赋值以后:
1.5 如何声明结构体
1.5.1、声明
1.5.2、字段/属性
a、从概念或者叫法上看:结构体字段=属性=field
b、字段是结构体的一个组成部分,一般是基本数据类型、数组、也可以是引用数据类型,比如前面我们定义的猫猫结构体,Name string就是属性
1.5.3、字段/属性注意事项(结构体是值类型!!!)
注意事项3:
注意事项4:不同结构体字段是独立的,互不影响
画出上面代码的内存示意图:
第一步、值拷贝
第二步、修改值
1.5.4、创建结构体变量和访问结构体字段
1.5.5、struct类型的内存分配机制
1.5.6、结构体的注意事项和使用细节
a、结构体的所有字段在内存中 是连续的
b、结构体是用户单独定义的类型,和其他类型转换时需要有完全相同的字段(名字、个数和类型)
c、结构体进行type重新定义(相当于取别名),golang认为是新的数据类型,但是互相间可以强转
d、struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和烦序列化
案例演示:
二、方法
2.1 基本介绍
2.2 方法的声明和调用
上面代码说明:
a、func (a A) test(){} 表示A结构体有一个方法,方法名test
b、(a A) 体现test方法和A类型绑定
案例演示:
对上面代码总结:
a、test()方法和类型Person5绑定
b、test()方法只能通过Person5类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用
c、func(p Person5)test(){}, p表示哪个Person5变量调用,这个p就是它的副本,这点和函数的传参非常相似
d、p这个名字,由程序员指定,不固定
练习案例:
1.
2.
2.3 方法的调用和传参机制
说明:方法的调用、传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法
2.4 方法注意事项和细节
a、结构体类型是值类型,在方法调用中,遵守值类型传递机制,是值拷贝传递方式
b、如果程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理,通常也多用指针方式,程序运行效率更高
内存说明:
c、golang中的方法作用在指定类型上,(即:和指定的数据类型绑定),因此,自定义类型都可以有方法,而不仅仅是struct,比如int、float64等等都可以
d、方法的访问范围控制规则,和函数一样。方法名首字母小写,只能在本包访问,方法名首字母大写,可以在本包和其他包函数访问
e、如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个类型的String()进行输出,一般可以用在日志记录
2.5 方法和函数的区别
a、调用方式不一样
函数的调用方式:函数名(实参列表)
方法的调用方式:变量.方法名(实参列表)
b、对于普通函数,接收者为值类型时,不能将指针类型的数据传递,反之亦然
c、对于方法,接受者为值类型时,可以直接用指针类型的变量调用方法,反过来同样可以
三、面向对象编程应用实例
3.1 步骤
3.2 创建结构体变量时指定字段值
3.3 工厂模式
工厂模式解决结构体中结构体名字首字母小写问题::
如果结构体student的age字段首字母也是小写,在其他包不可以直接访问,我们可以提供一个方法
3.4 面向对象编程思想-抽象
简介:
案例演示:
3.5 面向对象编程的三大特性
3.5.1 基本介绍
golang仍然有面向对象编程的继承、封装、多态的特性,只是实现的方式和其他的OOP语言不一样
3.5.2 封装:封装就是把抽象出来的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作
封装的实现步骤:
1、将结构体、字段的首字母小写(不能导出,其他包不能使用,类似private)
2、给结构体所在的包提供一个工厂模式的函数,函数名首字母大写,类似一个构造函数
3、提供一个首字母大写的Set方法
4、提供一个首字母大写的Get方法
特别说明:在golang开发中并没有特别强调封装,这点并不像java、PHP,golang本身对面向对象的特性做了简化的
案例:
3.5.3 继承:当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出这些结构体,在该结构体中定义相同的属性和方法,其他的结构体不需要重新定义这些相同的属性和方法,只需要嵌入一个(基础结构体的)匿名结构体就可以了;也就是说,一个结构体嵌套了另一个匿名结构体,那么这个结构体就可以直接访问匿名结构体的属性和方法,从而实现继承特性
画图说明继承的特性:
嵌套匿名结构体的基本语法:
案例演示:
继承给编程带来的便利:
a、代码的复用性提高了
b、代码的扩展性和维护性提高了
继承深入讨论:
a、结构体可以使用嵌套匿名结构体的所有字段和方法,即:首字母大写、小写的字段 方法都可以使用, 注意::但是跨包不能访问小写字段和方法
b、匿名结构体字段访问可以简化
上图代码说明:
c、当结构体和匿名结构体有相同的字段或方法时,编译器采用“就近原则”访问,如果希望访问匿名结构体的子弹和方法,可以通过匿名结构体来区分
d、当结构体嵌入多个匿名结构体...
e、如果一个struct嵌套了一个"有名结构体",这种模式就是“组合”,如果是组合关系,那么在访问组合的结构体方法或字段时,必须带上结构体的名字
f、嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
指针方式:
g、结构体中匿名字段使用
四、接口
4.1.1 golang接口简介
interface类型可以定义一组方法,但是这些方法不需要实现,并且interface不能包含任何变量,到某个自定义类型(比如下面列子的结构体Phone)要使用的时候,再根据具体情况把这些方法写出来(实现)。
4.1.2 基本语法
4.1.3 为什么会有接口
4.1.4 入门案例演示
4.1.5 接口注意事项和细节说明
a、接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
b、接口中所有的方法都没有方法体,即:都是没有实现的方法
c、在golang中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口
d、只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
e、一个自定义类型可以实现多个接口
f、golang接口中不能有任何变量,编译不过,会报错
g、一个接口(比如A接口),“可以继承多个”别的接口(比如B、C接口),这时如果要实现A接口,必须将B、C接口的方法全部实现
h、interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil
i、空interface{}没有任何方法,所以,所有类型都实现了空接口(换句话说就是,我们可以把任何数据类型的变量赋值给空接口)
4.1.6 接口编程实践
4.1.7 实现接口 vs 继承
案例演示:
说明:
a、当结构体B继承了结构体A,那么结构体B就自动的继承了结构体A的字段和方法,可以直接使用
b、当B结构体需要扩展功能,同时不希望破坏继承关系,则可以去实现接口,因此我们认为:实现接口是对继承的补充!!!
4.1.8 接口 和 继承的区别
五、面向对象编程-多态
5.1.1 基本介绍
变量(实例)具有多种形态,在go语言中,多态特征是通过接口来实现的,可以按照统一的接口来调用不同的实现,这时接口变量就呈现不同的形态。
5.1.2 接口体现多态的两种形式
案例演示:
5.1.3 类型断言基本介绍
类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言
如何在断言时,带上监测机制,如果成功就ok,否则也不要报panic
5.1.4 类型断言实践
第一题、usb接口案例
第二题、写一个函数, 循环判断传入参数的类型