JAVA面向对象(二) 三大特性

本文详细介绍了Java中的封装、继承和多态性。封装通过private关键字隐藏实现细节,提供公共方法访问。继承用于代码复用,子类继承父类并可重写方法。多态性体现在父类引用指向子类对象时,运行时调用子类重写的方法,增强了代码的灵活性和可维护性。
摘要由CSDN通过智能技术生成

一、封装

把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
隐藏一个类中不需要对外提供的实现细节;
使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
便于修改,增强代码的可维护性;

四种权限修饰符:public protected default private,区分 类的权限 和 成员权限

对于class的权限修饰只可以用public和default(缺省)
public类可以在任意地方被访问。
default类只可以被同一个包内部的类访问。
Java权限修饰符public、protected、(缺省)、private置于类的成员定义前,用来限定对象对该类成员的访问权限:
public(最大权限:当前工程内的类都可以调用)
protected(当前工程下不同包内的子类可以调用)
default(当前包内的不同类可以调用)
private(仅在当前类内可以调用)
注:对于default权限的class,定义protected的属性没有意义,因为该类压根不能被继承,完全可以用更小权限的修饰符 default代替。

二、继承

为什么要有继承?
多个类中存在相同属性和方法时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和方法,只要继承那个类即可。
此处的多个类称为子类(派生类),单独的这个类称为父类(基类 或超类)。

类继承语法规则:class Subclass extends SuperClass{ }
注意:不要仅为了获取其他类中某个功能而去继承。

子类继承了父类,就继承了父类的方法和属性。在子类中,可以使用父类中定义的方法和属性,也可以创建新的属性和方法。
关于继承的规则:
子类不能直接访问父类中私有的(private)的成员变量和方法。也即 访问权限 优先级高于 继承
那继承了吗?继承了,但是仍不能直接访问。

方法重写 override (为了多态性)
子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。
程序执行时,子类的方法将覆盖父类的方法
要求:
1 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型;只有两种 a:和父类相同。b:是父类中返回类型的子类。
3 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限;
子类不能重写父类中声明为private权限的方法,但是子类中可以重新定义与父类同名同参的方法,此时不能称为重写;(因为子类无法直接访问父类的私有方法)
子类方法抛出的异常不能大于父类被重写方法的异常。
这是一种“多态性”:同名的方法,用不同的对象来区分调用的是哪一个方法。

为什么有这些要求
因为,在java多态机制中(都是考虑到满足多态性的情况,才做出的如下限制),对象引用 在编译期是属于父类类型,但是在运行时属于子类类型
例如:Object obj = new String(); 对象引用,编译时是Object类型,运行时才是String类型。
假设父类抛IOException, 子类抛Exception:
在编译的时候,编译器记录父类中抛出的是IOException异常;
但是在运行时期,由于变成了子类类型,子类重写的方法抛出的异常是Exception,显然IOException不能捕获这个比它更大的异常,
因此在运行时期也就出现失败。
假设父类方法权限是public,子类方法是default:
在编译的时候,记录的方法类型是public,因此不同包的类对于该方法的调用都能通过;
但是在运行时期,由于变成了子类类型,权限变小了,显然不同包的类无法调用该方法,因此运行时会报错。
返回值类型同理:如果运行时把父类对象赋给子类引用类型,必然会报错。

注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),
或者同时声明为static的(不是重写)。因为static方法是属于类的,是在类加载时就加载到jvm运行时中的,子类无法覆盖父类的方法。

继承成员变量和继承方法的区别:
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。多态情况下,属性是在编译时确定的,方法是在运行时确定的。声明的父类引用,调用的是父类的属性、子类的方法。

三、多态

(应用:虚拟方法调用与动态绑定)
多态性,是面向对象中最重要的概念,在Java中的体现:
对象的多态性:父类的引用指向子类的对象(可以指向所有子类new的对象)。可以直接应用在抽象类和接口上。(看起来像是专门为接口和抽象类服务的)
方法的多态性:override() 同名同参的方法,用不同的对象来区分调用的是哪一个方法。(方法的多态才是最终目的,因为对象的多态最终体现在对重写方法的调用上)
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
Java 引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。

若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism):
多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法) “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)

注:如果一个引用类型变量声明为父类的类型,但实际引用的是子类对象:
1、那么该变量就不能再访问子类中新增的属性和方法。因为该变量的编译时类型是父类类型,没有子类的新增的属性和方法。
2、如果该变量调用的是重写方法,则仍然执行子类中重写的方法。(虚拟方法调用,下文详述)
如果方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法。因为,子类可看做是特殊的父类。

虚拟方法调用(多态情况下) :
子类中定义了与父类同名同参数的方法(override),在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。因为,方法的调用是在运行时确定的——动态绑定

小结:方法的重载与重写
1、 二者的定义细节:
重载:同一个类中,必须同名、不同参数类型。
重写:子类对父类方法的重写,必须同名、同参。
同时,有三条规定,都是考虑到了对象的多态性,因为多态情况下编译时是父类类型,运行时是子类类型,避免编译通过而运行失败:
返回值类型不大于父类(父类或者父类的子类)、抛出的异常不大于父类(异常类也是对象)、权限不低于父类。
前两个规定具体因为:父类引用能够指向自身及子类对象。而子类引用不能指向父类对象。
权限规定具体因为:其他类对于该类方法的访问。
2、从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰,对于编译器而言,这些同名方法就成了不同的方法,它们的调用地址在编译期就绑定了。所以,对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;重载的方法是在编译期就确定的。
注:Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。

而对于多态情况下方法的重写,只有等到方法调用的那一刻,解释运行器才会根据调用方法的对象确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”;重写的方法是在运行时才确定的

多态小结:
多态作用:提高了代码的通用性,常称作接口重用。
前提:需要存在继承或者实现关系、子类或者实现类中对方法重写。
然后在设计方法时,可以设置传入的形参为父类对象,以实现动态方法调用。
成员方法:
编译时:要查看引用变量所声明的类中是否有所调用的方法。
运行时:调用实际new的对象所属的类中的重写方法。
成员变量:不具备多态性,只看引用变量所声明的类。

多态是编译时行为还是运行时行为?
多态是运行时行为。编译期是按照声明类型编译的,无法看出是哪个类的对象;并且编译期方法名和参数列表时相同的,方法的标识符也相同,运行时才会确定具体调用哪个方法,体现多态性(动态绑定)。
直观上看:根据测试代码无法看出结果,只有真正运行时才知道赋值给引用变量的是哪个对象,调用的是哪个对象的方法!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值