重写(Override) 和 重载(Overload)
方法重写: 是一种语言特性,它是多态的具体表现,它允许子类重新定义父类中已有的方法,且子类中的方法名和参数类型及个数都必须与父类保持一致。
基本用法如下:
public class Father {
public void Say(){
System.out.println("Father say:");
}
}
public class Son extends Father{
@Override
public void Say() {
System.out.println("Son Say:");
}
}
public class Main {
public static void main(String[] args) {
Father father = new Father();
Son son = new Son();
father.Say();
son.Say();
}
}
/*输出:
Father say:
Son Say:
*/
重写的最经典的例子莫过于Object的equal方法,众所周知,在Java中Object是所有类的父类,所以所有的类都从Object继承下来了equal方法,用于比较两个实例对象是否相等,不过不同的类的判断相等的方法肯定不一样,所以不可能用Object类内写的equal方法来作比较,因此每个类在需要比较是否相等时,都需要自己实现自己的判断相等的逻辑,这个时候就需要在类内重写继承自Object的equal方法。
可能这时候你就要问了,不用重写可不可以实现两个实例对象的比较呢?答案是当然可以,你大可以在自己写的类中写一个Myequal方法用于判断两个实例对象的比较。可能你又要问了,既然可以那为什么要用重写呢?这个问题其实也非常容易回答,想象一下,如果不用这种方式,而是每个类中自己实现自己的比较函数,那么就可能会出现这样一种情况:我写的类里面,用于判断相等的函数叫is;小明写的类的判断相等的函数叫xiangdeng;张三写的叫EQUAL;王五写的叫Equal。然后你用他们写的类,当你想判断是否相等的时候,你还得去翻一下文档看看他写的判断相等的函数是什么,然后再来敲代码。但是如果用重写的方式的话,那就随便拿一个类来,当你想判断是否相等时候,只需要调用equal就行了。
当然重写的好处并不止于此,它是多态的具体表现,提供了抽象能力。
在Java中使用重写需要注意以下几点:
- 子类方法的权限控制符不能变小,也就是如果父类方法的权限控制符是 protected,那么子类的方法权限控制符只能是 protected 或 public;
- 子类方法返回的类型只能变小,也就是说如果父类方法返回的是 Number 类型,那么子类方法只能返回 Number 类型或 Number 类的子类 Long 类型,而不能返回 Number 类型的父类类型 Object;
- 子类抛出异常的类型只能变小;
- 子类方法名必须和父类方法名保持一致;
- 子类方法的参数类型和个数必须和父类保持一致。
方法重载: 指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。
基本用法如下:
public class Main {
public static int add(int a,int b){
System.out.println("int");
return a+b;
}
public static double add(double a, double b){
System.out.println("double");
return a+b;
}
public static void main(String[] args) {
System.out.println(add(1,1));
System.out.println(add(1.0,1.0));
}
}
/*输出:
int
2
double
2.0
*/
遇到一个东西,首先先问问它有什么用,那么重载有什么用呢?先试想这样一个场景,需要写一套从数据库Person表里查询数据的函数,可以通过姓名查询对应的数据,可以通过年龄查询数据。
public List<Person> queryByName(String Name);
public List<Person> queryByAge(int Age);
现在,还需要查询某一个年龄段的数据。
public List<Person> queryByAges(int from,int to);
需要通过兴趣爱好查询数据。
public List<Person> queryByHobbies(List<Hobby> hobbies);
需要通过年龄以及姓名查询。
public List<Person> queryByAgeAndName(int age,String Name);
需要通过年龄以及姓名以及兴趣爱好查询。
public List<Person> queryByAgeAndNameAndHobbies(int age,String Name,List<Hobby> hobbies);
你写好这些后,放了个假,你把工作放下,出去玩了几天,等放假结束后,你继续回到工作中,现在开始需要写客户端的代码了,这个时候你需要从通过姓名和年龄查找数据,这个时候你会调用哪个函数?queryByNameAndAge还是queryByAgeAndName?你不确定,又得去回过头看当时写的到底是什么。但是如果使用重载写呢?
public List<Person> query(String Name);
public List<Person> query(int Age);
public List<Person> query(int from,int to);
public List<Person> query(List<Hobby> hobbies);
public List<Person> query(int age,String Name);
public List<Person> query(int age,String Name,List<Hobby> hobbies);
这样写的话是不是只需要记住一个query函数就可以进行所有的查询了呢?而且也不需要在给函数取名字的时候疯狂查字典,然后写出一个非常非常长的函数名了。
在Java中使用重载需要注意以下几点:
- 被重载的方法必须改变参数列表(参数个数,类型或顺序不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
总结
方法的重写是发生在继承关下的,子类重写父类的方法,本质上是同一个函,重写时参数类型和个数需要保持一致,返回值只能变小不能变大。而方法重载本质上是不同的函数,只是名字一样,正因如此,需要让重载的函数之间可以区分,即不能两个重载的方法参数个数和类型一致,而且返回值不能作为两个函数之间区分的标志。