代码是由我们写的,写完后就交给编译器去编译为电脑认识的东西,然后电脑再把它放到 CPU 里面去高速执行,最后再将结果展示给我们。这里面涉及到两次视角转换:一次是我们将写的转换为电脑认识的;一次是电脑执行完后转换为我们认识的。
换句话说就是:我们写的代码是面向我们的,编译为电脑执行的代码就是面向计算机的。
正如下图所示:
我们发现,人看的东西很直白,但是效率又很低;而电脑看的东西则根本无法读懂,但是效率又很高;那么怎么让人容易看明白,电脑执行起来又高效呢?
这就涉及到一个问题:面向对象和面向过程。
面向过程
面向过程是以当前发生的事情为目标进行编程,以过程为核心,不考虑将来可能发生的事情。
简单直白点说就是:我只管现在的,将来啥样我不管,将来好不好改我也不管,我重视的就是当前这个过程,这就是计算机的思维。比如 C 语言,就是这个原理,C 语言是没有对象的,有对象的 C 语言叫 OC,虽然被骂的自己都不认识自己了,但是起码人家有了对象。
比如,现在要做个车库管理软件,用面向过程的思维就是:只有一个类,你要干啥全部写在这里面,趁早把肚子里的需求直接梭哈,否则将来不好改我不改,或者你加钱。我不管好不好改,我只管现在写着爽不爽,快不快,刺激不刺激。
面向过程这么垃圾,为什么还存在呢?
因为有用啊!
面向过程本来就不是用来做客户软件的,是用来写计算机硬件的,谁家计算机硬件天天改?
而且,面向过程主要是针对计算机的,跟计算机谈扩展性?跟计算机谈可读性和可维护性?人家一秒一亿次的速度你有吗?就算可读性再差,也能用速度击败你。
其实,面向过程的速度并不差,反而更快!
为啥子呢?因为面向过程只注重当前,不注重未来的拓展性,所以就没有多余的逻辑,反而执行得更快。
考察如下代码:
int getPrice(int level){
if(level>10) {
return 100;
}else if(level>9) {
return 90;
}else if(level>5) {
return 50;
}
return 10;
}
这是面向过程的,如果将来加了其他 level,那就继续加 if-else 分支,拓展性真是差的一批,再去看面向对象的写法。
interface IGetPrice {
int getPrice(int level);
}
class Level10 implements IGetPrice{
int getPrice(int level) {
return 100;
}
}
class Level9 implements IGetPrice {
}
class ....
嗯,拓展了多了类,用反射根据 level 创建出对应的对象就可以了,将来增加需求只需增加一个类就行了,拓展性确实强。
但是,哪个效率高啊,明显是面向过程效率高啊,只需要调用函数,甚至连对象都不用创建,就可以了。
所以,你看,面向过程还是有用的,尤其是在开发需求修改不频繁,比如写一些工程算法或者写系统软件的时候,就应该采用面向过程,因为这时候要求的不是拓展性,而是速度,是性能!
那么,不要求速度的时候呢?那就应该考虑面向对象了。
面向对象
什么是面向对象呢?
面向对象就是将世间万物都视为对象,针对这些对象的具体行为进行编码。
面向对象有三大特点: 封装、继承和多态。
封装
封装就是把现实事物封装成抽象的类,并可以将类的权限加以修改,把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
简单来说就是:你只能看我给你看的,只能改我让你改的,别的你都不可以。
封装是面向对象的特征之一,是对象和类概念的主要特性,如果没有封装,那么继承就是无意义的,多态也就不存在了。
封装让对象针对不同的角色有不同的表现,让程序更有层级感。
有人说,封装不就是将一些属性改为私有的,一些属性改为公有的吗,也就是改个权限吗?
不是!
修改权限只不过是封装的一部分,而且是次要部分,封装的主要部分是:将具体事物抽象成类这个过程。
比如,现实中的一辆汽车,你把它抽象为一个类:
class Car {}
这才是封装的主要部分。
继承
继承是让类拥有另一个类的数据和函数,表达一种我也是你的关系。
继承的类叫做子类,也叫做派生类;被继承的类叫做父类,也叫基类。
就像人类只有一个亲爹一样,子类也只能有一个父类。C++ 这种就另当别论。
比如,上述的汽车,我们具体封装如下:
class Car {
private int speed = 100;
public int price = 100;
public void run() {
println("run");
}
}
class AudiCar extends Car {
}
我们的AudiCar只需要继承一下Car,那么它就自己带有了Car里面的price和run()了,因为继承了就表示我也是你,那么奥迪车也是车,也有价格、也会跑。那么,里面有个speed是私有的属性,AudiCar有没有这个属性呢?答案是有的,其实也确实是继承了的,只不过不可见而已,不可见且不可修改,那也就等于没有继承。
那你这继承有啥用呢?我自己再写一份不就行了吗?
继承可以省略大量代码,并且容易拓展,子类的公有属性和行为都可以放在父类中,而子类只需要放自己特有的属性和行为就可以了,这样可以统一处理子类的共有属性,方便管理;而子类的特有属性又可以在子类内部自己处理。
如果,我不仅有一个奥迪车,还有宝马、奔驰、布加迪,那么我只要创建出它们的类型,直接继承Car就行了,就不用反复声明run()和price了,这就节省了代码。
有人会问,不对啊,它们的速度都不一样,价格也不一样,这直接继承了肯定有问题啊。
这就涉及到多态了,其实继承就是为了多态。
多态
多态指的是:一个类的子类可以有不同的表现。
比如,还是上述例子:
class Car {
private int speed = 100;
public int price = 100;
public void run() {
pritnln("run");
}
}
class AudiCar extends Car {
public void run() {
println("audi run");
}
}
class BenchiCar extends Car {
public void run() {
println("Benchi run");
}
}
我们让奥迪和宝马分别实现了自己的run(),但是它们价格和速度都一样,这就是多态,可以拥有相同的属性,也可以拥有不同的行为。
上面我们说到,继承就是为了多态。
这就来证明。
假如继承不是为了多态,那么上述的AudiCar和BenchiCar的代码就一模一样,都跟Car一样,那么,为什么要写三个一模一样的类呢?所有地方直接使用Car不就行了吗。
有人说,复用啊,继承除了多态不就是复用吗?
但这不是复用,你这是 CV,你这代码一模一样,复用率都 100% 了,就不叫复用了。
所以,继承就是为了多态。
那么,多态又是为了啥呢?
多态是为了代码容易修改,容易拓展。换句话说,没有多态,就没有设计模式了。
我们考察如下代码,我要一辆汽车,上班开:
public User {
public AudiCar car;
public void goWork(){
car.run();
}
}
简单明了的代码,直接给你一辆奥迪车。如果有一天,我发现奥迪车被偷了,那就只能换个奔驰了?
嗯,那就修改下代码:
public User {
public BenchiCar car;
public void goWork(){
car.run();
}
}
如果后面再想换呢?那就再改?
这样不太好啊,我本来只说要个能跑的车就行,没有说要啥车,只要能跑就行啊,那所有的车都能跑啊,直接这样行吗:
public User {
public Car car;
public void goWork(){
car.run();
}
}
并不指定啥牌子的车,只要是车就行,换句话说,不指定具体的子类,而是指定它们共同的能跑的父类——车!
这样将来不管你啥车被偷都没事,只要不被偷光,我就不用改代码:
Car car = new AudiCar();
car = new BenchiCar();
car = new 还没被偷的Car();
都可以啊,这就是多态的好处,拓展性强,容易修改。
继承和实现的关系
我们上述说到了继承关系只能有一个,也就是只能有一个父类。那么,如果我需要多继承呢?
这时候你就可以用接口了。
接口表示我具有某种功能,继承表示我就是某种东西。
当然,你只能是一种东西,所以你只能单继承;但是你可以有多种功能,所以你可以实现多个接口。
interface CanFly {}
interface CanSwim {}
interface Can72Change {}
class NvWa {}
class Sunwukong extends NvWa implenents CanFly, CanSwim, Can72Change {
}
继承的坏处
上面我们通过实现接口解决了多继承的问题,那么继承有什么坏处呢?
嗯,继承破坏了封装。上面我们说了,封装的次要功能是:将某些属性保护起来,不被别人知道。但是,一旦你被继承了,你那些保护起来的东西,也都被子类继承到了,那么子类会不会滥用呢?
所以,我们就发明了权限关键词这个东西。
-
public:所有人可见。
-
protected:只有自己和子类可见。
-
private:只有自己可见。
-
final:用来修饰方法,避免被重写。
父类只要把自己的属性声明为 private 类型的,那么子类也就不可见了(通过反射修改的行为是可耻的)。
通过这个东西,可以让继承更加完美。