阿里云【名师课堂】Java面向对象开发57 ~ 59:覆写
57:方法覆写
概念
方法覆写指的是子类定义了与父类方法名称、参数类型及个数完全相同的方法,凡是被覆写的方法不能拥有比父类更严格的访问控制权限。
范例:观察简单的方法覆写
class Person {
public void printInfo() {
System.out.println("【Person类】printInfo()方法") ;
}
}
class Student extends Person {
public void printInfo() { // 注意方法名完全相同
System.out.println("【Student类】printInfo()方法") ;
}
}
public class TestDemo {
public static void main(String args[]) {
Student stu = new Student() ; // 子类实例化对象
stu.printInfo();
}
}
使用覆写时一定要关注以下两点
- 当前使用的对象是哪个类new的(哪个类实例化的);
- 调用方法时,如果某方法已经被子类覆写了,则调用的一定是被覆写的方法。
访问权限
再次强调:凡是被覆写的方法不能拥有比父类更严格的访问控制权限
- 访问控制权限:
private
<default
<public
- 就是说,如果父类方法使用
public
权限,子类方法只能是public
权限;如果父类default
权限,子类可以是default
、public
。- 这时需要关注
private
权限,如果父类方法被private定义,那么表示该方法只能被父类调用,对子类而言根本没有这个方法。
- 这时需要关注
- 范例:错误的覆写
class Person {
void printInfo() { // default权限
System.out.println("【Person类】printInfo()方法") ;
}
}
class Student extends Person {
private void printInfo() { // 访问权限变严格了
System.out.println("【Student类】printInfo()方法") ;
}
}
public class TestDemo {
public static void main(String args[]) {
Student stu = new Student() ; // 子类实例化对象
stu.printInfo();
}
}
//输出:错误提示: Student中的printInfo()无法覆盖Person中的printInfo()
- 范例:观察private权限
class Person {
public void method(){
this.printInfo() ;
}
private void printInfo() {
System.out.println("【Person类】printInfo()方法") ;
}
}
class Student extends Person {
// 这时这个方法只是子类定义的新方法,和父类一点关系没有
public void printInfo() {
System.out.println("【Student类】printInfo()方法") ;
}
}
public class TestDemo {
public static void main(String args[]) {
Student stu = new Student() ; // 子类实例化对象
stu.method();
}
}
// 这时输出为:【Person类】printInfo()方法
// 如果把父类方法private换为default或public,输出为:【Student类】printInfo()方法
结论:绝大多数情况下写重载时访问权限就用public。
重载和覆写的区别
请回顾:《阿里云【名师课堂】Java面向对象开发8:构造方法与匿名对象》中的方法重载部分。
区别 | 重载 | 覆写 |
---|---|---|
英文 | Overload | Override |
概念 | 方法名称相同,参数的种类、个数不同 | 方法名称、返回值类型、参数类型及个数完全相同 |
范围 | 发生在一个类中 | 发生在继承关系中 |
限制 | 没有权限要求 | 凡是被覆写的方法不能拥有比父类更严格的访问控制权限 |
- 重载时返回值是否可以不同?
- 方法重载时返回值可以不同,但是良好的设计上要求返回类型相一致
58:属性覆盖(了解)
当子类定义了与父类中属性名称完全相同的属性时就叫做属性的覆盖。
class Person {
public String info = "it was called yellow" ;
public void printInfo() {
System.out.println("【Person类】printInfo()方法") ;
}
}
class Student extends Person {
public int info = 3000 ;
public void printInfo() {
System.out.println(info) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Student stu = new Student() ; // 子类实例化对象
stu.printInfo() ;
}
}
这个操作实际上没有意义,因为在实际操作中我们会用private定义属性,这时子类根本不知道父类中有哪些属性,也就不存在对父类属性进行重载操作。
结论:写属性时尽量不要有重名。
59:super关键字
在之前的子类对象实例化操作的讲解时介绍了super(),当时是子类调用父类方法时才使用。
那么在进行覆写的操作过程中,子类也可以使用super.方法()
或者super.属性
明确调用父类的方法或属性。
调用父类方法
范例:观察一个程序
class Person {
public void printInfo() {
System.out.println("【Person类】printInfo()方法") ;
}
}
class Student extends Person {
public void printInfo() {
System.out.println("【Student类】printInfo()方法") ;
}
}
public class TestDemo {
public static void main(String args[]) {
Student stu = new Student() ; // 子类实例化对象
stu.printInfo() ;
}
}
对于这段程序,输出是:【Student类】printInfo()方法。
- 首先,实例化子类对象
stu
,然后stu
调用子类中的printInfo
方法。 - 因为子类覆写了父类的
printInfo
方法。
如果实例化操作改为:Person stu = new Person() ;
,输出是:【Person类】printInfo()方法。
- 因为实例化了父类对象,直接调用父类中的
printInfo
方法。
如果将子类改为:
class Student extends Person {
public void printInfo() {
printInfo() ; // this.printInfo() ;
System.out.println("【Student类】printInfo()方法") ;
}
}
输出为:java.lang.StackOverflowError,即栈溢出。
- 因为这里是递归调用,这里的
printInfo() ;
就相当于this.printInfo() ;
。 - this起到的作用为:先在本类中查找所需要的方法,如果本类没有,则去找父类中方法进行调用。所以这里就是在自己的
printInfo() ;
中调用printInfo() ;
。
如果将子类改为:
class Student extends Person {
public void printInfo() {
super.printInfo() ;
System.out.println("【Student类】printInfo()方法") ;
}
}
输出为:
为什么?
- 使用super关键字之后不在本类寻找方法,而是直接前往父类调用父类中的方法。
- 这里就是先输出父类的
printInfo() ;
再输出自己的printInfo() ;
。
调用父类属性
范例:观察调用父类属性
先回顾第58讲中的属性覆盖程序,子类中的System.out.println(info) ; // info=this.info
语句输出的永远是3000,因为它输出的是自己的info。
要想输出父类的info应该:在子类的printInfo
方法中添加:System.out.println(super.info) ;
super与this
通过分析我们发现:super与this的使用形式上高度相似,但是二者最大区别在于:super是子类访问父类的操作,this是在本类自身中访问处理的操作。
区别 | this | super |
---|---|---|
概念 | 访问本类中的属性、方法 | 由子类访问父类中的属性、方法 |
查找范围 | 先查找本类,如果本类没有则调用父类 | 不查找本类,直接调用父类 |
特殊 | 表述当前对象 | 没有类似“表示父类对象”的概念 |
覆写总结:
- 子类覆写父类的方法是因为父类的方法功能不足;
- 方法覆写时统一使用public权限,注意返回值、名称的完全相同
- 以后在子类中调用父类方法时一定加上super关键字。