阿里面经之解答by cmershen(1)——Java的基本特性,面向对象的六大特征等

来源:互联网 发布:手机如何进入淘宝店铺 编辑:程序博客网 时间:2024/05/22 22:15

在牛客网上看到一份阿里面经,真假先不谈,就其中总结概括的知识点和常见问题还是不错的,在面试中也是经常会被问到的。
美中不足的是,里面的解答有的太过冗杂,有的则太过简略。本学妹按自己的理解,将这些问题重新整理,给出一份自己的理解,与读者共同学习共同进步。

1.自我介绍(略)
2.做过的项目(就说dts好啦~~)


(以下是重点的开始)


3.Java的四个基本特性:抽象,封装,继承,多态,对多态的理解以及项目中哪些地方用到多态
原po的解释太过繁杂,根本记不住
抽象:将一类对象的共同特征抽象成类。包括数据抽象(即成员变量)和行为抽象(即方法)。
继承:将已知类和继承信息合并起来创建新类的过程。
封装:隐藏内部的实现逻辑,只向外界提供最简单的接口的过程。
多态:允许不同子类型的对象,对同一消息做出不同的响应。

 3.1 多态的理解
 方法重载和重写:重载是同一个类中,同名方法有不同的参数列表 例如:

f(arg1,arg2) {    ...}f(arg1,arg2,arg3) {    ...}

方法重写:
是在子类中重写父类已有的方法或抽象方法,例如:

class ClassA {    public void f() {        System.out.println("ClassA f()");    }}class ClassB extends ClassA {    public void f() {        System.out.println("ClassB f()");    }}

要实现多态需要做的两件事:方法重写,对象造型(ClassA b = new ClassB();)
项目中多态的应用:
在dts中的抽象语法树部分使用过多态,类SimpleNode代表抽象语法树的节点,但抽象语法树有许多种类型的节点,所以SimpleNode类中有一些抽象方法,在不同类型的节点中分别使用不同的实现类实现不同的处理逻辑。
(其实根本就不是这样的,编一个吧,DTS的设计模式无力吐槽。。。)
4.面向对象和面向过程的区别?用面向过程可以实现面向对象吗?
面向过程以事件为中心,分析出解决问题的步骤,然后用函数把这些步骤实现。
面向对象以类和对象为中心。
举例:”汽车发动“和”汽车停止“
对于面向对象来说是这样的:

Class Car {    ...    void start();    void stop();}Car c=new Car();c.start();c.stop();

对于面向过程来说是这样的:

void start(Car* c)void stop(Car* c)

5.重载和重写如何确定调用哪个函数?
重载:发生在同一个类中,同名方法具有不同参数,根据调用函数时的入口参数,选择与之对应的重载函数。
重写:发生在父类与子类之间,根据不同的子类对象确定调用哪个方法。

6.面向对象开发的六个基本原则:
(1)单一职责(SRP):There should never be more than one reason for a class to change.
即一个类只有一个引起变化的原因。
举例:一个调制解调器有四个功能,分别为拨号,挂断,接受消息,发送消息。

public class Modem {    public void dial();    public void hangup();    public void receive();    public void send();}

实际上Dial(拨通电话)和Hangup(挂电话)是属于连接的范畴,而Receive(收到信息)和Send(发送信息)是属于数据传送的范畴。这里类包括两个职责,显然违反了SRP。
因此要重构Modem类,从中抽象出两个接口,一个专门负责连接,另一个专门负责数据传送。依赖Modem类的元素要做相应的细化,根据职责的不同分别依赖不同的接口。如下:

public interface IConnection {    public void dial();    public void hangup();}public interface IDataTransfer {    public void receive();    public void send();}public class Modem implements IConnection,IDataTransfer{}

这样无论单独修改连接部分还是单独修改数据传送部分,都彼此互不影响。
(2)里氏替换原则:“Subtypes must be substitutable for their base types”
也就是说子类必须能够替换成他们的基类。
举例:一个士兵用枪(手枪、步枪、机枪)射击杀人可描述为:

public class Soldier {    public void killEnemy(AbstractGun gun) {        gun.shoot();    }}public abstract class Gun {    public void shoot();}public class HandGun extends Gun{    public void shoot() {        System.out.println("HandGun shoot!");    }}public class Rifle extends Gun{    public void shoot() {        System.out.println("Rifle shoot!");    }}public class MachineGun extends Gun{    public void shoot() {        System.out.println("MachineGun shoot!");    }}

如果public void killEnemy(AbstractGun gun)中不能调用父类AbstractGun,则违反了里氏代换原则。
(3)依赖倒置原则(Dependence Inversion Principle DIP )
所谓依赖倒置原则就是要依赖于抽象,不要依赖于具体。简单的说就是对抽象进行编程,不要对实现进行编程。面向对象的开发很好的解决了这个问题,一般的情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变化,只要抽象不变,客户程序就不需要变化。
例如一个自动驾驶系统可以在福特车和本田车上使用,提供了三个方法:开车,停车,转弯。

如果不考虑依赖倒置原则来实现,应该这样写:

public class HondaCar {    public void Run() { System.out.println("本田车启动了!"); }    public void Turn() { System.out.println("本田车拐弯了!"); }    public void Stop() { System.out.println("本田车停止了!"); }}public class FordCar {    public void Run() { System.out.println("福特车启动了!"); }    public void Turn() { System.out.println("福特车拐弯了!"); }    public void Stop() { System.out.println("福特车停止了!"); }}public class AutoSystem {    public enum CarType{ Ford,Honda}    private HondaCar hondacar=new HondaCar();    private FordCar fordcar=new FordCar();    private CarType type;    public AutoSystem(CarType carType) {        this.type = carType;    }    public void RunCar() {        if (this.type == CarType.Honda)            hondacar.Run();        else if (this.type == CarType.Ford)            fordcar.Run();    }    public void StopCar() {        if (this.type == CarType.Honda)            hondacar.Stop();        else if (this.type == CarType.Ford)            fordcar.Stop();    }    public void TurnCar() {        if (this.type == CarType.Honda)            hondacar.Turn();        else if (this.type == CarType.Ford)            fordcar.Turn();    }}

若该自动驾驶系统增加了对宝马车的支持,则需要增加BMWCar类,并在AutoSystem里按规律增加支持BMWCar的语句。如果又增加了10种,20种车的支持呢?如果不只是Run,Stop,Turn三种功能,而是100种,10000种功能呢?改动量将会变得极大。
这样我们需要抽象出来,使得Run,Stop,Turn三个函数只与车接口耦合,而不直接依赖具体车型,应该重构以上功能如下:

public interface ICar {    public void Run();    public void Turn();    public void Stop();}public class HondaCar implements ICar{    public void Run() { System.out.println("本田车启动了!"); }    public void Turn() { System.out.println("本田车拐弯了!"); }    public void Stop() { System.out.println("本田车停止了!"); }}public class FordCar implements ICar{    public void Run() { System.out.println("福特车启动了!"); }    public void Turn() { System.out.println("福特车拐弯了!"); }    public void Stop() { System.out.println("福特车停止了!"); }}public class AutoSystem {    public ICar car;    public AutoSystem(ICar car) {        this.car=car;    }    public void runCar() {        this.car.run();    }    public void stopCar() {        this.car.stop();    }    public void turnCar() {        this.car.turn();    }}

这样AutoSystem只与ICar耦合,无论增加多少种车,也不需改变AutoSystem类,只需新增加ICar接口的实现类即可。
4合成聚合复用:子类是超类的一个特殊种类,而不是超类的一个角色。区分“Has-A”和“Is-A”。只有“Is-A”关系才符合继承关系,“Has-A”关系应当用聚合来描述。
尽量使用聚合而不是继承,只有严格满足“Is-A”关系才使用继承。
5.开放封闭原则:Software entities(classes,modules,functions etc) should open for extension ,but close for modification.
软件实体应该对扩展开放,而对修改封闭。
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立其工作,而不要对类做任何修改。
举例:
银行业务系统有3种功能,存款、取款、转账。若不考虑OCP,则这样设计:

public class BankProcess{    public void Deposite();    public void Withdraw();    public void Transfer();}public class BankStaff{    private BankProcess bankpro = new BankProcess();    public void BankHandle(Client client) {        switch (client.Type) {            case "deposite":                bankpro.Deposite();                break;            case "withdraw":                bankpro.Withdraw();                break;            case "transfer":                bankpro.Transfer();                break;        }    }}

如果该系统升级,增加了第四种功能,那么BankProcess和BankStaff类都要修改。正确的做法是把三种业务抽象成接口,当业务员依赖于固定的抽象时,对修改就是封闭的,而通过继承和多态继承,从抽象体中扩展出新的实现,就是对扩展的开放。
6接口隔离原则(ISP)

接口隔离原则 认为:”使用多个专门的接口比使用单一的总接口要好”。因为接口如果能够保持粒度够小,就能保证它足够稳定,正如单一职责原则所标榜的那样。多个专门的接口就好比采用活字制版,可以随时拼版拆版,既利于修改,又利于文字的重用。而单一的总接口就是雕版印刷,显得笨重,实现殊为不易;一旦发现错字别字,就很难修改,往往需要整块雕版重新雕刻。

迪米特法则:一个类应该对其他类有尽可能少的了解。简称为“不要和陌生人说话”。
(DTS中的应用:遍历控制流图时使用观察者模式。)

1 0
原创粉丝点击