向上转型
按上次总结先抛出部分代码
class Person {
protected String name;
public Person(String name){
this.name = name;
}
public void dress (String color) {
System.out.println(this.name+"穿了一件"+color+"的短袖");
}
}
// 使用 extends 关键字来继承父类
class Boy extends Person {
public Boy (String name) {
//调用父类的构造方法
super(name);
}
//增加一个行为
public void play(){
System.out.println(this.name+"正在打羽毛球");
}
}
class Gril extends Person {
public Gril (String name) {
super(name);
}
//增加一个行为
public void play() {
System.out.println(this.name+"正在化妆");
}
}
开始调用类及其方法:
public class Test {
public static void main() {
Boy boy = new Boy("小毕");
boy.dress("白色");
}
}
输出:
小李穿了一件白色的短袖
但是这段代码也可以用如下形式表达:
public class Practice{
public static void main(String[] args) {
Person boy = new Boy("小李");
boy.dress("白色");
//或者写成
//Boy boy = new Boy("小李");
//Person boy2 = boy;
//boy2.dress("白色");
}
}
输出:
小李穿了一件白色的短袖
这样的写法中,boy是一个父类的引用,但是指向了子类Boy,这种子类对象转化为父类就称为向上转型。
public class Practice{
public static void main(String[] args) {
Person boy = new Boy("小李");
boy.play; // 报错 Cannot resolve method 'play()
}
}
虽然boy 引用的是 Boy 类的实例,但是编译器是以 Person 的类型来查看有哪些方法的,而Person内没有,Boy中有的方法则无法执行。
- 编译器检查存在哪些方法,看的是 boy 的类型
- 执行时究竟要执行父类还是子类的方法,看的是 Boy的类型
如果要执行子类中的方法,则要向下转型(与向上转型相反,即是把父类对象转为子类对象):
public class Practice{
public static void main(String[] args) {
Person boy = new Boy("小李");
Boy boy2 = Boy (boy);
boy.play;//正常输出
}
}
并且,向上转型发生的时间除了直接赋值外,还包括方法传参和方法返回。
注意:
- 父类引用指向子类对象,但子类引用不能指向父类对象
- 把子类对象直接赋给父类引用叫做向上转型,不需要强制转换
动态绑定
当子类和父类出现同名方法时,再调用会出现动态绑定的情况。
先为父类添加与子类相同的方法:
//父类
class Person {
protected String name;
public Person(String name){
this.name = name;
}
public void dress (String color) {
System.out.println(this.name+"穿了一件"+color+"的短袖");
}
public void play() {
System.out.println(this.name+"不知道在干嘛");
}
}
// 使用 extends 关键字来继承父类
class Boy extends Person {
public Boy (String name) {
//调用父类的构造方法
super(name);
}
@Override
public void play(){
System.out.println(this.name+"正在打羽毛球");
}
}
//子类
class Gril extends Person {
public Gril (String name) {
super(name);
}
@Override
public void play() {
System.out.println(this.name+"正在化妆");
}
}
调用试运行:
public class Practice{
public static void main(String[] args) {
Person He = new Person("他");
Person She = new Gril("她");
He.play();
She.play();
}
}
输出:
他不知道在干嘛
她正在化妆
这里的 He 和 She 都是父类的引用,但是He指向了父类的实例,She指向了子类的实例
因此他们都调用play方法时,发现He调用了父类的方法,She调用了子类的同名方法。
可见,在Java中,调用某个类的方法,执行了哪段代码,要看这个引用指向了哪个实例,如果是父类实例,则执行父类的方法,如果只想子类实例,则执行子类方法,并且这个过程是运行时决定的,所以叫做动态绑定。
而如同上面,父类的 play 方法和 子类的 play 同名方法,它们的参数类型和个数完全相同,但是方法内部代码不同,这种情况称为方法重写。
但是值得注意的是:
- 方法的重写和重载完全不同
- 普通方法可以重写,但static静态方法不可以重写
- 重写的方法的访问权限不能低于父类的访问权限
- 重写的方法返回值类型不一定和父类的返回值类型相同(最好相同)
- 进行重写时,建议在重写方法上显示加上 @Override 注解