一、面向对象程序设计(OOP)的三大基本特征(定义、特性、优缺点、java中的表现形式、应用)
1.封装
封装的定义
通俗的说:普通人对计算机进行应用操作,只需知道如何在计算机上连网,如何浏览网页,下载东西等。不需要知道网络的具体底层搭建,计算机的基本结构组成(这些可以看作被封装)。
对于编程来说:将对象的属性(状态)和方法(行为)封装成一个类。而这个类可以有很多对象。举例:人这个类(class),可以有很多不同的人(每个人即为每个对象),而人都有相同的属性描述词(身高,体重),也有相同的行为描述词(吃,喝,睡)。程序语言则将属性描述词定义为对象的属性,行为描述词定义为方法。
简单来讲: 将现实世界的事物抽象成计算机领域中的对象,对象同时具有属性和行为,这种抽象就是封装.
封装的特性
封装的一个重要特性: 数据隐藏. 对象只对外提供与其它对象交互的必要接口,而将自身的某些属性和实现细节对外隐藏,通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。这样就在确保正常交互的前提下,保证了安全性.
封装的表现形式
1)对象属性和方法的封装,使用private,protected,public设置访问权限
2)类的封装,将多个类封装到一个jar包中
封装的好处
1)通过隐藏对象的属性来保护对象内部的状态(隐藏信息、实现细节)。
2)提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展(将变化隔离,类内部的结构可以*修改,增加内部实现部分的可替换性)。
3)禁止对象之间的不良交互提高模块化(良好的封装能够减少耦合)。
4)可以对成员变量进行更精确的控制。
5)容易保证类内部数据间的一致性,从而提高软件的可靠性。
封装的弊端
因为有时候,class被封装在jar中,即class中,没办法去逐步去看源代码。
封装,即将class文件放入jar,供java文件调用,是java的特点,是java的优势。
同时,却也是java的劣势,封装不利于深入调试、debug,即没办法深入读每一个源码。
2.继承
继承的定义
通俗的说:儿子(“子类”或“派生类”)继承父亲(“基类”、“父类”或“超类”)的产业(属性和方法)。
对于编程来说:派生自同一个基类的不同类的对象具有一些共同特征。
详细的说:继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。
继承的特性
面向对象的一个重要特性是复用性.继承是实现复用性的一个重要手段.可以在不重复编写已经实现的功能的前提下,对功能进行复用和拓展.面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
继承的实现方式
继承概念的实现方式有三类:实现继承、接口继承、可视继承。
1)实现继承是指直接使用基类的属性和方法而无需额外编码的能力(extends)
2)接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(implements)
3)可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
继承的优缺点
优点
1)代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
2)提高代码的重用性;
3)子类可以形似父类,但又异于父类,“龙生龙,凤生凤,老鼠生来会打洞”是说子拥有 父的“种”,“世界上没有两片完全相同的叶子”是指明子与父的不同;
4)提高代码的可扩展性,实现父类的方法就可以“为所欲为”了,君不见很多开源框架的扩展接口都是通过继承父类来完成的;
5)提高产品或项目的开放性。
缺点
1)继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;
2)降低代码的灵活性。子类必须拥有父类的属性和方法,让子类*的世界中多了些约束;
3)增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在 缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大段的代码需要重构。
补充
开发的原则:高内聚,低耦合。
耦合:类与类的关系
内聚:就是自己完成某件事情的能力
3.多态
多态的定义
通俗的说:在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息(obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。 比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同.
编程的说:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。即不同对象具有同一个方法名,但方法的实现方式可能不同,执行的结果效果可能不同。
简单的说:就是用基类的引用指向子类的对象。
多态的好处
1)应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。//继承
2)派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。 //多态的真正作用。(提高了代码的维护性(继承保证);提高了代码的扩展性(由多态保证))
优点概括:消除类型之间的耦合关系、可替换性、可扩充性、接口性、灵活性、简化性。
多态的弊端
***父类不能使用子类的特有功能。***不能使用子类的特有属性和行为。
解决办法:
A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了)
B:把父类的引用强制转换为子类的引用。(向下转型)
对象间的转型问题:
向上转型:
Fu f = new Zi();//F为父类,Zi为子类
向下转型:
Zi z = (Zi)f; //要求该f必须是能够转换为Zi的.(所属关系)
多态存在的三个必要条件
1)继承
2)重写
3)父类引用指向子类对象(类A,另外一个类B继承了类A。有如下代码:A a = new B() ; 这就称父类的引用a 指向了子类的对象new B(),就是这个意思。调用方法的时候,调用子类的实现)
多态和继承的好处举例
假如你的A是一个接口,或者抽象类,那么是不可以创建对象的,这样写就可以也能够它的引用去指向他子类或者实现类的对象。这句话写在这里感觉不明显。其实主要是用在方法的参数上,也就是多态性(java的三大特性之一,可想何等重要)。举例
假如有一个类,这个类需要实现吃各种水果,有香蕉,苹果,梨子等等。
我们是不是就得写:
public void eat(Banana banana) { //.....}
public void eat(Apple apple) { //.....}
等等等等。这样是不是很麻烦啊,但是我要是把这个方法写成:
public void eat(Fruit fruit){//....}
这样只要继承Fruit或者实现Fruit接口,就都可以作为eat的参数,很大的简化了编程。
多态的表现形式
在java语言中,多态性体现在哪些方面
1)方法重载: 通常是指在同一个类中,相同的方法名对应着不同的方法实现,这些方法名相同的方法其区别在于他们需要的参数不同。即采用不同的方法形参表,区分重载方法要求形参在数据类型、个数和顺序的不同。
2)方法重写: 方法重写主要用于父类和子类间。子类重写父类的方法,只是对应的方法实现不同,其方法名和参数都相同。
3)抽象类: 在java语言中,一个类中的方法只给出了标准,而没有给出具体的实现方法,这样的类就是抽象类。例如父类就是抽象类,它不会被实例化的类。
4)接口: 在多态机制中,接口比抽象类使用起来更加方便。而抽象类组成的集合就是接口。
二、面向对象程序设计五大基本原则
1.单一职责原则SRP(Single Responsibility Principle)
是指一个类的功能要单一,不能包罗万象。
2.开放封闭原则OCP(Open-Close Principle)
一个模块在扩展性方面应该是开放的,而在更改性方面应该是封闭的。
比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码
这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。
3.替换原则(the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。
比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。
4.依赖原则(the Dependency Inversion Principle DIP)
具体依赖抽象,上层依赖下层。
假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口: 这样就达到了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。
通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。
5.接口分离原则(the Interface Segregation Principle ISP)
模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来
原文链接:https://blog.csdn.net/qq_42733811/article/details/88380562