`

010 方法重载与重写

 
阅读更多

重载与重写

 

 

在Java编程语言中,有两个概念非常相近,很容易混淆。它们就是重写与重载。

 

这里先在类继承的话题上,继续来说说重写,也叫覆盖。

 

没有继承,就不可能有重写。这是子类对父类的一种特殊操作。这里先提一下,重载主要发生在同一个类中的操作,也可以发生在父类与子类之间。

 

当一个子类继承了一个父类时,它也同时继承了父类的属性和方法。我们可以直接使用父类的属性和方法,或者,如果父类的方法不能满足子类的需求,则可以在子类中对父类的方法进行“改造”,这个“改造”的过程在Java中称为“覆盖(override)”。


比如,假设我们在Person 类中定义了一个方法用于显示姓名,如下:
public String showName(){
 return name;
}


那么,在子类Teacher中,就会继承这个父类的showName()方法,此时,即使Teacher类中没有定义showName()方法,也可以在它的实例(instance)中使用这个方法。

 

比如,我们实例化了一个Teacher的实例,这是就可以直接调用从Teacher的父类Person中继承的这个showName()方法。如下:
Teacher teacher = new Teacher();
teacher.showName();


但是,我们在Teacher类中,我们或许需要在姓名后面加上“老师”,比如,“张三”就显示为“张三老师”,那么,这个时候就需要在子类“Teacher”中对从父类中继承的showName()方法进行“改造”,也就是在子类Teacher中覆盖父类Person的方法showName():
public class Teacher extends Person{
 … …
 public String showName(){
  return name+”老师”;
 }
}


在子类Teacher中,覆盖了父类的showName方法,在覆盖的过程中,需要提供和父类中的被覆盖方法相同的方法名称、输入参数(此处为空)以及返回类型。


另外,在子类对父类的方法进行覆盖的过程中,不能使用比父类中的被覆盖方法更严格的访问权限。

 

比如,父类Person中的方法showName()的修饰符是public,那么,在子类Teacher中的覆盖方法showName()就不能用protected、默认(Default)或者private等来限制。


从前面的讨论可以看出,类的继承主要可以从两方面来看:


对父类的扩充。如,在子类中加入新的属性、新的方法。
对父类的改造。比如,对方法的覆盖。

首先定义一个父类:Person,它有三个属性,分别由各自的存取方法来存取。

public class Person {
 private String name;

 private int age;

 private String sex;

 public String showName() {
  return name;
 }

 public void setName(String theName) {
  name = theName;
 }

 public int getAge() {
  return age;
 }

 public void setAge(int theAge) {
  age = theAge;
 }

 public String getSex() {
  return sex;
 }

 public void setSex(String theSex) {
  sex = theSex;
 }
}


在这边,我们将它的属性的访问控制定义为Default,是因为在子类中我们需要访问这些属性。


接着,我们定义一个类“Teacher”,它继承“Person”类。

public class Teacher extends Person {
 // 扩充父类属性
 private String department;

 // 扩充父类方法
 public void setDepartment(String theDept) {
  department = theDept;
 }

 public String getDepartment() {
  return department;
 }

 // 方法覆盖
 public String showName() {
  return name + "老师";
 }

 // 测试
 public static void main(String[] args) {
  Teacher t = new Teacher();
  t.setName("Alex");
  System.out.println(t.showName());
 }
}


在这个子类中,继承了父类,新增了一个“部门”的属性:department,并新增了相应的存取方法。改写(覆盖)了父类中的showName()方法,在姓名后面加上“老师”后返回。

 

因为在同一个包中的子类中用到了父类的属性name,所以,父类Person中的name属性不能定义为private的。最后定义了一个main()方法用于测试。

 

执行这个程序,在控制台上将会输出如下信息:
Alex老师


在程序中加入一个main()方法用于测试一个类是不错的主意,无论这个类是否是一个Java应用程序(Java Application)。

 

你可以通过类的main()方法测试这个类的各个方法是否与你的设计相一致。

 

 

1 重载

 

 

在Java程序中,如果同一个类(或者父类子类之间)中如果有两个相同的方法(方法名相同、返回值相同、参数列表相同)是不行的,因为这样编译器无法将方法调用和特定的方法联系起来。但是,在一个类中,如果有多个方法具有相同的名称,而有不同的参数,这种情况是允许的,我们就称这种行为为方法的重载(overload)。

 

我们经常使用println来向控制台输出各种类型的数据,这些println方法就是实现了方法的重载。


在进行方法的重载时方法的参数列表必须不同(参数个数或者参数数据类型,或者两者皆不同)。而方法的返回值可以相同,也可以不同。


我们来看一个例子。还是以上面的“Person”类,我们需要取得“Person”对象的“name(姓名)”属性,可能会有两种情况:一种是直接得到对象的“name”属性,还有一种情况是在获取的对象属性“name”的方法上输入一个参数,将这个参数当作头衔和“name”结合起来,比如:输入的头衔是“先生”,则通过方法返回的是:“XXX先生”。

 

我们可以为这两个需求定义两个方法,但是,为了显示这两个方法的相似点,我们更倾向于使用方法重载来完成:
public class Person{
 … …
 public String getName() {
  return name;
 }
 public String getName(String title) {
  return name+title;
 }
 … …
}


这样,如果只需要得到“Person”对象的“name”属性,调用不带参数的getName()方法就可以了;如果需要得到“Person”对象的“name”属性并且需要指明它的头衔,则可以调用带一个参数的getName()方法。


在进行方法的重载时,有四条基本原则需要遵守:


方法名相同;
参数列表必须不同;
返回值可以不同;
可以相互调用。


注意:方法的返回值不是方法的签名(signature)的一部分,所以,进行方法的重载的时候,不能将返回值类型的不同当成两个方法的区别。也就是说,在同一个类中,不能有这样的两个方法,它们的方法名相同、参数相同,只是方法的返回值类型不同。


构造器重载

 

在前面我们说过,构造器在某种程度上可以看成是一个特殊的方法:它没有返回值,它的方法名称必需和类的名称一致。因此,构造器也常常被称为“构造方法”。

 

作为Java类的组成成分之一,构造器也可以进行重载,下例中的类Person就定义了4个重载的构造器以满足不同的需要。


public class Person {
 private String name;

 private int age;

 private String sex;

 public Person() {
  System.out.println("构造器Person()被调用");
  name = "";
 }

 public Person(String theName) {
  System.out.println("构造器Person(String theName)被调用");
  name = theName;
 }

 public Person(String theName, int theAge) {
  System.out.println("构造器Person(String theName,int theAge)被调用");
  name = theName;
  age = theAge;
 }

 public Person(String theName, int theAge, String theSex) {
  System.out.println("构造器Person(String theName,"
    + "int theAge,String theSex)被调用");
  name = theName;
  age = theAge;
  sex = theSex;
 }
 // 其他代码
 // …. …
}


在这个类中,定义了四个构造器,这四个构造器中,各自的参数个数都不一样。在创建对象的时候,编译器会根据参数类型和参数个数来确定到底调用哪一个构造器。


我们来看下面这个创建“Person”对象的例子:

public class NewPerson {
 public static void main(String[] args) {
  Person person = new Person("Tom", 18, "male");
 }
}


编译并运行上面这个程序,它将会向控制台打印出如下信息:
构造器Person(String theName,int theAge,String theSex)被调用


这说明,它的带三个参数的构造器被调用来创建Person对象了。

 

 

1)方法重载(Overload)。表示两个或多个方法名字相同,但方法参数不同。方法参数不同有两层含义:1)参数个数不同。2)参数类型不同。 注意:方法的返回值对重载没有任何影响。

 

普通的方法在重载的情况下,可以互相调用。

2)构造方法重载:只需看参数即可。如果想在一个构造方法中调用另外一个构造方法,那么可以使用this()的方式调用,this()括号中的参数表示目标构造方法的参数。this()必须要作为构造方法的第一条语句,换句话说,this()之前不能有任何可执行的代码。

 

13. 方法重写(Override):又叫做覆写,子类与父类的方法返回类型一样、方法名称一样,参数一样,这样我们说

子类与父类的方法构成了重写关系。

14. 方法重写与方法重载之间的关系:重载发生在同一个类内部的两个或多个方法。重写发生在父类与子类之间。重载是一种平行关系,几个方法参数不同。重写是一种层次关系。

15. 当两个方法形成重写关系时,可以在子类方法中通过super.run()形式调用父类的run()方法,其中super.run()

不必放在第一行语句,因此此时父类对象已经构造完毕,先调用父类的run()方法还是先调用子类的run()方法是根据

程序的逻辑决定的。

 

 

Java中虚方法调用

 

 

我们前面已经知道了,在多态的情况下,声明为父类类型的引用变量只能访问父类中定义过的方法,但如果此变量实际引用的是子类的对象,而子类中又进行了方法覆盖时,实际调用的是子类中覆盖后的方法,这种机制称为虚方法调用


在使用多态的情况下,有可能出现编译时类型和运行时的类型不一致的问题,如上面的例子中:
public class CalClass{
 .. …
 public int calPersonBirthYear(Person p) {
  // 根据参数p的年龄来计算出生年代
 }
}


在编译的时候,方法calPersonBirthYear 的参数类型是Person的,而在运行的时候,可能就是Student 或Teacher或Person或其他的Person子类类型了。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics