super
问题引入:
从父类继承的非private成员,和子类新增的成员重名,如何在子类成员方法中区分两个成员?
答:
借助super关键字可以实现区分!
具体用法:
-
super.数据成员
,表示从父类继承的数据成员 -
super.成员方法(实参列表)
,表示调用从父类继承的成员方法
super表示子类对象中从父类继承的那部分(可看成一个父类对象)引用!
super内存结构图如下:
案例展示:
修改父类代码:
package com.briup.chap06.bean;
//提供父类
public class Animal {
//对age进行显示初始化,其他代码不动
public int age = 10;
//...省略
}
修改子类代码:
package com.briup.chap06.bean;
//定义Dog子类 继承 Animal
public class Dog extends Animal {
//子类新增数据成员并做显式初始化,成员名和从父类继承的一样
private int age = 20;
//新增方法,其形参名(局部变量)和数据成员名一样
public void memberAccess(int age) {
System.out.println("age: " + age);
System.out.println("this.age: " + this.age);
System.out.println("super.age: " + super.age);
}
//新增重载方法,其形参为空
public void memberAccess() {
System.out.println("age: " + age);
System.out.println("this.age: " + this.age);
System.out.println("super.age: " + super.age);
}
//其他代码不变,省略...
}
测试类:
package com.briup.chap06.test;
public class Test0203_AccessMember {
//修改main方法如下
public static void main(String[] args) {
Dog d = new Dog();
d.memberAccess(30);
System.out.println("------------");
d.memberAccess();
}
}
运行效果:
在子类方法中访问变量,会遵循就近原则,具体如下:
- 先在子类局部范围中查找局部变量
- 再在子类中查找新增的成员变量
- 最后从父类继承的成员变量中查找
如果一定要使用从父类继承的成员,可以通过super关键字,进行区分。
super 总结:
super 关键字的用法和 this 关键字的用法相似
this:代表本类对象的引用
super:代表父类存储空间的标识(可以理解为父类对象引用)
构造方法
思考:如何给子类对象进行初始化?
子类对象的数据成员包含两部分:继承部分,新增部分
-
对新增部分数据成员初始化,子类构造方法中直接
this.新增数据成员 = 值;
即可 -
对继承部分数据成员初始化
-
子类构造方法中直接
super.继承数据成员 = 值;
Bug:父类中数据成员如果private修饰,则子类中没有权限直接访问
-
子类构造方法中采用
super.setXxx(值);
对继承部分数据成员进行初始化Bug:过于繁琐,如果从父类继承的成员很多,要写很多set方法
-
子类对象的初始化(构造方法中),要借助父类的构造方法,对父类部分成员进行初始化
-
子类构造方法通过super关键字调用父类构造方法
格式:
super(实际参数列表);
-
子类构造方法前,会优先找到父类构造方法调用,对父类继承部分成员进行初始化
-
父类部分初始化完成后,再执行子类构造方法代码
-
如果子类构造方法中没有显式通过
super
调用父类构造方法,则系统会默认调用super()
案例展示:
package com.briup.chap06.test;
//定义父类
class Fu {
//1个私有成员
private int f;
//2个构造方法
public Fu() {
System.out.println("in Fu() ...");
}
public Fu(int f) {
System.out.println("in Fu(int) ...");
this.f = f;
}
//对应的get|set方法
public int getF() {
return f;
}
public void setF(int f) {
this.f = f;
}
}
//定义子类
class Zi extends Fu {
//新增1个数据成员
private int z;
//子类构造方法
public Zi() {
//如果不显式调用super,则默认调用父类无参构造器
//super();
System.out.println("in Zi() ...");
}
public Zi(int z) {
super(10);
System.out.println("in Zi(int) ...");
this.z = z;
}
public Zi(int f, int z) {
//下面这行注释的代码,放开则编译报错
//System.out.println("in Zi(int,int) ...");
//super调用,必须为子类构造方法的第一行有效代码
super(f);
System.out.println("in Zi(int,int) ...");
this.z = z;
}
// 新增方法
public void disp() {
//借助super可以直接访问父类继承部分的成员
System.out.println("super.f: " + super.getF());
//借助this,会先去找子类新增getF(),如果找不到,再去父类继承部分查找
System.out.println("this.f: " + this.getF());
System.out.println("Zi.z: " + z);
}
}
public class Test04_Init {
public static void main(String[] args) {
Zi z1 = new Zi();
z1.disp();
System.out.println("---------------");
Zi z2 = new Zi(20);
z2.disp();
System.out.println("---------------");
Zi z3 = new Zi(100,200);
z3.disp();
}
}
运行效果:
注意1:子类构造方法中如果显式调用super(实参列表),则该代码必须为第一行有效代码!
注意2:子类构造方法中显式调用的super(实参列表),父类中必须提供,否则编译报错!