这是一篇关于如何正确理解Java转型机制的文章
谨以此文纪念明天要默单词我还在复习Java谁让他明天考Java呢
什么是转型
就是转型啊还能怎么说!子类转父类,父类转子类巴拉巴拉。父类转子类需要注意
这里边有什么不好理解呢,主要有这几点:
- 假如说子类重写了父类的一个函数,那么不管子类转父类或者父类转子类,转完了之后调用该函数,那么执行的是父类的函数还是子类的呢?
- 到底谁可以转成谁?还是说可以随意互相转?
(当然不是)
- 强制类型转换是什么?
本文将以如下两个类进行讲解,第一个是父类Person,只有一个方法WhatAreYou:
class People
{
public People() {
// TODO Auto-generated constructor stub
System.out.println("Father");
}
public void WhatAreYou()
{
System.out.println("我是一个人类");
}
}
第一个子类,男人,继承了父类People的WhatAreYou,同时有自己独特的方法Hunt:
class Men extends People
{
public Men() {
// TODO Auto-generated constructor stub
System.out.println("Men");
}
public void Hunt() {
//男人独有的打猎技能
System.out.println("Men hunting");
}
public void WhatAreYou()
{
System.out.println("I am A Man");
}
}
第二个子类,女人,继承了父类People的WhatAreYou,同时有自己独特的方法Breed(哺育小孩):
class Women extends People
{
public Women() {
// TODO Auto-generated constructor stub
System.out.println("Women");
}
public void WhatAreYou()
{
System.out.println("I am a Woman");
}
public void Breed()
{
//女人才能哺育小孩
System.out.println("Women Breeding child");
}
}
子类转父类
先来看一段代码:
public static void main(String[] args)
{
People people = new Men();
people.WhatAreYou();
people = new Women();
people.WhatAreYou();
}
输出结果是:
第一行输出Father是父类构造函数,都是先执行父类构造函数后执行子类构造,可以显示调用父类构造函数(关键字Super)
第二行是子类Men的构造函数的输出。
高潮来了:
people是People
型引用,但是给他一个Men的对象后,调用其WhatAreYou函数就是调用了子类
的函数。
下一行的给people赋值Women也是一样道理。
父类转子类
这能行么…看下边这个代码(只截图了)
把父类转到子类直接报错
来看看编译器给出的解决方案:
第一种是把
Men men = new People()
改为Men men = (Men)new People()
来看看这样改会怎么样:
哦编译确实是不报错,但是
运行阶段抛出异常
也就是说,
父类不能转换为子类
.但是这也是有例外的,
如果父类引用本就是由子类转化而来,那么他可以转为子类。
看如下一段代码:
public static void main(String[] args)
{
People people = new Men();
Men men = (Men)people;
men.WhatAreYou();
}
这段代码很皮地将子类转为父类,由转回子类。那么会不会报错呢?看运行:
完美运行。
这里其实涉及到能不能强制转换的问题。比如一个Integer对象,就不能转换为一个string对象,谁让他不是string的子类呢?
java提供了用于判断能不能完成强制转型的关键字:instanceof,用法如下:
if ( A instanceof ClassB ) { ClassB b = (ClassB)A ;}
总结
经过试验,得出结论:子类无压力转父类,父类转子类有条件,那就是该父类对象本就是由子类转的。
或许是这样:
子类转父类实际上是用一个父类型的引用指向子类型的对象,而父类型的引用这个东西,是专门为了服务父类的对象而产生的,可以说其中放了一个表,表项一个是父类中的变量和函数,另一个就是其指向的实体对象中相应的变量和函数。由于子类是父类的扩展,所以子类中一定有父类中有的变量和函数,所以该父类引用的表可以填满,正常使用;而反过来说,子类型的引用的表中有一些是父类所没有的,表项无法填满,故而出错。而父类转子类的那个特殊情况,可能是JVM给对象打了一个tag。
大体如下图所示:
这是向上转型:
父类转子类:
实体对象中有函数没有被表项指向无所谓,大不了不用这个函数;(实际也就是这么干的)
但是如果表项中有项目没有目标是不行的,表项中的东西是登记了给人用的,项目没有目标用个锤子?
引用中存放表项的方法是我为了简化理解想出来的,实际可能与此不用,建议读者查阅介绍JVM的书籍自行理解
!!!!!有一句话要记住,函数表是根据引用的类型调用的,但是实际运行的是实体对象的函数体
标了!!!!!的那句话极其重要
标了!!!!!的那句话极其重要
或者皮一点,儿子都想当老子
,你让他当老子他屁颠屁颠就去了;(向上转型)
老子不想当儿子,除非他原来就是儿子,破格当了一会儿老子
,现在要把他变回儿子,就要强制转成儿子(向下转型,只能强制转换,而且有条件)