Java入门系列(三)面向对象三大特性之封装、继承、多态
面向对象综述
封装
封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项,或者叫接口。
有了封装,就可以明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者;而外部调用者也可以知道自己不可以碰哪里。
这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
封装成员方法来间接操作类里面的成员变量
使用成员属性来间接访问类里面的成员变量
访问修饰符
private 私有的 只能在该类中访问
protected 受保护的 只能在该类和它的子类中访问
public 公有的 在任何地方都可以访问
继承和多态
什么是继承?
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
继承有什么好处?
继承可以把父类的所有功能都直接拿过来,这样就不必重零做起(比如object类实现了 ToString()方法,则继承自object的子类就无需再次实现该方法,直接调用)
子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写;
继承的第二个好处需要我们对代码做一点改进。当子类和父类都存在相同的Speak()
方法时,我们说,子类的Speak()
覆盖了父类的Speak()
,在代码运行的时候,总是会调用子类的Speak()
。这样,我们就获得了继承的另一个好处:多态。
有了继承,才能有多态。在调用类实例方法的时候,尽量把变量视作父类类型,这样,所有子类类型都可以正常被接收;
理解多态之前首先要对面向对象的里氏替换原则和开放封闭原则有所了解。
里氏替换原则(Liskov Substitution Principle):派生类(子类)对象能够替换其基类(超类)对象被使用。通俗一点的理解就是“子类是父类”,举个例子,
“男人是人,人不一定是男人”,当需要一个父类类型的对象的时候可以给一个子类类型的对象;当需要一个子类类型对象的时候给一个父类类型对象是不可以的!
开放封闭原则(Open Closed Principle):封装变化、降低耦合,软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
因此,开放封闭原则主要体现在两个方面:对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。
对这两个原则有一定了解之后就能更好的理解多态。
任何时候,如果没有合适的类可以继承,就继承自object类。
Person
package cn.cnki.test; public class Person { public void Speak() {
System.out.println("人可以说话");
}
}
Chinese
package cn.cnki.test; public class Chinese extends Person{
public void Speak() {
System.out.println("中国人说中国话");
}
}
Japan
package cn.cnki.test; public class Japan extends Person{
public void Speak() {
System.out.println("日本人说日本话");
}
}
Test
@Test
public void FunctionTest()
{
Person p1=new Chinese();
p1.Speak();
Person p2=new Japan();
p2.Speak();
}
结果
一句话总结:多态就是父类类型的变量可以指向子类类型的对象,调用方法的时候调用的是子类的实现(父变量指向子对象,调用子实现)
为什么需要多态?多态的好处?如果没有多态会怎样?
继续上面的代码,如果此时我们新增一个子类American,只需新增一个American类继承自Person并重写Speak()方法即可,而父类是无需任何修改的。
至此,该程序的扩展性得到了提升,而又不需要查看源代码是如何实现的就可以扩展新功能。这就是多态带来的好处。
public class American extends Person{
public void Speak() {
System.out.println("美国人说美国话");
}
}
对于一个变量,我们只需要知道它是Person类型,无需确切地知道它的子类型,就可以放心地调用Speak()方法,而具体调用的Speak()方法是
作用在Chinese、Japan还是American对象上由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,
而当我们新增一种Person的子类时,只要确保Speak()方法编写正确,不用管原来的代码是如何调用的。
这就是著名的“开闭”原则:
对扩展开放:允许新增Person子类;
对修改封闭:不需要修改依赖Person类型的Speak()函数。
多态的本质作用就是把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
虚方法(virtual)
Java普通方法就是虚方法
抽象类和抽象方法
还是刚才的例子,我们发现Person这个父类,我们根本不需要使用它创建的对象,它存在的意义就是供子类来继承。所以我们可以用抽象类来优化它。
public abstract class Person {
public abstract void Speak();
}
其他代码不变,输出结果依然相同。
由此可见,我们选择使用虚方法实现多态还是抽象类抽象方法实现多态,取决于我们是否需要使用基类实例化的对象.
Person类作为基类,Chinese,Japan类继承American,我们需要使用的是Chinese和Japan创建的对象,根本不需要使用Person创建的对象,所以在这里Person完全可以写成抽象类。
总而言之,是使用虚方法,或者抽象类抽象方法实现多态,视情况而定,什么情况?以上我说的两点~
接口
为什么需要接口?
我们知道少林寺的武僧会武术,歌剧院的演员会舞蹈,虽然所有人都可以Speak,但并非所有的人都会武术都会舞蹈吧,
此时如果我们在父类Person里面新增武术方法和舞蹈方法,然后让所有的子类都去实现这两个方法是否有所不妥呢?
你要是非要这样做,功能完全可以实现,可是这样违背了面向对象开放封闭原则。下次我要再扩展一个戏曲演员唱京剧方法,
我还要去源代码中看下是怎么实现的,然后在戏曲演员类中再次添加唱京剧方法,相同的功能,重复的代码,这样是不合理的,程序也不便于扩展;
接来下我们看接口是如何解决以上问题的
新增武术接口
public interface IWuShu {
void WuShu();//武术
}
新增跳舞接口
public interface ITiaoWu {
void Tiaowu();//跳舞
}
新增武僧类
public class WuSeng extends Person implements IWuShu {
public void Speak() {
System.out.println("武僧会念经");
}
@Override
public void WuShu() {
System.out.println("武僧会武术");
}
}
新增演员类
public class YanYuan extends Person implements ITiaoWu {
public void Speak() {
System.out.println("演员会唱歌");
}
@Override
public void Tiaowu() {
System.out.println("演员会跳舞");
}
}
可以看出实现了IWuShu接口的武僧可以会武术,实现了ITiaoWu接口的演员可以会跳舞。我们想要哪类人拥有哪项技能只需定义一个接口让其去实现即可。
实现了就具备该项技能,反之则不具备。
接口实现多态程序的扩展性得到了大大提升,以后不管是再扩展一个京剧演员,还是篮球运动员,创建一个类,实现这个接口,在主函数中添加该对象就可以了。
也不需要查看源代码是如何实现的,体现了开放封闭原则!
什么时候使用接口,什么时候使用抽象类
如果你想拥有一些方法,并且这些方法有默认实现,那么久使用抽象类
如果你想实现多继承,那么就是用接口吧,java不支持多继承,但是可以实现多个接口
小结
优先选用接口
在既要定义子类的行为,又要为子类提供公共的功能时,应选择抽象类。
虚方法(virtual)和抽象方法(abstract)
相同点:
虚方法(virtual)和抽象方法(abstract)都可以被派生类重写
不同点:
1.虚方法(virtual)有方法实体,抽象方法(abstract)没有方法实体【类似接口】
virtual void SayWord()
{
//代码
}
abstract void SayWord();
2.虚方法(virtual)在派生类中可以不重写,抽象方法(abstract)派生类中必须重写【类似接口】
abstract class Person
{
abstract void SayName();
}
class Man:Person
{
override void SayName()
{
//方法实体
}
}
3.抽象方法(abstract)必须声明在抽象类中,抽象方法结合抽象类实现多态
Java入门系列(三)面向对象三大特性之封装、继承、多态的更多相关文章
- Python 入门 之 面向对象的三大特性(封装 / 继承 / 多态)
Python 入门 之 面向对象的三大特性(封装 / 继承 / 多态) 1.面向对象的三大特性: (1)继承 继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,父类又可以 ...
- JAVA基础——面向对象三大特性:封装、继承、多态
JAVA面向对象三大特性详解 一.封装 1.概念: 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问. 2.好处: 只能通过规定的方法访问数据. ...
- Javascript面向对象三大特性(封装性、继承性、多态性)详解及创建对象的各种方法
Javascript基于对象的三大特征和C++,Java面向对象的三大特征一样,都是封装(encapsulation).继承(inheritance )和多态(polymorphism ).只不过实现 ...
- java面向对象三大特性:封装、继承、多态
一.封装 封装也称信息隐藏,是指利用抽象数据类型把数据和基于数据的操作封装起来,使其成为一个不可分割的整体,数据隐藏在抽象数据内部,尽可能的隐藏数据细节,只保留一些接口使其与外界发生联系.也就是说用户 ...
- Java中面向对象三大特性之——封装
概述 面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改. 封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问.要访问该类的数据,必须通 ...
- C# 面向对象三大特性:封装、继承、多态
面向对象有封装.继承.多态这三个特性,面向对象编程按照现实世界的特点来管理复杂的事物,把它们抽象为对象,具有自己的状态和行为,通过对消息的反应来完成任务.这种编程方法提供了非常强大的多样性,大大增加了 ...
- python面向对象三大特性之一封装
一.什么是封装 在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其 含义是其他程序无法调用. 要了解封装,离不开“私有化”,就是将类或者 ...
- C#面向对象三大特性:封装
什么是封装 定义:把一个或多个项目封闭在一个物理的或者逻辑的包中.在面向对象程序设计方法论中,封装是为了防止对实现细节的访问. 封装的优点 1. 隔离性,安全性.被封装后的对象(这里的对象是泛指代码的 ...
- Objective-C----MRC内存管理 、 自动释放池 、 面向对象三大特性及封装 、 继承 、 组合与聚合
1 MRC练习 1.1 问题 引用计数是Objective-C语言采用的一种内存管理技术,当一个对象被创建在堆上后,该对象的引用计数就自动设置为1,如果在其它对象中的对象成员需要持有这个对象时,则该对 ...
随机推荐
- 用JavaScript添加选择按钮的背景颜色和juqery添加选择按钮的背景色
在项目开发中经常遇到要选择的按钮,选择完之后被选择的按钮的背景色会发生变化,表示被选择 样式图如下: 每点击一个数字,相应的背景色变为蓝色,其他的依旧是白色,先用JavaScript实现 html代码 ...
- Android Apollo MQTT入门
一.Apache Apollo服务器其实是一个消息中转站 下载地址 http://activemq.apache.org/apollo/download.html 服务搭建方式,参看博客Android ...
- UVAlive6439_Pasti Pas!
题目是说给你一个字符串,现在要你用一些特殊的符号代替这个字符串中某一些子串,使得被替换后的串是一个回文串. 现在要你求替换后的字符串的最大的可能的长度. 其实这个题目没有什么固定的算法哦,我直接暴力就 ...
- BZOJ3162 独钓寒江雪(哈希+树形dp)
数独立集显然是可以树形dp的,问题在于本质不同. 假设已经给树确立了一个根并且找到了所有等效(注意是等效而不是同构)子树,那么对转移稍加修改使用隔板法就行了. 关键在于找等效子树.首先将树的重心(若有 ...
- MT【122】一个重要的不平凡的无穷级数
求证:$1+\dfrac{1}{4}+\dfrac{1}{9}+\cdots +\dfrac{1}{n^2}+\cdots = \dfrac{\pi^2}6$. 解答:考虑$$\dfrac{\sin ...
- 【刷题】洛谷 P3768 简单的数学题
题目描述 由于出题人懒得写背景了,题目还是简单一点好. 输入一个整数n和一个整数p,你需要求出(\(\sum_{i=1}^n\sum_{j=1}^n ijgcd(i,j))~mod~p\),其中gcd ...
- Harbor快速部署到Kubernetes集群及登录问题解决
Harbor(https://goharbor.io)是一个功能强大的容器镜像管理和服务系统,用于提供专有容器镜像服务.随着云原生架构的广泛使用,原来由VMWare开发的Harbor也加入了云原生基金 ...
- Mininet 系列实验(七)
实验内容 本实验在基于 Mininet 脚本的不同拓扑环境下使用 OpenDaylight 控制交换机行为.任务一:一台交换机两台主机,从1端口进入的数据流转发到 2 端口,从 2 端口进入的数据流转 ...
- Android Paging库使用详解
Android分页包能够更轻易地在RecyclerView里面缓慢且优雅地加载数据. 许多应用从数据源消耗数据, 数据源里面有大量的数据, 但是一次却只展示一小部分. 分页包帮助应用观测和展示大量数据 ...
- CF960G Bandit Blues 【第一类斯特林数 + 分治NTT】
题目链接 CF960G 题解 同FJOI2016只不过数据范围变大了 考虑如何预处理第一类斯特林数 性质 \[x^{\overline{n}} = \sum\limits_{i = 0}^{n}\be ...