以下都是一些个人的观点,不喜勿喷。仅作为自己的学习心得,备忘录,希望高手指教一二。
现在大三了,正在找IT公司实习。在面试的时候,经常会被问到一个问题——什么是面向对象?请解释下面向对象的概念?
我接触比较多的是C++和Java,C++有面向对象的成分,而Java可以说是一个纯面向对象的语言,那么到底什么是面向对象?一谈到面向对象,我的脑海里首先浮现的便是面向对象的特性:抽象,封装,继承,多态等。
我的理解,就是将现实世界中的一些具有共同性质(这里的共同性质的包含范围,影响最终抽象成的对象范围)的事物抽象成一个对象(类),这些抽象后的对象被赋予一个名字用来指代这些事物,比如说人类,鸟类,鱼类……这里的对象(类)是一个概念,很抽象的概念,我们最终需要去实例化它。然后我们考虑下人类,如果是个人类,那么他有什么性质呢?人会跑,会吃饭,不会飞,有男女之分,有年龄,有两只手,两只脚……我们就把这些共同性质分为两部分:属性和行为。属性就是特性,年龄,身高,体重等,行为就是可以做的事,吃饭,睡觉,写代码……到这里有没有人会想,人为什么会吃饭,而不会飞?或许这个问题看起来很蠢,但是却告诉我们一件事:一般对象(类)的定义都是程序猿自己的想法,这些想法或许是约定俗成,或许基于惯性思维(换句话说,程序猿想让这个对象是怎么样的就可以是怎么样的。最后影响的是程序猿编程的效率和其他人的理解)。所以我完全可以定义一个鸟人类,让他飞一会。
//抽象出来的人类
class Man
{
public:
void eat(Food f) // 吃饭
{...}
void run() // 跑步
{...}
void sleep() // 睡觉
{...}
......
// void fly() // 飞翔?
// {....}
private:
int age; //年龄
int height; //身高
int weight; //体重
string name; //名字
bool isMale; //性别
};
在这里你或许会注意到面向对象的另一个特性:封装。用来解释一个问题,人是怎么吃饭的?这也是一个蠢问题,中国人拿筷子或勺子吃饭,外国人拿叉子和刀子吃饭,如果你对怎么吃有兴趣,可以看看美食节目,但这里我们看的是行为的具体实现——肌肉是如何运动的?大脑下达了什么命令?细胞间发生了什么?
或许这些令你觉得完全摸不着头脑,那么我们换个更简单的例子——汽车会行驶,那么汽车是怎么行驶的呢?答案:踩下油门!那么在踩下油门和车子行驶之间发生了什么?汽油被挤入活塞,然后被点燃,突然膨胀的气体推动活塞运动,活塞运动带动马达,马达带动车轮,然后车就开了。然后我想说,我们看的到这些吗?我们需要关心这些吗?不需要,我们只需要关心输入和输出,剩下的就让行为自己去完成。我们只需要关心,踩下了油门后车子能够行驶,具体的实现细节被封装。我们只需要关心,拿起筷子我们就可以大块吃肉,具体的实现细节被封装。拿到我们经常用的函数来说,我们在使用函数的时候,只需要关心输入什么后,函数会输出我们需要的东西,而输入与输出之间发生了什么,我们不用关心。(或许有人会问这些都不关心,怎么能写出好的代码呢?我想说,既然你封装了这个函数,你就要相信他能处理好)
// 中国人吃饭
void eat(Food f)
{
// 1.先准备好筷子
// 2.再用筷子夹取食物f
// 3.然后送到嘴里
// 4.咬碎咀嚼
// 5.如果食物没有吃完,请返回第3步
// 否则,结束吃饭。
}
// 外国人吃饭
void eat(Food f)
{
// 1.先准备刀子和叉子
// 2.用叉子固定,用刀子分割食物f
// 3.用叉子叉起送到嘴里
// 4.咬碎咀嚼
// 5.如果食物没有吃完,请返回第2步
// 否则,结束吃饭。
}
// 虽然都是吃,但是我们不用关心他们是怎么吃的。已经把吃的方法给封装了。
int main()
{
Food a;
Chinese c;
c.eat(a);
Foreigner f;
f.eat(a);
return 0;
}
对象(类)与对象(类)之间不可能都是相互独立,互不联系的。他们之间或许有包含关系,比如哺乳动物类与人类;或许有兄弟关系,比如人类与熊猫类都是哺乳动物类,他们就可能是兄弟类;也有可能是朋友类(友元类)等等。这里我们遇到了面向对象第三个特性:继承。子类继承父类,那么子类就拥有了和父类相似的行为和属性。具体的分析可以看看《C++ Primer》这样的基础书籍对继承的介绍。这里继承对于面向对象来说,或许描述的就是 对象(类)与 对象(类)之间的一种常见并且重要的关系。当然有时候你会发现,子类可能有很多个父类,这也是C++的一类继承——多重继承。
// 父类1
class Parent1
{...};
// 父类2
class Parent2
{...};
// 单继承
class Child1 : public Parent1
{...};
// 多重继承
class Child2 : public Parent1, public Parent2
{...};
当然还有最后一个特性……多态。何为多态?就是多种形态的意思。那么什么东东会有多种状态呢?主要有两类运行时多态(动态多态)和编译时多态(静态多态)。运行时多态就是我们常说的:创建一个父类指针指向一个子类实例,一般通过虚函数实现,只有在运行时才确定具体所指向的对象是父类,还是子类;编译时多态也可以叫做函数重载,拥有相同的函数名但参数等不同的函数,在调用时会根据参数类型自动调用合适的函数。
//运行时多态,动态多态
class Parent
{...};
class Child : public Parent
{...};
int main()
{
Parent *p=new Child();
return 0;
}
//编译时多态,静态多态
class Parent
{
public:
void fun() // 返回值不能作为重载的依据
{}
void fun(int a)
{}
void fun(int* a)
{}
void fun(const int& a) // 有时候会和fun(int a)冲突
{}
void fun() const // 常量性不同,也可以重载函数
{}
};
但是面向对象只是指这些特性吗?对于现在的编程可能不确切。面向对象还有许多概念,例如面向对象的分析(OOA,Object Oriented Analysis),面向对象的设计(OOD,Object Oriented Design),以及我们经常说的面向对象的编程实现(OOP,Object Oriented Programming)。“面向对象”逐渐成为一种思想,是一种以对象为中心的编程思想(Java)。不同于“面向过程”,是一种以过程为中心的编程思想。也不同于“面向结果”,是一种以结果为中心的编程思想(SQL语句)。
// 面向过程的设计
void 上车买票
{...}
void 汽车启动
{...}
void 汽车到站
{...}
// 面向对象的设计
class 汽车
{
public:
void 上车买票
{...}
void 汽车启动
{...}
void 汽车到站
{...}
};