Java学习笔记13

时间:2022-01-06 16:06:55

  子类重新实现父类的方法称重写;重写时可以修改访问权限修饰符和返回值,方法名和参数类型及个数都不可以修改;仅当返回值为类类型时,重写的方法才可以修改返回值类型,且必须是父类方法返回值的子类;要么就不修改,与父类返回值类型相同。那么,该如何理解呢?为什么要是父类返回值类型的子类?

  还是先看示例,详见下文。

  包human中定义了三个类,Person类、Student类和TestMain类,其中Student类是Person类的子类。代码分别如下:

Person类的代码如下:

package human;

public class Person {
String name;
int age;

//test:重写
public Person overRide() {
Person per
= new Person();
per.name
= "liu";

return per;
}
}

 

Student类重写了父类的overRide()方法,代码如下:

package human;

public class Student extends Person {
String stuNumber;
int score;

//test:重写
public Student overRide() {
Student stu
= new Student();
stu.name
= "li";

return stu;
}
}

 

TestMain类的代码如下:

package human;

public class TestMain {

public static void main(String[] args) {
Student stu
= new Student();
Person per
= new Person();

per
= stu.overRide();
System.out.println(per.name);
per
= per.overRide();
System.out.println(per.name);
}

 

输出结果为:

Java学习笔记13Java学习笔记13
1 li
2 li
View Code

  有没有人跟我一样,第一反应输出应该为“li liu”呢,怎么两个都是“li”?

  仔细分析一下,看下面的几张内存图就明白了。

  第1、第2条语句分别创建一个子类对象和一个父类对象,其中,stu指向子类对象,per指向父类对象。如下面图1所示:

Java学习笔记13

                                                           图1

 

  接着执行第3条语句:per = stu.overRide();;

  stu先调用overRide(),方法体里创建了一个子类对象,并让临时变量stu指向该对象,其存储位置就是以C为首地址的内存块;

  然后把该对象的变量name赋值为“li”;最后返回stu的值并赋给per,也就是说,虽然per是父类对象引用,但最后指向了overRide()里创建的子类对象,这里以蓝色箭头表示;  原先指向的以B为首地址的父类对象这时没有引用指向它,这里把红色箭头变为虚线表示。此时访问per的name,显然是“li”。内存结构见图2:

Java学习笔记13 

                                                           图2

 

  再接着要执行per = per.overRide();,调用overRide()方法;

  由于子类重写了父类的overRide()方法,虽然per为父类对象引用,此时父类的该方法被覆盖,所以此时要调用子类的方法;执行过程同上,per不再指向以C为首地址的子类对象,改为指向新创建的子类对象,以D为首地址,如图3所示。

  同上面一样的道理,此时访问per的name仍然为“li”,因为父类的overRide()两次压根都没有被调用到。

Java学习笔记13

                                                           图3

 

修改一下TestMain,如下所示:

package human;

public class TestMain {

public static void main(String[] args) {
Student stu
= new Student();
Person per
= new Person();
Person per2 = per;
// per = stu.overRide();
System.out.println(per.name);
per
= per.overRide();
System.out.println(per.name);
per2
= per2.overRide();//
System.out.println(per2.name);//
}

  此时定义了一个父类对象引用per2,并让它与per指向同一个对象;最后两行,由per2调用overRide()方法,很显然要调用父类的方法,所以方法体中创建的也是父类的对象,再把结果返回给per2,此时per2指向新创建的父类对象,该父类对象的name就为“liu”了。

  说了这么多,貌似还没解决开头的问题,为什么是父类返回值类型的子类?为方便说明,记父类的返回值类型为A。

  我的理解是,这是为了向上转型;既然子类重写了父类的方法,有时候就需要用父类对象引用来调用子类重写的方法,在上面例子的情况下,也就是说要把A的子类对象引用赋给A的对象引用,如果此时返回值类型不是A类或A的子类,其他类的对象引用是不能赋给A的对象引用的,这样就会出错;所以说,子类重写的方法,如果返回值为类类型,其返回值类型必须与父类返回值类型相同或为父类返回值类型的子类。

  不知道有没有说清楚。

  PS:例子选得不是特别好,如果返回值类型是与Person和Student不想干的类,可能更好理解,不然容易把返回值的类与方法所属的类混淆。