面向对象

来源:互联网 发布:java 线程状态 编辑:程序博客网 时间:2024/05/21 01:47

1.为什么要面向对象

       面向对象给我们一种忽略细节的能力,忽略的越多,人类有限的智力就可以容纳越多越复杂的问题,提高生产效率。我在这里先打个比方:我指挥一个人,他能够明白我的意思;我指挥100个人,我需要成立组织架构;我治理一个国家,需要有行政部门帮我管理;总理管理拥有13亿人口的中国,虽然智力和精力有限,但是他可以通过管理顶级部门。就这样,把具体的功能封装起来,我们可以忽略很多细节,我们的智商才够用。在java程序中,部门=类,人力资源部门=人力资源类,所有Java代码都以类为组织单元。

2.什么是面向对象

       面向对象Object Oriented,简称OO;面向对象编程Object Oriented Programming简称OOP;面向对象是一种思想,面对事物的思考方式;Java就属于一种面向对象的语言。

3.面向对象和面向过程有什么区别

       面向过程语言:提供小面积的问题解决思路;

       面向对象语言:通过“封装、继承、多态”,让程序分析和设计能容纳更大的编程范围和系统规模。

       面向过程==>蛋炒饭==>把米饭和鸡蛋混在一起炒==>如果不喜欢鸡蛋,需要倒掉炒饭重做;

       面向对象==>盖浇饭==>把米饭和盖菜分开来搭配==>如果不喜欢盖菜,把盖菜拨掉重新加。

4.面向对象的三大基本特征

封装

封装的概念            

       封装就是把属于同一类事物的共性(包括属性与方法)归到一个类中,以方便使用。其中属性用来描述同一类事物的特征,方法描述一类事物可做的操作。

       封装也称为信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对象内部方法的实现细节,但可以根据对象提供的外部接口(对象名和参数)访问该对象。

封装的目的和作用

       (1) 实现了专业的分工。将能实现某一特定功能的代码封装成一个独立的实体后,各程序员可以在需要的时候调用,从而实现了专业的分工。

       (2) 隐藏信息、保证安全。尽量隐藏类中的属性和方法,只对外开放部分接口和外界联系,外部无法直接访问和修改类中属性,从而实现类和对象的安全性。

       (3) 类的属性可以对外只读。

       (4) 类可以完全控制自己的属性。

       (5) 类可以修改属性的数据类型,而不会影响到外部的调用。

封装的操作步骤

       (1) 将属性私有化

       (2) 编写get和set方法 

继承

继承的概念

       子类中有一些共有的属性和方法,把这些共有的属性和方法提取出来,放到父类里,然后子类就可以继承这些属性和方法。

继承的目的和作用

       使用继承可以优化设计,实现代码的复用。注意:继承后子类自动拥有了父类的属性和方法,但特别注意的是,父类的私有属性和构造方法并不能被继承。另外子类可以写自己特有的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法即方法的重写。

使用继承的条件

    符合“is-a”关系的设计都可以用继承(例如:小猫是一种哺乳动物,藏獒是一种狗,金鱼是一种鱼等等)。

使用继承的注意事项

       java中类只能单根继承,即在java语言中,只存在单继承,也就是说,子类只允许有一个父类。这也是我们为什么相对于继承,更推荐使用接口实现的原因之一。有关接口的概述,后面笔者会再加叙述。需要特别注意的是,一旦达成继承关系,子类就会自动拥有父类中所有非private权限的属性以及方法。

子类访问父类成员

子类访问父类成员的注意事项

       (1) 使用super关键字,super代表父类对象 ;

       (2) 在子类构造方法中调用且必须是第一句。

       (3) 不可以访问父类中定义为private的属性和方法。

访问父类构造方法

       Super(); //调用无参构造方法

       Super(name); //调用带一个参数name的构造方法

       Super(name,health);//调用带参数name和health的构造方法

访问父类方法

       Super.getName(); //调用父类里的getName()方法

       Super.print(); //调用父类里的print()方法

子类访问父类成员的小结

       子类:如果子类里定义了带参构造,和无参构造,那么父类里必须有带参构造和无参构造。

       子类:如果子类里没有带参构造和无参构造,那么默认调用父类的无参构造.

       注意:

       (1) 子类在没有通过this关键字调用自身的构造方法,也没有通过super关键字调用父类的构造方法,那么默认调用父类的无参构造方法。(顺序)先初始化父类的属性,再执行父类的无参构造,再执行子类的无参构造。

       (2) 子类通过super(属性)调用父类的带参构造函数,那么只执行父类的带参构造和自己的带参构造,不会执行父类的无参构造。

       (3) 如果子类通过this关键字调用自身的其他构造方法,那么在这些你调用的其他构造方法里应用以上两条A,B原则。

重写(overwrite)

重写的概念

      子类根据需求对从父类那里继承得来的方法进行重新编写,注意重写时,可以用super.方法的方式来保留父类的方法,但是构造方法却不能重写。另外,方法重写又称为方法覆盖。

方法重写的规则

      (1) 方法名相同;
      (2) 参数列表相同(包括参数的个数,类型和顺序);
    (3) 返回值类型相同或者是其子类;
      (4) 访问权限不能严于父类。(子类的访问修饰符的等级大于等于父类。比方说父类是protected,那么子类可以是protected,或者是public,常用修饰符高低依次为:public > protected > deault [什么都不写,默认修饰符] > private);
      (5) 父类的静态方法不能被子类覆盖为非静态方法,父类的非静态方法不能被子类覆盖为静态方法;
      (6) 父类的私有方法不能被子类覆盖;
      (7) 子类不能抛出比父类方法更多的异常。

重载(overload)

重载的概念

      在同一个java类当中,拥有多个方法,这些方法拥有相同的名字,但是参数列表(参数的个数、类型和顺序)却有所不同,此外它和访问权限和返回值无关。

重载和重写的区别

     通过上面有关重写和重载的介绍,我们不难发现,两者之间的区别:
     方法的重写:涉及的是两个类,父子类,方法名相同,参数列表(包括参数的个数、类型和顺序)相同,访问权限(访问修饰符)不能严于父类方法的权限(访问修饰符),返回值相同或者是其子类。
     方法的重载:涉及的是一个类,同一个类,在这个类当中,多个方法拥有相同的名字,但是参数列表(包括参数的个数、类型和顺序)却不相同。对于访问权限和返回值也没有特殊规定和要求。

多态

多态的概念

      多态概念的提出,是以封装和继承为基础的。多态是指相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。多态的定义也可以表述为,同一个引用类型,使用不同的实例,而执行不同的操作。还可以表述为:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法(第三种表述来自百度官方)。正所谓万变不离其宗,尽管各种表述均有异同,但核心观点还是一致的,关键在于个人的理解。

多态的好处(网上已经有详细说明,笔者在此点到即止,不再赘述)

      (1) 可替换性。多态对已存在代码具有可替换性。

      (2) 可扩充性。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。

      (3) 接 口 性。多态是父类通过方法签名,向子类提供了一个共同接口,由子类来重写或者重载它而实现的。

      (4) 灵 活 性。它在应用中体现了灵活多样的操作,提高了使用效率。

      (5) 简 化 性。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作。

多态存在的必要条件

      (1) 要有继承;

      (2) 要有重写;

      (3) 父类引用指向子类对象。

使用多态的注意事项

      (1) 使用父类类型的引用,需要指向子类的对象;

      (2) 该引用只能调用父类里面定义的方法和变量;
      (3) 如果子类当中重写了父类中的一个方法,则在调用这个方法的时候,将会调用子类当中的这个方法(这也称之为动态调用);
      (4) 如果父类引用指向子类对象,但是父类和子类拥有同名的属性或者同名的静态方法,则执行的是父类的静态方法,获取的是父类的属性(这也称之为静态绑定);
      (5) 如果子类继承一个父类之后,对从父类那里继承得来的方法加以重载,则父类引用是不能使用子类重载后的那个方法的。

面向对象三大基本原则的小结

     (1)封装:封装隐藏了类的内部实现机制,可以在不影响使用者的前提下修改类的内部结构,同时保护了数据;
     (2)继承:继承是为了重用父类代码,子类继承父类就拥有了父类的成员。
     (3)多态:多态允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。

5.面向对象的六大基本原则

里氏替换原则

里氏替换原则的概念

       里氏替换原则,外文名为Liskov Substitution Principle,于1987年提出,官方给出的原始定义是:继承必须确保超类所拥有的性质在子类中仍然成立。

里氏替换原则的个人理解

       按照里氏替换原则的定义,不难理解:任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉基类,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的功能。

接口隔离原则

接口隔离原则的概念

      客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。

接口隔离原则的个人理解

       接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中定义的方法尽量少,也就是说我们要为各个类建立专用的接口,而不要试图建立一个很庞大的接口供所有依赖于它的类去调用。

       在程序设计中,依赖多个专用接口要比依赖一个综合的接口更加灵活。通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。

       注意:单一职责原则和接口隔离原则看似很像,其实不然:一方面:单一职责原则注重的是职责;接口隔离原则则注重对接口依赖的隔离。另一方面:单一职责原则主要是约束类,其次才是接口和方法。它针对的是程序中的实现和细节。而接口隔离原则主要约束接口,主要针对抽象,针对程序整体框架的构建。

运用接口隔离原则一定要适度,接口设计的过小也不好,会使得接口数量过多,设计变得复杂化。

依赖置换原则

依赖置换原则的概念

       程序要依赖于抽象接口,不要依赖于具体实现。该原则要求:对抽象进行编程,不需要对实现进行编程,降低客户与实现模块之间的耦合。

依赖置换原则的个人理解

       面向过程的开发:上层调用下层,上层依赖于下层,当下层发生剧烈变动的时候,上层也要跟着变动,这会导致模块的复用性降低,而且加大了开发成本。

面向对象的开发:很好解决上面的问题,一般情况下,抽象的变化概率很小,让用户的程序依赖于抽象,实现的细节也依赖于抽象,这样做的话,即使实现细节不断变化,只要抽象不变,客户程序就不需要变化。大大降低客户程序与实现细节的耦合度。

开闭原则

开闭原则的概念

      面向对象设计中,可复用设计的基石;软件实体对扩展开放,对修改关闭。

      通俗地说:软件中系统中的各种组件,例如,模块、类、功能等,应该在不修改原有代码的基础上,引入新的功能。不必改动模块的源代码。

      实现开闭原则的关键,在于“抽象”,把系统的所有可能的行为抽象成一个抽象底层。我们在实际开发过程的设计开始阶段,就需要罗列出系统所有可能的行为。它是面向对象设计的终极目标。

开闭原则的个人理解

单一职责原则

单一职责原则的概念

       一个类或者模块应该有且只有一个改变的原因。

单一职责原则的个人理解

       如果一个类承担的职责过多,就等于把这些职责耦合在一起了,一个职责的变化可能会削弱或者抑制这个类去完成其他职责的能力。此原则的核心就是实现:高内聚,低耦合。

       注意:程序设计中,耦合发生常常是在不经意之间。究其原因,大都因为某种原因,某一职责被分化为颗粒度更细的多个职责了。解决方案:遵守单一职责原则,将不同的职责封装到不同的类或模块中。

迪米特法则(最少知道原则)

最少知道原则的概念

       一个对象应当对其他对象有尽可能少的了解。一个实体应当尽可能少的与其他实体发生相互作用,每一个软件单位对其他的单位都只有最少的知识,而且局限于那些和本单位密切相关的软件单位。

最少知道原则的个人理解

       迪米特法则的初衷是:降低类之间耦合,由于每个类尽量减少对其他类依赖,因此很容易使得系统的功能模块功能独立,相互之间没有或者很少有依赖关系。

迪米特法则不希望类之间建立直接的联系。如果真的又需要建立联系,则通过第三方(友元类)来实现转达。所以应用迪米特法则的后果会出现:系统中存在大量起传递作用的中介类,这在一定程度上增加了系统的复杂度。


未完待续……


原创粉丝点击