Java 小知识点

面向对象软件系统(OOPS)的四大主要特征为:

封装(Encapsulation)

继承(Inheritance)

多态(Polymorphism)

抽象(Abstraction)

封装(Encapsulation)

封装机制在对象之间提供了一种隐藏域可见性的协议。Java中使用可见性修饰符private将方法和变量限制在类内部。Java提供的可见性修饰符包括public/ default/ protected/ private,用来在不同层面上隐藏变量、方法和类,但最终目的在于封装那些不需要进行修改的东西。实践表明,每个类应该只存在一种被修改的原因,而封装(Encapsulate)让这种“唯一原因”原则成为现实。

同时,最佳实践表明,封装意味着将会经常改变的东西隐藏起来,以防止对其他类造成破坏。

封装的优点

我们可以通过隐藏属性来保护对象的内部状态;

能够防止对象之间不恰当的相互作用,进而促进代码模块化;

增强可用性;

在特定对象之间维护互访协议;

封装以促进代码维护;

可以独立地进行代码修改

多态(Polymorphism)

多态是指使用相同的接口来应对不同的底层形式(例如数据类型)的能力。这就意味着同一个类可以使用一个共同的接口来实现多种不同的功能,并能通过传递特定的类引用来动态触发这些功能。

一个经典的多态的实例为“形状”类(Shape),以及所有继承Shape的类,如方形(square)、圆形(circle)、多面体(dodecahedron)、不规则多边形(irregular polygon)和长条(splat)等。在这个例子中,每个类中都拥有一个自己的Draw()函数,客户端程序代码可以简简单单地如下所示:

Shape shape=new Square ();

执行Shape.area() 可以得到任何形状的正确面积。

多态的美妙之处在于,不同类里的代码不需要知道自己所在的是哪个类,它们的使用方式都是一样的。

面向对象的编程语言在运行时所实现的多态过程叫做动态绑定。

注:多态是指根据调用函数的对象来选择更具针对性的方法的特性。当没有抽象类的时候就可以使用多态。

多态的优点

可用于创建可重用代码:一旦类被创建,实施和测试,就可以直接进行使用而不考虑具体的代码细节;

提供更为泛化和松耦合的代码;

编译时间更短,开发更为敏捷;

动态绑定;

可以使用同一个接口的不同方法来实现不同的功能;

可以使用相同的方法签名来代替完全实施。

方法覆盖实现多态:覆盖涉及到两个不同的方法,一个父类的方法,另一个则是子类中的方法,两个方法具有相同的函数名和方法签名。

覆盖可以以不同的方式对不同的对象类型定义相同的操作,例如:

while(it.hasNext()) {
    Shape s = (Shape) it.next();
    totalArea += s.area(dim); //多态方法调用,将根据对象类型自动调用正确的方法
}

方法重载、Ad-hoc多态性和静态多态

重载涉及的是同一个类内具有相同名称,但方法签名不同的多个方法。可以用不同的方法为不同的数据定义相同的操作。我们经常所说的静态多态实际上并不是真正意义上的多态。

方法重载实际上就是指两个方法使用相同的名称,但参数不同。这与继承和多态完全没有关系。重载方法不是覆盖方法。

Java中基于泛型的参数多态性

当进行类声明时,一个属性域名称可以与多种不同的数据类型相关联,一个方法也可以与不同的参数类型和返回类型相关联,Java支持使用泛型的参数多态性。例如,一个list对象可以通过泛型来接收它所包含的数据类型:

List list = new ArrayList();

为什么在Java里我们不能覆盖静态(static)方法?

覆盖依赖于具体的类实例。多态的关键之处在于你可以继承一个类,而该类的子类所定义的实例对象又可以对父类中定义的方法进行了覆盖。而静态方法是不依赖与具体实例的,因此覆盖这一概念不适用于静态方法。

在Java设计早起有两点考虑直接导致了这一现象。第一是对性能方面的考虑:之前人们对Smalltalk语言(一种面向对象编程语言)运行太慢(垃圾回收和多态调用所致)的批评不绝于耳,Java的设计者决定回避这一弊端。第二是考虑到Java的预期受众主要是C++开发人员,而使静态方法能直接被调用刚好能迎合C++编程人员的开发习惯,同时由于不用上溯类层级结构来查找要调用的方法,而是直接调用指定类中的特定方法,这一设计使得代码运行非常快速。

继承(Inheritance)

继承是指派生类中包含了基类中的所有的行为(即方法)和状态(即变量),并能通过该派生类进行访问。继承的关键好处在于它提供了代码重用和避免重复的一遍机制。

继承类通过重用父类的方法并添加一些新的功能来扩展应用程序的功能。这回导致紧耦合,如果你想对父类进行修改,你必须清楚其所有子类的具体细节以防止阻断。

这是一种软件复用性,新类(子类)继承已有的父类,重用父类的特征并能添加一些新的功能。

因此,举例来说,如果你有一个Parent类和一个扩展(使用关键字extends)Parent类的Child类,那么Child类继承了Parent类所有特征。

优点

促进重用性; 建立逻辑“is a”关系,如:Dog is an animal. 使代码模块化; 避免冲突。

缺点

紧耦合:子类的实现依赖于父类,导致紧耦合。

抽象(Abstraction)

抽象意味着只需要开发类的接口和功能声明,而不需要实现具体的实施细节。抽象类呈现的是接口,而不需要具体实现,将对象的实施与行为或实现分离开来,进而通过隐藏无关的细节来减少复杂度。

优点

通过使用抽象,我们可以将不同类别的东西分离开来; 经常需要修改的属性和方法可以被分离出来形成一个单独的类别,而那些主要留下的部分就不需要进行修改了,进而增强面向对象的分析与设计(OOAD)原则,即“代码应该易于扩展,而不应该经常修改”; 简化领域模型的表征。

抽象和封装的区别:

封装作为一种策略,被用作广义抽象的一部分。封装是与对象状态相关的——对象将自己的状态封装起来并对外界不可见,类外部的用户只能通过该类的方法来与其进行交互,但不能直接改变其状态。因此,类可以将与状态相关的实施细节通过抽象隔离开来。

抽象是一个更泛化的概念,可以通过子类来实现具体的功能。例如:在Java标准库中,List是“一串事物”的抽象,ArrayList和LinkedList是List的两个具体的类型,作用于抽象List的代码同样抽象地不指明具体所使用的List类型。

如果没有通过封装隐藏底层状态,也就不可能进行抽象处理。也就是说,如果一个类的内部状态全部都是公开的,内部功能无法被更改,该类也就无法进行抽象。

什么是抽象类和抽象方法?

在程序设计过程中,你希望基类只是其派生类的一个接口,也就是说,你不希望任何人能实例化该基类。你只是想隐式(可以实现多态性)地提出它,以便可以使用它的接口。那么你可以使用abstract关键字来定义一个抽象类。

为该抽象类设定一些限制,所有使用该抽象类的子类都必须实现其中的抽象方法,并提供多态性。

抽象类中可以既包括抽象方法和具体方法,如果一个方法是抽象方法,其所在的类必须被声明成抽象类。反之不然,如果一个类是抽象类,其中不一定包括抽象方法。

如果一个方法只提供了方法签名,但没有被具体实现,则这个方法是一个抽象方法,该方法的具体实现是在扩展该抽象类的子类中进行的。

抽象方法不能被实例化,其他类只能扩展它。

什么时候使用抽象类?

抽象类定义了一些默认的行为,而将具体的功能留给子类来实现。例如:List是一个接口,而抽象类AbstractList提供了List的默认方法,这些默认方法可以被子类ArrayList继承或重新定义。