向上转型
我们经常在代码中看到这样的写法
父类型 fat = new 子类型();
这便是向上转型的实例,即父类引用指向子类对象。
一个完整的实例
package test;
/**
* @Author: Lemon
* @Program:
* @Description:向上转型
* @Date 2019/10/28-20:31
*/
public class Father {
void eatfood(){
System.out.println("吃饭");
}
}
package test;
/**
* @Author: Lemon
* @Program:
* @Description:向上转型
* @Date 2019/10/28-20:33
*/
public class Son extends Father {
void playgame(){
System.out.println("打游戏");
}
}
package test;
/**
* @Author: Lemon
* @Program:
* @Description:向上转型
* @Date 2019/10/28-20:34
*/
public class Client {
public static void main(String[] args) {
Father father = new Son();
father.eatfood();
father.playgame(); //这里会报错
}
}
以上代码使用了向上转型,创建了一个Father对象指向子类对象内存。但当代码运行到 father.playgame(); 时会报错。其实很容易理解,我们在栈中开辟一个Father对象的引用,但在堆中按照其子类对象开辟了一个内存空间,实际类型还是Father,所以无法调用父类中没有的方法。
此时我们会感觉向上转型似乎没啥用,反而加大我们对代码理解的难度。请看下面的例子:
package test;
/**
* @Author: Lemon
* @Program:
* @Description:向上转型
* @Date 2019/10/28-20:31
*/
public class Father {
void eatfood(){
System.out.println("爸爸吃饭");
}
void sleep(){
System.out.println("爸爸睡觉");
}
}
package test;
/**
* @Author: Lemon
* @Program: LeetCode
* @Description:
* @Date 2019/10/28-20:33
*/
public class BigSon extends Father {
//重写父类方法
void eatfood(){
System.out.println("大儿子吃饭");
}
void sleep(){
System.out.println("大儿子睡觉");
}
}
package test;
/**
* @Author: Lemon
* @Program:
* @Description:向下转型
* @Date 2019/10/28-20:48
*/
public class SmallSon extends Father {
//重写父类方法
void eatfood(){
System.out.println("小儿子吃饭");
}
void sleep(){
System.out.println("小儿子睡觉");
}
//子类自己的方法
void play(){
System.out.println("玩耍");
}
}
package test;
/**
* @Author: Lemon
* @Program:
* @Description:向上转型
* @Date 2019/10/28-20:34
*/
public class Client {
public static void main(String[] args) {
display(new BigSon());
display(new SmallSon());
}
static void display(Father father){
father.eatfood();
father.sleep();
}
}
运行结果:
在上述的例子中,大儿子类和小儿子类都重写了父类的方法,在Client类中写了一个display方法用来统一调用,使用了向上转型所以在main()只需要创建相应的对象即可。
如果不使用向上转型,则实现方式如下
package test;
/**
* @Author: Lemon
* @Program:
* @Description: 向上转型
* @Date 2019/10/28-20:34
*/
public class Client {
public static void main(String[] args) {
display(new BigSon());
display(new SmallSon());
}
static void display(BigSon bigSon){
bigSon.eatfood();
bigSon.sleep();
}
static void display(SmallSon smallSon){
smallSon.eatfood();
smallSon.sleep();
}
}
运行结果同上,但哪个方便已经很明显了。
接口引用指向实现类的对象
提到向上转型还不得不提的就是接口引用指向实现类的对象。
其可以看做向上转型的的一种实例情况,在实际使用中使用的也很广泛。
创建的对象只能调用接口中存在的方法。
向下转型
向下转型就是将父类对象再转换成子类对象,实际操作如下(父类和子类的方法还是参照上面的例子):
package test;
/**
* @Author: Lemon
* @Program:
* @Description: 向下转型
* @Date 2019/10/28-20:34
*/
public class Client {
public static void main(String[] args) {
Father father = new SmallSon();
SmallSon ss = (SmallSon)father;
ss.sleep();
//向下转型后即可调用子类自己的方法
ss.play();
}
}
分析:father指向子类对象的堆内存,ss也指向子类对象的堆内存,此时子类对象是一致的,ss自然也可以指向同样一块堆内存。
此时于SmallSon ss = new SmallSon();无区别