go语言面向对象编程继承 golang面向接口编程

没有类,C语言有结构体,那么Go的结构体有什么特别之处? Go语言中没有“类”的概念go语言面向对象编程继承,也不支持“类”的继承等面向对象的概念 。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性 。
自定义类型
在Go语言中有一些基本的数据类型,如string、整型、浮点型、布尔等数据类型 , Go语言中可以使用type关键字来定义自定义类型 。
自定义类型是定义了一个全新的类型 。我们可以基于内置的基本类型定义 , 也可以通过struct定义 。例如:
通过Type关键字的定义,MyInt就是一种新的类型,它具有int的特性 。
类型别名
类型别名是Go1.9版本添加的新功能 。
类型别名规定:TypeAlias只是Type的别名 , 本质上TypeAlias与Type是同一个类型 。就像一个孩子小时候有小名、乳名 , 上学后用学名,英语老师又会给go语言面向对象编程继承他起英文名,但这些名字都指的是他本人 。
type TypeAlias = Type
我们之前见过的rune和byte就是类型别名,他们的定义如下:
类型定义和类型别名的区别
类型别名与类型定义表面上看只有一个等号的差异,我们通过下面的这段代码来理解它们之间的区别 。
结果显示a的类型是main.NewInt,表示main包下定义的NewInt类型 。b的类型是int 。MyInt类型只会在代码中存在 , 编译完成时并不会有MyInt类型 。
Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型 , 这种数据类型叫结构体,英文名称struct 。也就是我们可以通过struct来定义自己的类型了 。
Go语言中通过struct来实现面向对象 。
结构体的定义
使用type和struct关键字来定义结构体,具体代码格式如下:
其中:
举个例子,我们定义一个Person(人)结构体,代码如下:
同样类型的字段也可以写在一行,
这样我们就拥有了一个person的自定义类型,它有name、city、age三个字段,分别表示姓名、城市和年龄 。这样我们使用这个person结构体就能够很方便的在程序中表示和存储人信息了 。
语言内置的基础数据类型是用来描述一个值的,而结构体是用来描述一组值的 。比如一个人有名字、年龄和居住城市等,本质上是一种聚合型的数据类型
结构体实例化
只有当结构体实例化时,才会真正地分配内存 。也就是必须实例化后才能使用结构体的字段 。
基本实例化
举个例子:
我们通过.来访问结构体的字段(成员变量),例如p1.name和p1.age等 。
匿名结构体
在定义一些临时数据结构等场景下还可以使用匿名结构体 。
创建指针类型结构体
我们还可以通过使用new关键字对结构体进行实例化,得到的是结构体的地址 。格式如下:
从打印的结果中我们可以看出p2是一个结构体指针 。
需要注意的是在Go语言中支持对结构体指针直接使用.来访问结构体的成员 。
取结构体的地址实例化
使用对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作 。
p3.name = "七米"其实在底层是(*p3).name = "七米" , 这是Go语言帮我们实现的语法糖 。
结构体初始化
没有初始化的结构体,其成员变量都是对应其类型的零值 。
使用键值对初始化
使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值 。
也可以对结构体指针进行键值对初始化,例如:
当某些字段没有初始值的时候,该字段可以不写 。此时,没有指定初始值的字段的值就是该字段类型的零值 。
使用值的列表初始化
初始化结构体的时候可以简写,也就是初始化的时候不写键,直接写值:
使用这种格式初始化时,需要注意:
结构体内存布局
结构体占用一块连续的内存 。
输出:
【进阶知识点】关于Go语言中的内存对齐推荐阅读:在 Go 中恰到好处的内存对齐
面试题
请问下面代码的执行结果是什么go语言面向对象编程继承?
构造函数
Go语言的结构体没有构造函数,我们可以自己实现 。例如,下方的代码就实现了一个person的构造函数 。因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型 。
调用构造函数
方法和接收者
Go语言中的方法(Method)是一种作用于特定类型变量的函数 。这种特定类型变量叫做接收者(Receiver) 。接收者的概念就类似于其他语言中的this或者 self 。
方法的定义格式如下:
其中 ,
举个例子:
方法与函数的区别是,函数不属于任何类型,方法属于特定的类型 。
指针类型的接收者
指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的 。这种方式就十分接近于其他语言中面向对象中的this或者self 。例如我们为Person添加一个SetAge方法,来修改实例变量的年龄 。
调用该方法:
值类型的接收者
当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份 。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本 , 无法修改接收者变量本身 。
什么时候应该使用指针类型接收者
任意类型添加方法
在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法 。举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法 。
注意事项:非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法 。
结构体的匿名字段
匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个 。
嵌套结构体
一个结构体中可以嵌套包含另一个结构体或结构体指针 。
嵌套匿名结构体
当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找 。
嵌套结构体的字段名冲突
嵌套结构体内部可能存在相同的字段名 。这个时候为了避免歧义需要指定具体的内嵌结构体的字段 。
结构体的“继承”
Go语言中使用结构体也可以实现其他编程语言中面向对象的继承 。
结构体字段的可见性
结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问) 。
结构体与JSON序列化
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式 。易于人阅读和编写 。同时也易于机器解析和生成 。JSON键值对是用来保存JS对象的一种方式 , 键/值对组合中的键名写在前面并用双引号""包裹 , 使用冒号:分隔,然后紧接着值;多个键值之间使用英文,分隔 。
结构体标签(Tag)
Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来 。Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:
`key1:"value1" key2:"value2"`
结构体标签由一个或多个键值对组成 。键与值使用冒号分隔,值用双引号括起来 。键值对之间使用一个空格分隔 。注意事项:为结构体编写Tag时,必须严格遵守键值对的规则 。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值 。例如不要在key和value之间添加空格 。
例如我们为Student结构体的每个字段定义json序列化时使用的Tag:
Go语言与Java之间性能相差多少Java是一门较为成熟的语言,相对于C要简单的多,C里没有内存回收,所以比较麻烦,Java加入了内存自动回收,简单是简单,却变慢了,go语言是一门新兴的语言,现在版本是1.9 ? go语言的性能比Java要好,但由于出现晚,资料较Java少,有些Java的功能go也没有,并且有许多的软件是支持Java但支持go的很少.所以在短期内Java是比go通用的
C语言的最大的优势是时间性能好,只比汇编慢20%~30%,C最大的优势是快且面向对象,Java最大的优势是垃圾回收机制,GO语言的目标是具备以上三者的优势
面向对象编程三大特性-封装、继承、多态 封装从字面上来理解就是包装的意思 , 专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系 。系统的其他对象只能通过包裹在数据外面的已经授权的操作来与这个封装的对象进行交流和交互 。也就是说用户是无需知道对象内部的细节,但可以通过该对象对外的提供的接口来访问该对象 。
对于封装而言 , 一个对象它所封装的是自己的属性和方法 , 所以它是不需要依赖其他对象就可以完成自己的操作 。使用封装有三大好处:
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果不想被外界方法,我们大可不必提供方法给外界访问 。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了 。
封装可以使我们容易地修改类的内部实现,而无需修改使用了该类的客户代码 。就可以对成员变量进行更精确的控制 。
2.1 继承综述
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能 , 也可以用父类的功能,但不能选择性地继承父类 。通过使用继承我们能够非常方便地复用以前的代码,能够大大的提高开发的效率 。
继承所描述的是“is-a”的关系 , 如果有两个对象A和B , 若可以描述为“A是B”,则可以表示A继承B,其中B是被继承者称之为父类或者超类,A是继承者称之为子类或者派生类 。
实际上继承者是被继承者的特殊化,它除了拥有被继承者的特性外,还拥有自己独有得特性 。例如猫有抓老鼠、爬树等其他动物没有的特性 。同时在继承关系中,继承者完全可以替换被继承者,反之则不可以,例如我们可以说猫是动物,但不能说动物是猫就是这个道理 , 其实对于这个我们将其称之为“向上转型” 。
诚然,继承定义了类如何相互关联,共享特性 。对于若干个相同或者相识的类,我们可以抽象出他们共有的行为或者属相并将其定义成一个父类或者超类 , 然后用这些类继承该父类,他们不仅可以拥有父类的属性、方法还可以定义自己独有的属性或者方法 。
同时在使用继承时需要记住三句话:
学习继承一定少不了这三个东西:构造器、protected关键字、向上转型
2.2 构造器
通过前面我们知道子类可以继承父类的属性和方法,除了那些private的外还有一样是子类继承不了的---构造器 。对于构造器而言,它只能够被调用,而不能被继承 。调用父类的构造方法我们使用super()即可 。
构建过程是从父类“向外”扩散的,也就是从父类开始向子类一级一级地完成构建 。而且我们并没有显示的引用父类的构造器,这就是java的聪明之处:编译器会默认给子类调用父类的构造器 。
但是,这个默认调用父类的构造器是有前提的:父类有默认构造器 。如果父类没有默认构造器,我们就要必须显示的使用super()来调用父类构造器 , 否则编译器会报错:无法找到符合父类形式的构造器 。
对于子类而已,其构造器的正确初始化是非常重要的,而且当且仅当只有一个方法可以保证这点:在构造器中调用父类构造器来完成初始化,而父类构造器具有执行父类初始化所需要的所有知识和能力 。
对于继承而言,子类会默认调用父类的构造器,但是如果没有默认的父类构造器 , 子类必须要显示的指定父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码) 。
2.3 protected关键字
private访问修饰符,对于封装而言,是最好的选择,但这个只是基于理想的世界,有时候我们需要这样的需求:我们需要将某些事物尽可能地对这个世界隐藏 , 但是仍然允许子类的成员来访问它们 。这个时候就需要使用到protected 。
对于protected而言,它指明就类用户而言,他是private , 但是对于任何继承与此类的子类而言或者其他任何位于同一个包的类而言,他却是可以访问的 。
2.4 向上转型
在上面的继承中我们谈到继承是is-a的相互关系,猫继承与动物,所以我们可以说猫是动物 , 或者说猫是动物的一种 。这样将猫看做动物就是向上转型 。
在这我们通过Person.display(husband) 。这句话可以看出husband是person类型 。
将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型 。由于向上转型是从一个叫专用类型向较通用类型转换,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失 。这就是为什么编译器在“未曾明确表示转型”活“未曾指定特殊标记”的情况下,仍然允许向上转型的原因 。
2.5 谨慎继承
在这里我们需要明确,继承存在如下缺陷:
所以说当我们使用继承的时候,我们需要确信使用继承确实是有效可行的办法 。那么到底要不要使用继承呢?《Think in java》中提供了解决办法:问一问自己是否需要从子类向父类进行向上转型 。如果必须向上转型 , 则继承是必要的,但是如果不需要,则应当好好考虑自己是否需要继承 。
3.1 多态综述
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定 。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性 。
所以对于多态我们可以总结如下:
指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法 。若子类重写了父类中的某些方法,在调用该些方法的时候 , 必定是使用子类中定义的这些方法(动态连接、动态调用) 。
对于面向对象而言,多态分为编译时多态和运行时多态 。其中编辑时多态是静态的 , 主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态 。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性 。
3.2 多态的实现条件
在刚刚开始就提到了继承在为多态的实现做了准备 。子类Child继承父类Father , 我们可以编写一个指向子类的父类类型引用 , 该引用既可以处理父类Father对象,也可以处理子类Child对象,当相同的消息发送给子类或者父类对象时 , 该对象就会根据自己所属的引用而执行不同的行为 , 这就是多态 。即多态性就是相同的消息使得不同的类做出不同的响应 。
Java实现多态有三个必要条件:继承、重写、向上转型 。
继承:在多态中必须存在有继承关系的子类和父类 。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法 。
向上转型:在多态中需要将子类的引用赋给父类对象 , 只有这样该引用才能够具备技能调用父类的方法和子类的方法 。
只有满足了上述三个条件 , 我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为 。
对于Java而言 , 它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时 , 被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法 。
3.3 实现形式
在Java中有两种形式可以实现多态:继承和接口 。
3.2.1、基于继承实现的多态
基于继承的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为 。
基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类 , 子类对象的不同,对方法的实现也就不同 , 执行相同动作产生的行为也就不同 。
如果父类是抽象类,那么子类必须要实现父类中所有的抽象方法,这样该父类所有的子类一定存在统一的对外接口,但其内部的具体实现可以各异 。这样我们就可以使用顶层类提供的统一接口来处理该层次的方法 。
3.2.2、基于接口实现的多态
继承是通过重写父类的同一方法的几个不同子类来体现的 , 那么就可就是通过实现接口并覆盖接口中同一方法的几不同的类体现的 。
在接口的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法 。
继承都是单继承,只能为一组相关的类提供一致的服务接口 。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口 。所以它相对于继承来说有更好的灵活性 。
3.2.3、经典实例分析
运行结果:
分析如下:
①②③比较好理解,一般不会出错 。④⑤就有点糊涂了,为什么输出的不是"B and B”呢?
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法 。(但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了 。)
在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O) 。
上面程序中的A,B,C,D存在如下关系:
分析4:
a2.show(b),a2是一个引用变量,类型为A,则this为a2,b是B的一个实例,于是它到类A里面找show(B obj)方法,没有找到,于是到A的super(超类)找 , 而A没有超类,因此转到第三优先级this.show((super)O),this仍然是a2,这里O为B,(super)O即(super)B即A,因此它到类A里面找show(A obj)的方法,类A有这个方法,但是由于a2引用的是类B的一个对象,B覆盖了A的show(A obj)方法,因此最终锁定到类B的show(A obj),输出为"B and A” 。
分析5:
a2.show(c) , a2是A类型的引用变量,所以this就代表了A,a2.show(c),它在A类中找发现没有找到,于是到A的超类中找(super),由于A没有超类(Object除外),所以跳到第三级,也就是this.show((super)O),C的超类有B、A,所以(super)O为B、A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A 。
分析8:
b.show(c),b是一个引用变量,类型为B,则this为b,c是C的一个实例,于是它到类B找show(C obj)方法,没有找到,转而到B的超类A里面找,A里面也没有,因此也转到第三优先级this.show((super)O),this为b,O为C,(super)O即(super)C即B , 因此它到B里面找show(B obj)方法 , 找到了,由于b引用的是类B的一个对象 , 因此直接锁定到类B的show(B obj),输出为"B and B” 。
按照同样的方法我也可以确认其他的答案 。
当超类对象引用变量引用子类对象时 , 被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法 。这我们用一个例子来说明这句话所代表的含义:a2.show(b);
这里a2是引用变量,为A类型,它引用的是B对象,因此按照上面那句话的意思是说有B来决定调用谁的方法,所以a2.show(b)应该要调用B中的show(B obj),产生的结果应该是“B and B”,但是为什么会与前面的运行结果产生差异呢?这里我们忽略了后面那句话“被调用的方法必须是在超类中定义过的”,那么show(B obj)在A类中存在吗?根本就不存在!所以这句话在这里不适用?那么难道是这句话错误了?非也!其实这句话还隐含这这句话:它仍然要按照继承链中调用方法的优先级来确认 。所以它才会在A类中找到show(A obj),同时由于B重写了该方法所以才会调用B类中的方法,否则就会调用A类中的方法 。
说明:所有的面试题目都不是一成不变的 , 特别是像一线大厂,上面的面试题只是给大家一个借鉴作用,最主要的是给自己增加知识的储备,有备无患 。最后给大家分享Spring系列的学习笔记和面试题,包含spring面试题、spring cloud面试题、spring boot面试题、spring教程笔记、spring boot教程笔记、最新阿里巴巴开发手册(63页PDF总结)、2022年Java面试手册 。一共整理了1184页PDF文档 。私信博主(777)领取 , 祝大家更上一层楼?。?
原文作者:jianyuerensheng
原文出处:
面向对象编程中的“继承”的概念是什么?是指go语言面向对象编程继承:派生自同一个基类go语言面向对象编程继承的不同类的对象具有一些共同特征
“继承”是面向对象软件技术当中的一个概念 。如果一个类A继承自另一个类Bgo语言面向对象编程继承,就把这个A称为"B的子类",而把B称为"A的父类" 。
继承可以使得子类具有父类的各种属性和方法 , 而不需要再次编写相同的代码 。在令子类继承父类的同时 , 可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能 。另外,为子类追加新的属性和方法也是常见的做法 。
有些编程语言支持多重继承,即一个子类可以同时有多个父类,比如C编程语言;而在有些编程语言中,一个子类只能继承自一个父类,比如Java编程语言,这时可以利用接口来实现与多重继承相似的效果 。
在c语言中,一个派生类可以从一个基类派生 , 也可以从多个基类派生 。从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承 。
go语言method有没有继承顺序,实例探讨计算机编程语言,尤其是面向对象的编程语言,虽然语法不同,但其原理大都是相通的!那么go语言的method方法的继承是否也有像python一样的顺序继承的机制呢?我们来用一个简单的实例探讨一下 。
因此 , 结论是go语言结构体(类)的继承并没有像python那样的顺序机制 。
【go语言面向对象编程继承 golang面向接口编程】 go语言method方法的继承、重写,继承顺序的探讨 - 大器编程
关于go语言面向对象编程继承和golang面向接口编程的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。

    推荐阅读