重写(Override)
重写是在不该变形式参数和返回值类型的条件下对父类允许访问的方法的实现过程进行重新编写。
重写的特点:
- 方法体不变,核心重写。
- 可根据子类的额需要定义自己的行为,实现父类的方法。
- 可以重写现有的任何方法。
实例:
Override.java
class Animal {
public void move() {
System.out.println("动物可以移动");
}
}
class Dog extends Animal {
@Override
public void move() {
System.out.println("狗狗可以跑和移动");
}
}
public class OverrideDemo {
public static void main(String[] args) {
// Animal对象
Animal animal = new Animal();
// Dog对象
Animal dog = new Dog();
animal.move(); // 执行Animal对象的方法
dog.move(); // 执行Dog对象的方法
}
}
结果:
动物可以移动
狗狗可以跑和移动Process finished with exit code 0
从上面的例子我们可以看到,尽管dog属于Animal类型,但是它依然可以运行Dog类的move()方法。
这是由于在编译阶段,只检查参数的引用类型,而在运行时,由Java虚拟机指定的对象的类型并且运行该对象的方法。上面的例子之所能够编译成功,是因为Animal类中存在move()方法。
OverrideDemo.java
class Animal {
public void move() {
System.out.println("动物可以移动");
}
}
class Dog extends Animal {
@Override
public void move() {
System.out.println("狗狗可以跑和移动");
}
public void doorkeeper() {
System.out.println("狗狗可以看家");
}
}
public class OverrideDemo {
public static void main(String[] args) {
// Animal对象
Animal animal = new Animal();
// Dog对象
Animal dog = new Dog();
animal.move(); // 执行Animal对象的方法
dog.move(); // 执行Dog对象的方法
// dog.doorkeeper(); // 报错,必须实例化一个对象
((Dog) dog).doorkeeper(); // 正确
}
}
结果:
动物可以移动
狗狗可以跑和移动
狗狗可以看家Process finished with exit code 0
上面的例子可以看出,当子类中“重写”的类在父类中没有或者参数返回值不一样是,编译器就会报错(cannot find symbol)。
方法重写规则:
- 参数列表与返回列表必须完全与被重写的方法相同;
- 重写方法访问权限不能高于父类访问权限。如父类方法为public,那么子类的方法就不能声明为protected或者private;
- 父类中成员方法只能被子类重写;
- 声明为final的方法不能被重写;
- 声明为static的方法不能被重写,但是能够被声明。如果一个方法不能被继承,该方法不能够被重写;
- 子类和父类在同一个包中,子类可以重写父类的方法,除了声明为private和final的方法;
- 子类和父类不在同一个包中,那子类只能重写父类声明为public和protected的非final方法;
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以;
- 构造方法不能被重写;
- 如果不能继承一个方法,则不能重写这个方法。
super
当需要在子类调用父类的被重写的方法时,要使用super关键字。
OverrideDemo.java
class Animal {
public void move() {
System.out.println("动物可以移动");
}
}
class Dog extends Animal {
@Override
public void move() {
super.move();
System.out.println("狗狗可以跑和移动");
}
}
public class OverrideDemo {
public static void main(String[] args) {
// Animal对象
Animal animal = new Animal();
// Dog对象
Animal dog = new Dog();
animal.move(); // 执行Animal对象的方法
System.out.println("-----------LINE----------");
dog.move(); // 执行Dog对象的方法
}
}
结果:
动物可以移动
-----------LINE----------
动物可以移动
狗狗可以跑和移动Process finished with exit code 0
重载(Overload)
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型呢?可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
重载规则:
- 被重载的方法必须改变参数列表;
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
实例:
OverloadDemo.java
public class OverloadDemo {
public int test() {
System.out.println("test1");
return 1;
}
public void test(int a) {
System.out.println("test2");
}
public String test(int a, String s) {
System.out.println("test3");
return "re3";
}
public String test(String s, int a) {
System.out.println("test4");
return "re4";
}
public static void main(String[] args) {
OverloadDemo overloadDemo = new OverloadDemo();
System.out.println(overloadDemo.test());
System.out.println("-----------LINE----------");
overloadDemo.test(5);
System.out.println("-----------LINE----------");
System.out.println(overloadDemo.test(5, "hello"));
System.out.println("-----------LINE----------");
System.out.println(overloadDemo.test("hello", 5));
}
}
结果:
test1
1
-----------LINE----------
test2
-----------LINE----------
test3
re3
-----------LINE----------
test4
re4Process finished with exit code 0
重写与重载的区别
区别 | 方法 | 重写方法 |
---|---|---|
参数列表 | 必修修改 | 不能修改 |
返回类型 | 可以修改 | 不能修改 |
异常 | 可以修改 | 可减少删除,不能抛出新或更广异常 |
访问 | 可以修改 | 不能做更严格限制 |
面试题
1. 下列代码输出结果为:
public class MethodOverrideVsOverload {
private boolean equals(MethodOverrideVsOverload other) {
System.out.println("MethodOverrideVsOverload equals method reached" );
return true;
}
public static void main(String[] args) {
Object o1 = new MethodOverrideVsOverload();
Object o2 = new MethodOverrideVsOverload();
MethodOverrideVsOverload o3 = new MethodOverrideVsOverload();
MethodOverrideVsOverload o4 = new MethodOverrideVsOverload();
if(o1.equals(o2)){
System.out.println("objects o1 and o2 are equal");
} else {
System.out.println("objects o1 and o2 are not equal");
}
if(o3.equals(o4)){
System.out.println("objects o3 and o4 are equal");
}else {
System.out.println("objects o3 and o4 are not equal");
}
}
}
结果:
objects o1 and o2 are not equal
MethodOverrideVsOverload equals method reached
objects o3 and o4 are equalProcess finished with exit code 0
分析:MethodOverrideVsOverload 类中的”equals(MethodOverrideVsOverload other)”方法并没有重写Object类中的”public boolean equals(Object obj)” 方法。这是因为其违背了参数规则,其中一个是MethodOverrideVsOverload 类型,而另一个是Object类型。因此,这两个方法是重载关系(发生在编译时),而不是重写关系。
因此,当调用o1.equals(o2)时,实际上调用了object类中的public boolean equals(Object obj)方法。这是因为在编译时,o1和o2都是Object类型,而Object类的equals( … )方法是比较内存地址(例如,Object@235f56和Object@653af32)的,因此会返回false。
当调用o3.equals(o4)时,实际上调用了MethodOverrideVsOverload 类中的equals( MethodOverrideVsOverload other )方法。这是因为在编译时,o3和o4都是MethodOverrideVsOverload类型的,因此得到上述结果。
2. 下列代码输出结果为:
public class MethodOverrideVsOverload {
@Override
public boolean equals(Object other) {
System.out.println("MethodOverrideVsOverload equals method reached");
return true;
}
public static void main(String[] args) {
Object o1 = new MethodOverrideVsOverload(); //编译期间o1位object类型,执行期间o1为MethodOverrideVsOverload类型
Object o2 = new MethodOverrideVsOverload(); //编译期间o2位object类型,执行期间o1为MethodOverrideVsOverload类型
MethodOverrideVsOverload o3 = new MethodOverrideVsOverload(); //编译期间和执行期间o3皆为MethodOverrideVsOverload类型
MethodOverrideVsOverload o4 = new MethodOverrideVsOverload(); //编译期间和执行期间o3皆为MethodOverrideVsOverload类型
if (o1.equals(o2)) {
System.out.println("objects o1 and o2 are equal");
} else {
System.out.println("objects o1 and o2 are not equal");
}
if (o3.equals(o4)) {
System.out.println("objects o3 and o4 are equal");
} else {
System.out.println("objects o3 and o4 are not equal");
}
}
}
结果:
MethodOverrideVsOverload equals method reached
objects o1 and o2 are equal
MethodOverrideVsOverload equals method reached
objects o3 and o4 are equalProcess finished with exit code 0
在Java5中,新增了注解,其中包括很好用的编译时注解(compile time annotations)@override,来保证方法正确的重写了父类方法。如果在上面的代码中添加了注解,那么JVM会抛出一个编译错误。
因此,解决的方法就是给MethodOverrideVsOverload 类的boolean equals( MethodOverrideVsOverload other )方法添加@override注解。这样的话编译时就会有错误抛出来提示开发者某个方法没有正确的重写父类方法。之后,还需要修改方法的参数,将其从 Object变成MethodOverrideVsOverload。
比较两个题目你会明白更多。