Java 程序员第一个要了解的基础概念就是:什么是面向对象编程(OOP)?
玩过 DOTA2 (一款推塔杀人的游戏)吗?里面有个齐天大圣的角色,欧洲战队玩的很溜,国内战队却不怎么会玩,自家人不会玩自家的神话英雄,实在有点悲哀。
我们用 Java 定义一个孙悟空的类吧:
public class SunWukong { private Integer healthPoint;// 生命值 private Integer magicPoint;// 魔法值 /** * 棒击大地 */ private void rodHittingTheEarth() { } /** * 丛林之舞 */ private void jungleDance() { } }
我们给他两个属性(生命值 healthPoint 和魔法值 magicPoint),给他两个技能(棒击大地 rodHittingTheEarth() 和丛林之舞 jungleDance())。
假如我是 LGD(老干爹,国内知名 DOTA 战队)的教练 357(已辞职),一名聪明绝顶的好教练(真的秃头了),主要负责赛前的 BP(Ban 和 Pick,拌掉对方的英雄和选己方英雄) 工作。
现在,是 TI9 的小组赛(已结束),LGD 对阵 OB(欧洲战队,两届冠军) 的第一局。
我——357 在 BP 阶段先 BAN 掉了比较克制孙悟空的兽王和发条技师,然后在选人阶段(PICK)挑选了水晶室女这个冰美人来和孙悟空这个一身毛的家伙搭配。
那么,在我选孙悟空的这个阶段,就等于说创建了一个孙悟空的对象,也就相当于 Java 中的 new SunWukong()。选好了孙悟空,让谁来玩呢?自然是队中号称世界第一中单的 maybe。
面向对象编程(OOP)其实并不难懂,不就是英文单词 Object-Oriented Programming 的首字母缩写嘛。OOP 给我们一种概念,就是“万物皆对象”——我们可以尽可能的发挥想象,把现实中的物(包括动物、人物、甚至无生命的事物)抽象为一种计算机语言(比如 Java)能懂的模型,那么接下来,计算机解决现实中的问题就变得简单了起来。
OOP 的强大之处就在于,它使我们不必依照计算机的限制来模拟现实中的物;我们可以基于现实中要解决的问题来构建对象,使这个对象具有现实中物所具有的特征(对应 Java 类中的属性)和行为(对应 Java 类中的方法)。物不同,对象所具有的属性和方法就会不同。
还拿 DOTA 中的英雄来说吧。
所有的英雄都可以抽象为一个类(对象是类的实例化),这个类包含了英雄对应的成长属性(生命值、魔法值、伤害值、护甲值等等),以及对应的技能行为。那么,怎么区分每个英雄的不同呢?比如说冰美人水晶室女是一个魔法辅助类的英雄,因此,她的初始化生命值自然比敏捷类的齐天大圣低一些,但魔法值高一些;另外,他们的技能也完全不同——孙悟空拿着一根金箍棒,所以他的技能就有棒击大地;水晶室女拿着一根法杖,所以她的技能就有冰霜新星、冰封禁制等。
面向对象编程给我们了一些重要的启示:
1)万物皆对象。
2)程序是对象相互协作的一个过程,他们通过发送消息来告知彼此要做的事情。
3)每个对象都有自己的类型,也就是“每个对象都是某个类(抽象数据类型)的一个实例”。尽管每个对象都是唯一的(在内存中拥有一个唯一的地址),但具有相同状态(属性、变量)和类似行为(方法)的对象可以归属为一个类。
4)每个对象都提供了某种服务。比如说孙悟空可以跳上树上(丛林之舞),然后再跳下来对区域内的敌人造成减速和伤害(乾坤跳跃)。
面向对象编程的代码易维护、易复用、易扩展,因为面向对象有三大特性:封装、继承、多态。
1)封装,每个类(对象的模板)可以自由地定义属性和方法,使其有别于其他的类。封装可以隐藏对象的内部细节,使其对外形成一道边界,只保留有限的属性和方法与其他对象进行交互。封装的原则是使对象以外的部分不能随意的访问和操作对象的内部属性,从而避免了外界对对象内部属性的破坏。
2)继承,子类可以轻松地复用父类的代码(非 private 修饰的属性和方法),只需要一个关键字——extends。如果父类的属性不满足子类的需求,可以追加;如果父类的方法不满足子类的需求,可以覆盖。
3)多态,父类中定义的方法被子类继承之后,可以表现出不同的行为。这使得同一个方法在父类及其各个子类中具有不同的语义。例如:“椭圆”和“多边形”都是“几何图形”,假如它们都有一个名叫“绘图”的方法的话,方法内容肯定是不同的。
多态性体现在两个方面:由方法重载实现的编译时多态和方法重写实现的运行时多态。
编译时多态:在编译阶段,具体调用哪个被重载的方法,编译器会根据参数的不同来确定调用相应的方法。
运行时多态:由于子类继承了父类所有的属性(私有的除外),所以子类对象可以作为父类对象使用。程序中凡是使用父类对象的地方,都可以用子类对象来代替。一个对象可以通过引用子类的实例来调用子类的方法。
封装、继承、多态这三个特性对于初学者来说,有点头大。不过不用担心,我会在后面的文章中继续深入地介绍。