面向对象的主要特征包括抽象、封装、继承、多态
一、抽象
抽象不打算解决所有问题,而是其中一部分问题,暂时不去理会部分细节。抽象包括两个方面:过程抽象;数据抽象。 数据抽象就是针对对象的属性; 过程抽象就是针对对象的行为特征,即方法。
由此展开抽象类与接口的异同:
抽象类:如果类中包含抽象方法,那么就是抽象类,也就是说,抽象类中一定有抽象方法,但不一定全是抽象方法。通过abstract修饰类或者方法(不能修饰属性)。抽象类不能被实例化,但是可以创建一个对象使其指向具体子类的一个实例。
接口:接口指的是方法的集合,接口中所有的方法都没有方法体,通过interface实现。可以通过接口实现多重继承,接口中的成员变量都是static final类型。
因为抽象类可以包含具体类,所以某些时候抽象类比接口更有优势。
两者的相同点:
- 都不能被实例化(面向接口编程)
- 接口的实现类或者是抽象类的子类都只能实现具体方法后才能被实例化。
不同点:
- 接口至于定义没有实现(全部是抽象方法),抽象类可以有定义和实现(部分抽象方法)。
- 接口需要用implements实现,而抽象类只能通过extends继承。
- 接口强调特定功能的实现,设计理念为“has a ”;抽象类强调所属关系,设计理念为“is a”.
- 接口成员变量默认为public static final,必须赋予初值,所有的成员方法都是public absract。但是抽象类类似于普通类,只不过多了抽象方而已,但是其抽象方法不能用private/static/synchronized/native修饰,不能有花括号,分号结尾。
- 接口一般用于实现常用的功能,有利于日后的维护或者添加删除方法;抽象类一般倾向于公共类的角色,不适于日后的修改。
二、继承
对象的一个新类可以从现有的类中派生,这个过程称之为类继承。新类继承了原始类的属性和方法,新类叫做原始类的子类,或者派生类;原始类叫做父类,或者是基类。派生类可以修改或增加新的方法使之更适应当前的需要。
使用格式:class 子类 exends 父类
继承的特点:
- 不能支持多重继承,也就是说只能有一个父类(只能有一个爸爸),但是如果想要实现多重继承可以使用接口实现(多个师傅)。
- 子类只能继承非私有的属性和方法(public、protected)。
- 当子类的成员变量和方法和父类重名时,此时不会继承,而是会覆盖。
组合和继承(两种复用方式): 组合是指在新类中创建原有类的对象,然后利用原有类的功能 继承是是面向对象的只要特性之一,它允许子类调用父类的功能
注意事项:
- 不要为了重用而大量的使用继承,因为过多的使用继承会破坏代码的可重用性,并且使之臃肿不堪,并且增加维护的难度和成本。
- 为了实现多态使用接口和组合的方式比使用继承和的方式更好扩展,例如策略模式。
总之,能用组合尽量不使用继承。
三、封装
封装是指将客观事物抽象成类,每个类对自身的数据和方法进行保护,让可信的类访问,对不可信的类进行信息隐藏。
四、多态
多态是指不同类的对象对同一消息作出响应。 多态包括:参数化多态;包含多态
多态的实现机制 当同一个操作作用在不同的对象时,会有不同的语义,从而产生不同的结果。 两种表现方式: 重载(overload):指的是具有相同的方法名,但参数不同,在编译时就可以确认调用哪儿个方法,因此是编译时多态。 需要注意的点:
- 重载通过不同的参数来区分的。包括不同的参数个数、不同的参数类型、不同的参数顺序。
- 不能通过访问权限、返回值类型、以及抛出的异常来进行重载。
- 如果父类的访问权限为private,那么就不会出现重载
覆盖(override):子类可以覆盖父类的方法,因此不同的方法在子类与父类中的表现形式不同。在Java语言中,父类的引用变量不仅可以指向父类的实例对象,也可以指向子类的实例对象(接口同样如此)。而程序的调用方法在运行时才动态绑定,因此这种动态绑定实现了多态,这种多态在运行时才决定调用哪儿个方法,因此叫做运行时多态。 需要注意的点:
-
覆盖的类必须与被覆盖的类有相同的函数名和参数。
-
覆盖的类必须与被覆盖的类返回值相同。
-
······抛出的异常一致
-
被覆盖的方法不能是private,否则达不到覆盖的效果。
注意:类中的方法才有多态的概念,类中的成员没有!
重载和覆盖的区别:
- 重载是同一个类中的方法之间的关系,是水平关系;覆盖是父类与子类之间的关系,是垂直关系。
- 重载是有多个方法之间产生;覆盖是在两个方法之中产生
- 重载要求参数列表不同;覆盖要求相同
- 重载是根据参数列表的不同决定的;覆盖是由对象的类型决定的。