Java面向对象之三大主要特征
文章目录
前言
记录学习Java面向对象编程中的三大主要特征:封装、继承、多态。
一、面向对象的特征之一:封装性(封装与隐藏)
1.1 封装性简介
- 程序设计追求“高内聚,低耦合”
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
- 低耦合:仅对外暴露少量的方法用于使用。
- 隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。简而言之:把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的思想。
- Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
- 隐藏一个类中不需要对外提供的实现细节;
- 使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
- 便于修改,增强代码的可维护性。
1.2 封装性思想的具体体现
- 体现一:将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值;
- 体现二:不对外暴露私有的方法;
- 体现三:单例模式(将构造器私有化);
- 体现四:如果不希望类在包外被调用,可以将类设置为缺省。
1.3 四种访问权限修饰符
- Java权限修饰符public、protected、(缺省)、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
- 对于class的权限修饰符只可以用public和(缺省)。
- public类可以在任意地方被访问。
- default类只可以被同一个包内部的类访问。
1.4 总结封装性
- Java提供了4种修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用是可见性的大小。
二、面向对象的特征之二:继承性(inheritance)
2.1 继承性简介
- 为什么要有继承?
- 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
- 此处的多个类称为子类(派生类),单独的这个类称为父类(基类)。可以理解为:“子类 is a 父类”。
- 类的继承语法规则
- class Subclass extends SuperClass{ }
- 子类继承了父类,就继承了父类的方法和属性。
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
- 在Java中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
- 关于继承的规则:子类不能直接访问父类中私有的(private)的成员变量和方法。
- 作用
- 继承性的出现减少了代码的冗余,提高了代码的复用性。
- 继承的出现,更有利于功能的扩展。
- 继承的出现让类与类之间产生了关系,提供了多态的前提。
- 注意:不要仅为了获取其他类中某个功能而去继承
- Java只支持单继承和多层继承,不允许多重继承
- 一个子类只能有一个父类
- 一个父类可以派生出多个子类
- class SubDemo extends Demo{ } //ok
- class SubDemo extends Demo1,Demo2… //error
- 子父类是相对的概念
- 子类直接继承的父类,称为直接父类;间接继承的父类称为:间接父类
- 所有的Java类(除java.lang.Object类之外)都直接间接的继承了Object类
2.2 方法的重写
- 定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
- 要求:
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型(若是基本数据类型则必须相同,若是返回值为引用类型A,则可以是A类或A类的子类)
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
- 子类不能重写父类中声明为private权限的方法
- 子类方法抛出的异常不能大于父类被重写方法的异常
- 要求:
2.3 关键字(super)
- 在Java类中使用super来调用父类中的指定操作:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造器中调用父类的构造器
- 注意
- 尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
- super的追溯不仅限于直接父类,还可以是间接父类
- super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
- super调用父类构造器
- 子类中所有的构造器默认都会访问父类中空参数的构造器
- 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器
同时,只能”二选一”,且必须放在构造器的首行 - 如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
- 子类对象实例化的全过程
- 从结果上来看:(继承性)
- 子类继承父类以后,就获取了父类中声明的属性或方法。
- 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
- 从过程上来看:
- 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,
直到调用了java.lang.object类中空参的构造器为止。
正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。 - 明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
- 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,
- 从结果上来看:(继承性)
三、面向对象的特征之三:多态性(Polymorphism)
3.1 多态性简介
- 理解多态性:可以理解为一个事物的多种形态。
- 多态性的定义:
- 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类引用)
- 例:Person person = new Man(); (其中Person是父类,Man是子类)
- 多态性的使用:虚拟方法调用(减少方法的重载)
- Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
- 简称:编译时,看左边;运行时,看右边。
- 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
- 多态情况下:
- “看左边”:看的是父类的引用(父类中不具备子类特有的方法)
- “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
- 多态性的使用前提:①类的继承关系 ②方法的重写
- 对象的多态性:只适用于方法,不适用于属性(属性调用的依旧是父类的属性)。
- 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
3.2 对象的多态
- 在Java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向(引用)多种不同类型的对象
- Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
- Person p = new Student();
- 子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
3.3 虚拟方法的调用(Virtual Method Invocation)
- 正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
- 虚拟方法调用(多态情况下)
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
- 编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。——动态绑定
3.4 方法重载与重写(从编译和运行的角度区分)
- 方法重载:是指允许存在多个同名方法,而这些方法的参数不同
- 编译器根据方法不同的参数表,对同名方法的名称做修饰。
对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
- 编译器根据方法不同的参数表,对同名方法的名称做修饰。
- 方法重写(多态中):在子类中可以根据需要对从父类中继承来的方法进行改造
- 对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
3.5 多态性附加知识(对象类型转换)
- 关键字:instanceof
- x instanceof A:检验x是否为类A的对象,返回值为boolean型。
- 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
- 如果x属于类A的子类B,x instanceof A值也为true。
- 对象类型转换 (Casting )
- 基本数据类型的Casting:
- 自动类型转换:小的数据类型可以自动转换成大的数据类型
如long g=20; double d=12.0f - 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
如 float f=(float)12.0; int a=(int)1200L
- 自动类型转换:小的数据类型可以自动转换成大的数据类型
- 对Java对象的强制类型转换称为造型
- 从子类到父类的类型转换可以自动进行 (向上转型(多态) Person p = new Man())
- 从父类到子类的类型转换必须通过造型(强制类型转换)实现 (向下转型)
- 无继承关系的引用类型间的转换是非法的
- 在造型前可以使用instanceof操作符测试一个对象的类型 (避免引发ClassCastException异常)
if (e instanceof Student) { //先用instanceof判断e对象是否是Student类的对象或是否是Student的父类对象 Student me = (Student) e; // 再将e强制转换为Student类型 System.out.pritnln(me.getschool()); }
- 基本数据类型的Casting:
3.6 多态性总结
- 多态作用:提高了代码的通用性,常称作接口重用
- 前提:
- 需要存在继承或者实现关系
- 有方法的重写
- 成员方法:
- 编译时:要查看引用变量所声明的类中是否有所调用的方法。
- 运行时:调用实际new的对象所属的类中的重写方法。
- 成员变量:不具备多态性,只看引用变量所声明的类。
总结
以上是对Java面向对象的封装、继承、多态的总结。