Java中的继承关系与方法覆盖
什么是继承关系?
基于某个父类对象定义的加以拓展,从而产生新的子类定义,子类可以继承父类原来的某些定义,也可以增加原来父类没有的定义,或者覆写父类中的某些特性。
从面向对象的角度上来说,继承是一种从一般到特殊的关系,是一种“ is a ”的关系,即子类是父类的拓展,是一种特殊的父类,比如狗是动物的一种特殊情况,狗属于动物。
在Java语言当中,存在多个类的时候,我们使用“extends”关键字来表示子类和父类之间的关系。
在Java语言当中,类和类之间的继承关系,只允许单继承,不允许多继承,也就是说一个类A,只能有一个直接的父类,不能出现A类同时继承于类B和类C,但是在Java中,允许多重继承。
多重继承的例子:
动物有胎生和孪生之分,胎生动物有老虎,老虎又分华南虎,东本虎,孟加拉虎等。
在Java中除了Object类之外,每一个类都有一个直接的父类
比如:class Student extends Person{}
我们就说此时的Student的直接父类是Person,那么Person的父类又是谁呢?
Object类是Java语言的根类(老祖宗,任何类都是Object的子类)
class Person{}等价与 class Person extend Object{}
Object类要么是一个类直接父类,要么是一个类间接父类
继承关系的作用
(1)解决了代码重复的问题
(2)真正的作用,表现出了一个体系。
子类可以继承哪些父类成员?
子类继承父类之后,可以拥有父类的某一些状态和行为(子类复用了父类的功能或状态)
子类到底继承了父类的哪些成员(根据访问修饰符来判断):
(1)如果父类中的成员使用public修饰,子类继承;
(2)如果父类中的成员使用protected修饰,子类也继承,即使父类和子类不在同一个包中;
(3)如果父类和子类在同一个包中,此时的子类可有继承父类中缺省修饰符的成员;
(4)如果父类中的成员使用private修饰,子类打死都继承不到,因为private只能在本类中访问;
(5)父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同。
方法覆盖(复写/覆写)
子类拓展了父类,可以或得父类的部分方法和成员变量。可是当父类的某个方法不适合本身的特征时,此时怎么办?
如:企鹅和鸵鸟是鸟中一个特殊品种,所以企鹅/鸵鸟是鸟类的一个子类,但是鸟类有飞翔的功能,但是对应于企鹅/鸵鸟,飞翔的行为显然不适合它。
这个时候就用到了方法覆盖,如下代码。
创建了一个子类对象Penguin ,并调用fly方法。这个过程其实是先从子类中去寻找fly方法,如果找到就执行,若是找不到,则继续去父类中去找。
//鸟类 clas Bird{ public void fly(){ System.out.println("我可以飞翔"); } } //企鹅类 class Penguin extends Bird{ // 重新定义了fly行为 public void fly(){ System.out.println("我不可以飞翔"); } }
方法覆盖的原则(一同两小一大):Override
一同:
(1)实例方法签名必须相同(方法签名=方法名+方法的参数列表)
两小:
(2)子类方法的返回值类型是和父类方法的返回类型相同或者其子类;
或者说,子类可以返回一个更加具体的类。如下代码:
但是在实际开发中,没必要这样做,直接使用(1)叙述的就行了
class Bird { public Object fly(){ System.out.println("fly"); return null; } } class Penguin extends Bird { public String fly(){ System.out.println("Can't fly"); return "xxx"; } }
(3)子类方法声明抛出的异常类型和父类声明抛出的异常类型相同或者是其子类。
子类方法声明抛出的异常类型小于或等于父类方法声明抛出的异常类型;
子类方法可以同时声明抛出多个属于父类方法声明抛出异常类的子类(RuntimeExcption类型除外);
一大:
(4)子类方法的访问权限比父类方法的访问权限更大或相等。
class Person { public void fly(){ System.out.println("fly"); } } class Student extends Person { // 编译报错,子类方法的访问权限要比父类方法的访问权限更大或相等。 void fly(){ System.out.println("Can't fly"); } }
private修饰的方法不能被子类所继承,也就不存在覆盖的概念。
判断是否覆盖方法的必杀技:@Override标签:若方法是复写方法,在方法前或上贴上该标签,编译通过,否则,编译出错。
class Person { private void fly(){ System.out.println("fly"); } } class Student extends Person { @Override public void fly(){ System.out.println("Can't fly"); } }
---------- 编译java ----------
ExtendsDemo.java:10: 错误: 方法不会覆盖或实现超类型的方法
@Override
^
1 个错误输出完成 (耗时 0 秒) - 正常终止
注意:
1. 只有方法存在覆盖的概念,字段没有覆盖。
2. 方法覆盖解决的问题:当父类的某一个行为不符合子类具体的特征时,此时子类需要重新定义父类
方法的重载与覆盖的区别
- 方法重载(Overload)
- 方法重写(Override)
二者本身没有一点关系,仅仅只是名字很像
方法的重写
作用:
解决子类继承父类之后,可能父类的某一个方法不再满足子类的具体特征,此时需要重新定义子类中定义的该方法,并重写方法体;
规则:
1.参数列表必须完全与被重写方法的相同;
2.返回类型必须完全与被重写方法的返回类型相同;
3.访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
4.父类的成员方法只能被它的子类重写。
5.声明为final的方法不能被重写。
6.声明为static的方法不能被重写,但是能够被再次声明。
7.子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
8.子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
9.重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
10.构造方法不能被重写。
11.如果不能继承一个方法,则不能重写这个方法。
重载
作用:
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
规则:
1.被重载的方法必须改变参数列表(参数个数或类型不一样);
2.被重载的方法可以改变返回类型;
3.被重载的方法可以改变访问修饰符;
4.被重载的方法可以声明新的或更广的检查异常;
5.方法能够在同一个类中或者在一个子类中被重载。
6.无法以返回值类型作为重载函数的区分标准。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
SpringBoot手动开启事务:DataSourceTransactionManager问题
这篇文章主要介绍了SpringBoot手动开启事务:DataSourceTransactionManager问题,具有很好的价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2023-07-07Spring @Configuration和@Component的区别
今天小编就为大家分享一篇关于Spring @Configuration和@Component的区别,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧2018-12-12
最新评论