说到父类就不得不说多态了,但想要翻看多态规则的小伙伴可以翻看我下一条博客
文章目录
重写父类的方法
- 方法重写的规则
Super限定
- 聪明的JVM
调用父类构造器
子类扩展了父类的方法,大部分时候,子类总是以父类为基础,额外增加新的成员变量和方法。
重写父类的方法
有些时候子类需要重写父类的方法,鸟类作为父类都包含了飞翔的方法,但其中鸵鸟却没有这个方法,这个时候就需要将飞翔的方法改掉了。
代码如下
//父类
public class Bird
public class Bird {
// Bird作为父类
public void fly() {
System.out.println("我在天空里自由地飞翔");
}
}
//子类继承父类
public class Ostrich extends Bird{
//Ostrich 作为Bir子类
@Override //重写Birdfly方法
public void fly() {
System.out.println("我只能在地上奔跑");
}
}
执行Ostrich的fly方法后可以发现输出了fly方法重写后的,这个方法就称为方法重写,也可以称为方法覆盖。可以说是子类覆盖了父类的方法,或是子类重写了父类的方法
方法重写的规则
-
方法名相同、形参列相同
-
子类 返回值类型不能超过父类返回值类型的范围,子类抛出的异常类的范围不能超过父类抛出的异常类的范围
。 如图继承关系 其中3类myMethod方法
以2类的类型返回
而4类改写了这个方法则4类改写的myMethod只能以2类、3类、4类的类型返回这个值。
-
子类方法的访问权限应比父类方法的访问权限更大或相等。
-
覆盖方法和被覆盖方法要么都是类方法(静态方法),要么都是实例方法(对象方法)。
-
如果父类方法中有private访问权限,则该方法对子类是隐藏的。如果 子类定义了一个与你类private方法一模一样的方法(方法体不同),依然不是重写,只是在子类定义了一个新方法。
class a {
private int method() {
return 0;
}
}
class b extends a { //此处并不是重写,所以可以增加 static 关键字
private static int method() {
return 0;
}
Super限定
如果需要在子类方法中调用父类被 覆盖的
实例方法
,则可以使用super限定来调用
- Super限定用于调用被继承的对象方法或变量。和this 一样不可以用于调用static修饰的方法(
只能访问实例成员
)。- 在构造器中使用super,则super用于限定该构造器创建对象时初始化的是该对象从父类继承得到的实例变量,而不是该类自己定义的实例变量(
构造器中使用super方法
)
如果子类定义了和父类相同名字的实例变量,、这个父类变量是对子类隱藏的。无法通过子类对象访问父类的这个实例变量的,如果想要访问则可以在子类方法中用super限定来访问。
下面用代码来体现这个具体实现
public class Super限定 {
public void main(String[]args) {
SubClass case_1 = new SubClass();
case_1.accessBase();
//输出7
case_1.accessOwner();
//输出5
}
class BaseClass{
public int a = 5; //此处定义了a 属于BaseClass
}
class SubClass extends BaseClass{ //SubClass 继承了 BaseClass
public int a =7; //此处也定义了a 属于SubClass
public void accessOwner() {
System.out.println(a);
}
public void accessBase() {
System.out.println(super.a); //使用super限定访问该实例从父类继承得到的 a 实例变量,而不是当前类定义的实例变量 a。
}
}
}
BaseClass 和 SubClass都定义了名为a的实例变量,则SubClass的a实例变量将会隐藏BaseClass的a实例变量,当系统创建了SubClass对象时会为SubClass对象分配两块内存,1、一块用于存储在SubClass类中定义的a实例变量
,2、一块用于存储从BaseClass类继承得到的a实例变量(注意:子类定义和父类中同名的实例变量不是覆蓋而是隱藏了父类同名的那个实例变量
)
聪明的JVM
如果在子类中没有包含和父类同名的成员变量,那么在子类对象中则无需使用限定super
或父类名(静态变量)
来访问这个成员变量。假设在某方法中访问名为 name
的成员变量,但没有显示指定调用者,则系统查找name
的顺序为
- 查找该方法中是否有名为
name
的局部变量 - 查找 当前类中是否包含名为
name
的成员变量 - 查找
name
直接父类中是否包含名为name
的成员变量,依次上溯所有父类,直到java.lang.Object类。如果最终不能找到名为name
的成员变量,若找不到则系统出现编译错误。
值得一提的是当存在间接父类时,则会开劈多个内存空间,如果一个java类有两个父类(一个直接父类A 一个间接父类B),若A类中定义了2个实例变量,B类中定义了3个实例变量,当前类定义了4个实例变量。那么这个java对象将会保存2+3+4个实例变量。
特殊情况
class Parent{
public String tag = "我是特例";
}
class Derived extends Parent{
private String tag = "我也是特例";//定义了一个私有的实例变量来隐藏父类的tag
}
public class TestStart{
public static void main(String[]args){
Derived d = new Derived();
//System.out.println(d.tag); //因为是私有的,所以只可以在类的内部被访问。所以此处会引起编译错误
System.out.println(((Parent)d).tag); //利用向上转型将Derived对象变为Parent对象
//输出 我是特例 。
}
}