继承性的主要特征是子类可以根据父类已有的功能进行功能的扩展,但是在子类定义属性或方法的时候,有可能定义的属性和方法与父类同名,这样的情况下就称为覆写。
一、方法的覆写
当子类定义了与父类方法名称相同、参数的类型及个数、返回值相同的方法时,就称为发生了覆写。
范例:观察覆写的产生效果
class A { public void fun() { System.out.println("A类中的fun()"); } } class B extends A { } public class TestDemo { public static void main(String args[]) { B b = new B(); b.fun(); } }
因为这个时候子类并没有fun()方法,所以这个时候子类使用fun()方法时,会直接使用A类继承而来的fun()方法。
范例:观察覆写
class A { public void fun() { System.out.println("A类中的fun()"); } } class B extends A { public void fun() {// 此处为覆写 System.out.println("B类中的fun()"); } } public class TestDemo { public static void main(String args[]) { B b = new B(); b.fun(); } }
当发生了覆写之后,此时会调用实例化子类中中已经被覆写的方法。
一个类可能会产生多个子类,每个子类都一定会有自己的实现。
class A {
public void fun() {
System.out.println("A类中的fun()");
}
}
class B extends A {
public void fun() {// 此处为覆写
System.out.println("B类中的fun()");
}
}
class C extends A {
public void fun() {// 此处为覆写
System.out.println("C类中的fun()");
}
}
public class TestDemo {
public static void main(String args[]) {
B b = new B();
C c = new C();
b.fun();
c.fun();
}
}
覆写结果的分析要素:
- 观察实例化的是哪一个类;
- 观察这个实例化的类里面调用的方法是否已经被覆写过的,如果没有覆写过,调用父类的。
覆写的使用原则(被动原则):
如果现在发现父类中的方法名称功能不足(不适合于本子类),但是又必须使用这个方法名称的时候,就需要采用覆写这一概念实现。
以上的代码确实已经实现了覆写的功能,但是如果想要更好的实现覆写的操作,还必须考虑到权限问题,被子类所覆写的方法不能够拥有比父类更严格的访问控制权限。
对于访问控制权限我们已经学习过三个:public>default>private,也就是说private的访问权限是最严格的,即:如果父类的方法使用的是public声明,那么子类覆写此方法的时候只能使用public,如果父类的方法使用的是default,那么子类覆写方法的时候只能使用default或public。99%情况下只要是方法都使用public声明。
范例:正确的覆写
class A { public void fun() { System.out.println("A类中的fun()"); } } class B extends A { public void fun() {// 此处为覆写 System.out.println("B类中的fun()"); } }
class A { void fun() { System.out.println("A类中的fun()"); } } class B extends A { public void fun() {// 此处为覆写 System.out.println("B类中的fun()"); } }
范例:错误的覆写
class A { public void fun() { System.out.println("A类中的fun()"); } } class B extends A { void fun() {// 此处为覆写 System.out.println("B类中的fun()"); } }
此时子类中使用了default权限,相对于父类中的public属于更加严格的范畴。
疑问?如果父类中方法使用了private声明,子类使用了public声明方法,那么叫覆写吗?
从概念上来讲,父类的方法是private,属于小范围权限,而public属于扩大范围权限。权限上符合于覆写要求。
下面先抛开private不看,先使用一个正常的覆写操作。
class A {
public void fun() {
print();
}
public void print() {
System.out.println("下午好");
}
}
class B extends A {
public void print() {// 覆写print()
System.out.println("你好");
}
}
public class TestDemo {
public static void main(String args[]) {
B b = new B();
b.fun();
}
}
下面使用private声明父类中的方法:
class A {
public void fun() {
print();
}
private void print() {
System.out.println("下午好");
}
}
class B extends A {
public void print() {// 覆写print()
System.out.println("你好");
}
}
public class TestDemo {
public static void main(String args[]) {
B b = new B();
b.fun();
}
}
这个时候发现子类中根本就没有覆写print()方法,也就是说如果使用了private声明,那么这个方法对子类而言是不可见的,就算子类定义了一个与之完全相同的符合于覆写要求的方法,那么也不能够发生覆写。这个时候子类所写的方法实际上就相当于子类自己定义了一个新的方法。
一旦有了覆写之后会发现,默认情况下子类所能够调用的一定是被覆写过的方法。为了能够明确的由子类调用父类中已经被覆写的方法,那么可以使用“super.方法()”来进行访问。
class A {
public void print() {
System.out.println("下午好");
}
}
class B extends A {
public void print() {// 覆写print()
super.print();
System.out.println("你好");
}
}
public class TestDemo {
public static void main(String args[]) {
B b = new B();
b.print();
}
}
关于super.方法()与this.方法的区别:
· 使用“this.方法()”会首先查找本类中是否存在有要调用方法名称,如果存在则直接调用,如果不存在,则查找父类中是否具备此方法,如果有就调用,如果没有,则会发生编译时的错误提示;
· 使用“super.方法()”明确的表示调用的不是子类的方法(不查找子类中是否存在有此方法),而直接调用父类中的指定方法。
扩展题目:请解释重载与覆写的区别?(请解释Overloading与Overrid的区别?)
在使用Overloading的时候,返回值能否不同?
No
区别
重载
覆写
1
英文单词
Overloading
Overrid
2
发生范围
发生在一个类里面
发生在继承关系中
3
定义
方法名称相同,参数的类型及个数不同
方法名称相同,参数的类型及个数相同,方法返回值相同
4
权限
没有权限限制
被覆写的方法不能拥有比父类更为严格的访问控制权限
在发生重载关系的时候,返回值可以不同,但是考虑到程序设计的统一性,重载时尽量保证方法的返回值类型相同。
二、属性的覆盖
如果说现在子类定义了和父类完全相同的属性名称的时候,就称为属性的覆盖。
范例:观察属性覆盖
class A { String info = "hello"; } class B extends A { int info = 100;// 名称相同 public void print() { System.out.println(super.info); System.out.println(this.info); } } public class TestDemo { public static void main(String args[]) { B b = new B(); b.print(); } }
我们一直强调,在任何的开发之中,类中的属性必须封装(99%),那么一旦封装之后,这样的覆盖是没有任何意义的,因为父类定义的私有属性子类根本就看不见,更不会互相影响了。
比较:关于super与this的区别。
No
区别
this
super
1
功能
调用本类构造、本类方法、本类属性
子类调用父类构造、父类方法、父类属性
2
形式
先查找本类中是否存在有指定的调用结构,如果有则直接使用,如果没有则调用父类定义
不查找子类,直接调用父类操作
3
当前对象
表示本类的当前对象
-
在以后的开发之中,对于本类或者是父类中的操作,强烈建议加上“this.”或者是“super.”,这样好区分。