理解JAVA面向对象的三大特性
首先告诉大家JAVA面向对象的三大特性,分别为封装性,继承性,多态性。这三个特性对于编程必不可少,Java亦是如此。下面对各个特性进行一一说明:
封装性
- 理解封装性概念
封装是将代码及其处理的数据绑定在一起的一种编程机制,该机制保证了程序(方法)和数据(属性)都不受外部干扰且不被误用。
理解封装性的一个方法就是把它想成一个黑匣子,它可以阻止在外部定义的代码随意访问内部代码和数据。对黑匣子内代码和数据的访问通过一个适当定义的接口严格控制。
如果想与现实生活中的某个事物作对比,可考虑汽车上的自动传送。自动传送中包含了有关引擎的数百比特的信息,例如你正在以什么样的加速度前进,你行驶路面的坡度如何,以及目前的档位。作为用户,你影响这个复杂封装的方法仅有一个:移动档位传动杆。例如,你不能通过使用拐弯信号或挡风玻璃擦拭器影响传动。所以档位传动杆是把你和传动连接起来的惟一接口。此外,传动对象内的任何操作都不会影响到外部对象,例如,档位传动装置不会打开车前灯!因为自动传动被封装起来了,所以任何一家汽车制造商都可以选择一种适合自己的方式来实现它。然而,从司机的观点来看,它们的用途都是一样的。与此相同的观点能被用于编程。封装代码的好处是每个人都知道怎么访问它,但却不必考虑它的内部实现细节,也不必害怕使用不当会带来负面影响。
- java封装的对象
Java封装的基本单元是类。一个类定义了将被许多对象共享的属性和行为方法。这个类的每个对象都包含这个类定义的行为和属性,好像它们是从同一个类的模子中铸造出来似的。因为这个原因,对象被看作是类的实例,可以说类是实例对象的模板。所以,类是一种逻辑结构,而对象是真正存在的物理实体。
当创建一个类时,你要指定组成这个类的属性和方法。在用Java编写的程序中,方法定义如何使用成员变量。这意味着一个类的行为和接口是通过方法来定义的,类的这些方法对它的实例数据进行操作。
- 访问权限
封装:隐藏对象的属性和实现细节,仅对外公开接口。
(1)private(私有变量):只能被定义它的类访问。
(2)protected(受保护变量):可以被定义它的类及子孙类直接访问,也可以由同一包中的其他类直接访问。
(3)public(公共变量):可以被任何类进行访问。
(4)package:可以被定义它的类和同一个包中的其他类(包括派生子类)所访问.(包变量在声明时常常省略package关键字),在其他包中的子类则不能访问父类的包变量
既然类的目的是封装复杂性,在类的内部就应该有隐藏实现复杂性机制。类中的每个方法或变量都可以被标记为私有(private)或公共(public)。类的公共接口代表类的外部用户需要知道或可以知道的每件事情;私有方法和数据仅能被一个类的成员代码所访问,其他任何不是类的成员的代码都不能访问私有的方法或变量。既然类的私有成员仅能被程序中的其他部分通过该类的公共方法访问,那么你就能保证不希望发生的事情就一定不会发生。
- 例子
属性可用来描述同一类事物的特征, 行为可描述一类事物可做的操作,封装就是要把属于同一类事物的共性(属性与行为)归到一个类中,以方便使用.比如人这个类,可用下面的方式封装:
人{
年龄(属性一)
身高(属性二)
性别(属性三)
做事(行为之一)
走路(行为之二)
说话(行为之三)
}
java代码如下:
public class Human {
private int age;
private double height;
private int sex;
public void doThing(){
}
public void walk(){
}
public void speak(){
}
}
简单来说,封装就是要把属于同一类事物的共性(属性与行为)归到一个类中,相当于用类把属性和方法装在类里面,同时设置各个属性和方法的访问权限。这就是封装性。
继承性
- 理解继承性概念
继承是一个对象获得另一个对象的属性的过程。
继承很重要,因为它支持了按层分类的概念。例如,尊贵的猎犬是狗类的一部分,狗又是哺乳动物类的一部分,哺乳动物类又是动物类的一部分。如果不使用层级的概念,我们就不得不分别定义每个动物的所有属性。使用了继承,一个对象就只需定义使它在所属类中独一无二的属性即可,因为它可以从它的父类那儿继承所有的通用属性。所以,可以这样说,正是继承机制使一个对象成为一个更具通用类的一个特定实例成为可能。下面让我们更具体地讨论这个过程。
一个深度继承的子类继承了类层级中它的每个祖先的所有属性。继承性与封装性相互作用。如果一个给定的类封装了一些属性,那么它的任何子类将具有同样的属性,而且还添加了子类自己特有的属性。新的子类继承它的所有祖先的所有属性。
(1)复用其他对象的代码有两种方法:
①. 继承:是由子对象完全继承父对象的数据和操作,”is”的关系。
②. 包含:把一个对象作为另外一个对象的一部分,从而达到复用的目的,”has”的关系。
(2)继承的理解:
①. 继承是类实现可重用性和可扩充性的关键特征,在继承关系下类之间组成网状的层次结构。
②. 继承避免公用代码的重复开发,减少代码的冗余。
③. 通过继承增强一致性,从而减少模块间的接口和界面
(3)单继承(Java支持):
子类只能有一个直接父类,多继承(Java不支持):子类可以有多个直接父类。
Java不支持类的多继承,但支持接口的多继承。继承通过关键字extends实现。
(4)变量隐藏:
在子类对父类的继承中,如果子类与父类的成员变量同名,此时成为子类隐藏(override)了父类的成员变量。子类若要引用父类的同名变量,要用super关键字做前缀加圆点操作符引用,即“super.变量名”。
(5)方法覆盖:
在子类中重写父类的方法,它们的名称,参数以及返回类型完全相同。通过改写方法体来完成不同的功能。注意子类方法不能缩小父类方法的访问权限 。
(6)super关键字:
如果子类调用父类的构造函数,则通过super()调用来实现。如果子类调用父类的同名方法,则通过super.方法名()来实现。
(7)this关键字:
this变量代表对象本身当类中有两个同名变量,一个属于类的成员变量,而另一个属于某个特定的方法,使用this区分成员变量和局部变量,使用this简化构造函数的调用引用自身对象的成员变量,引用自身对象的成员方法,调用自身的构造方法–this(参数)
public class Food {
public String feel = "神清气爽";
public Food() {
this.feel = feel;
}
public void eat() {
System.out.println("食物补充营养");
}
}
public class Rice extends Food {
public String feel = "精神饱满";
public Rice() {
super();
System.out.println("this:" + this.feel);
System.out.println("super:" + super.feel);
super.eat();
this.eat();
}
public void eat() {
System.out.println("米饭补充碳水化合物");
}
public static void main(String[] args) {
new Rice();
}
}
- 例子
由于封装,使得有共同特征的一类事物的所有描述信息都被归于一类之中,但我们知道,这并不是万能的,有些事物有共性,但还存在区别,比如教师,简单封装起来如下:
教师{
年龄(属性一)
身高(属性二)
性别(属性三)
做事(行为之一)
走路(行为之二)
说话(行为之三)
教书(行为之四)
}
上面对”教师”的封装,与对”人”的封装基本上差不多,只是多了一个特征行为:教书,教师有与人一样的共性, 但我们不能说”人教书”,也就是不能把教书封装到”人”之中去,教书是教师的特征行为之一. 为了省事地封装教师, 可以让教师去继承人,如:
教师 extends 人{
教书(行为之四)
}
public class Teacher extends Human {
public void teach() {
}
}
这样,我们就不用重新定义那些已经被”人”这一个类所封装的那些属性与行为了,而只需要使用继承的方式,在人的基础上拓展教师专有的行为,即”教书”即可把教师描述出来;这样的结果, 即是教师也同时拥有”人”之中所封装的一切属性与行为, 还拥有自己的特征行为”教书”.
多态性
多态性是允许一个接口被多个同类动作使用的特性,具体使用哪个动作与应用场合有关。
简单的理解一下多态,比如:
人这个类,封装了很多人类共有的特性,
教师是人的子类,继承了人的属性与行为,当然教师有自己的特征行为,比如教书授课;
学生是人的子类,继承了人的属性与行为,当然学生有自己的特征行为,比如学习做作业;
基于抽象类说明多态性
public abstract class Human {
private int age;
private double height;
private int sex;
public abstract void doThing();
}
public class Teacher extends Human {
public void teach() {
}
public void doThing() {
System.out.println("授课");
}
}
public class Student extends Human {
public void doThing() {
System.out.println("做作业");
}
}
public class App {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Student student = new Student();
teacher.doThing();
student.doThing();
}
}
现在,当我们需要去描述教师与学生各自的行为的时候, 我们可以分开来说“教师在授课”, “学生做作业”, 但如果我们要站在抽象的角度, 也就是从教师与学生的父类”人”的角度, 来同时描述他们各自的行为时,我们怎么描述?”人在授课”?”人在做作业”?这是不是怪怪的很不合适?不合适的问题就在于, 对于行为主体,我们使用了抽象层次的东东”人”,而对于行为本身, 我们却使用了具体的东东”授课”与”教书”. 怎么解决呢? 那就需要解决抽象与具体的矛盾问题.
既然是站在抽象在角度来描述,那我们把行为抽象一下,不就能同时描述了吗?比如”人在做事”(教师授课与学生做作业都可以说成人在做事),这样就解决了抽象层次与具体层次之间的矛盾.
到了这一步, 我们可以把两个描述: “教师在做事”,”学生在做事” ,两者统一为”人在做事”,然后, 我们可以在”教师”的”做事”行为中去调用教师自己的特征行为”授课”在”学生”的”做事”行为中去调用学生自己的特征行为”做作业”, 所以,当调用”人”去”做事”的时候,如果这个人是教师,那他所做的事实际上就是”教书”,如果这个人是学生,那他所做的事实际上就是”做作业”.也就是说在这里”人”是多态的, 在不同的形态时,特征行为是不一样的, 这里的”人”, 同时有两种形态,一种是教师形态,一种是学生形态,所对应的特征行为分别是”授课”与”做作业”.完成上述的描述过程, 其实就是多态机制的体现.
多态, 就是站在抽象的层面上去实施一个统一的行为,到个体(具体)的层面上时, 这个统一的行为会因为个体(具体)的形态特征而实施自己的特征行为.
多态比起封装与继承来说要复杂很多, 上面的描述很简单, 不用去死抠多态两个字,其实只要明白: 能站在抽象的角度去描述一件事, 而针对这件抽象的事, 对于每个个体(具体)又能找到其自身的行为去执行, 这就是多态.