最近在看JAVA教学的视频,觉得老师讲的很好,同时借用源代码还有笔记来撰写本系列博客,记录自己的学习内容,同时也供看到的人学习。
开始NO.14篇,这篇介绍的是JAVA面向对象的最后一个特征:多态。
先来看看多态的具体含义:
多态最直接的表现就是多个子类重写共有父类的一个方法,而在调用这个方法的时候只传入父类的类型,通过向上转型、强制转型等操作又具体到某个子类重写的方法的内容,转而执行那个子类重写的这个方法,节省了大量的代码空间。具体实现见下面的代码,都是关于细节部分的一些解释和总结我在原有代码的基础上进行了补充说明。
代码实现实例:
<span style="font-size:14px;">public class Animal {
String str;
public void voice(){
System.out.println("普通动物叫声!");
}
}
class Cat extends Animal {
public void voice(){
System.out.println("喵喵喵");
}
public void catchMouse(){
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void voice(){
System.out.println("汪汪汪");
}
public void seeDoor(){
System.out.println("看门!");
}
}
class Tiger extends Animal {
public void voice(){
System.out.println("哇哇哇");
}
}
class Pig extends Animal {
public void voice(){
System.out.println("哼哼哼");
}
}</span>
<span style="font-size:14px;">public class Test {
public static void testAnimalVoice(Animal c){ //注意这里传的是动物animal,没有具体的种类。多态通过传参的多样性会在这个方法里面节省大量的代码
c.voice();
if(c instanceof Cat){ //一个新的运算符:实例对象的判断(此句意思:如果c是Cat的一个实例对象,则之后把c强转为cat类型,并调用catchmouse方法。)
((Cat) c).catchMouse();
}
}
/* 如果不采用多态,则下面的代码要都写上才能正常运行本程序。
public static void testAnimalVoice(Dog c){
c.voice();
}
public static void testAnimalVoice(Pig c){
c.voice();
}*/
public static void main(String[] args) {
Animal a = new Cat(); //父类引用指向子类对象
Animal b = new Dog();
Animal c = new Tiger();
testAnimalVoice(a);
testAnimalVoice(b);
testAnimalVoice(c);
Cat a2 = (Cat)a; //注意这里。此句不能省略,省略了下面一句将会报错,因为在编译的时候a是animal型,animal里面没有catchmouse方法,所以会报错,想要成功执行必须重新转型成猫型,才能正常运行。
a2.catchMouse();
}
}
</span>
接下来把上述代码部分提取一部分,进行对应的内存分析,使我们更好地了解多态:
代码部分:
<span style="font-size:14px;">public class Blog {
public static void testAnimalVoice(Animal c){
c.voice();
if(c instanceof Cat){
((Cat) c).catchMouse();
}
}
public static void main(String[] args) {
Animal a = new Cat();
Cat a2 = (Cat)a;
testAnimalVoice(a);
}
}
</span>
<span style="font-size:14px;">public class Animal {
String str;
public void voice(){
System.out.println("普通动物叫声!");
}
}
class Cat extends Animal {
public void voice(){
System.out.println("喵喵喵");
}
public void catchMouse(){
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void voice(){
System.out.println("汪汪汪");
}
public void seeDoor(){
System.out.println("看门!");
}
}
class Tiger extends Animal {
public void voice(){
System.out.println("哇哇哇");
}
}
class Pig extends Animal {
public void voice(){
System.out.println("哼哼哼");
}
}
</span>
对应的内存分析图(图中的test类是上述代码中的Blog类~):
关于上述图的一些内容的解释:首先是在方法区里面吧相关的类还有信息放进去,这个不多做解释。接下来进行执行阶段,首先是对象堆里面由于继承的原因,Cat对象包含了父类以及父类的父类的全部内容,再加上自己重写的方法还有特有的方法,出现了这种抽象为嵌套型的形状,类似的内容可以参考笔记12,父类animal的引用a指向了子类Cat,所以指向堆中的首地址,接着强制转型把啊由Animal型转为cat型再赋给a2,所以a2也指向首地址,接下来调用testAnimalVoice方法,传参,把a传给形参c,c也指向首地址,接下来,c调用voice方法,如果c是Cat的一个实例引用(即如果a是指向c的首地址)则把c转为Cat型,在调用catchmouse方法。
最后介绍的内容是利用JSP开发里面一个很重要的类做一个多态的小例子:
代码如下:
public class HttpServlet {
public void service(){
System.out.println("HttpServlet.service()");
this.doGet(); //这里指的是整个对象的doGet方法(在内存图里面就是组外层的那个类的方法,这里this省略结果也是一样的,不要弄错)
doGet(); //与上行效果一样
}
public void doGet(){
System.out.println("HttpServlet.doGet()");
}
}
public class MyServlet extends HttpServlet {
public void doGet(){
System.out.println("MyServlet.doGet()");
}
}
public class Test {
public static void main(String[] args) {
HttpServlet s = new MyServlet();
s.service();
}
}
需要思考的问题是回到父类的service方法的时候里面涉及了调用doGet方法,那么这个doGet方法到底用的是谁的方法呢?
先看内存图:
从图中还有代码的解释部分可以看到调用的应该是子类的(最外层类的对象)doGet方法而不是父类的。即:输出结果如下图:
好了,这篇到此结束,内容很重要哦~