您的位置:首页 > 编程语言 > Java开发

关于java中多态的理解,涉及到内存空间

2014-11-11 23:00 603 查看
其实理解了程序在内存里如何搞,如何玩,是很爽的。

首先简要说明下程序运行时,内存的结构。堆区栈区,常量区,静态方法区和非静态方法区。

1.栈:存放基本类型的变量数据和对象的引用(也就是在new对象时左边那一块),但是对象本身不放在栈中,而是存在堆(new出来的对象)。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。

2.堆:存放new出来的对象。堆中的对象由垃圾回收器负责回收,因此大小和生命周期不需要确定。

3.常量区:存放字符串常量和基本类型常量。

代码示例:

[java] view
plaincopy





<span style="font-family:Microsoft YaHei;">class Fu  

{  

    void method1()  

    {  

        System.out.println("fu method_1");  

    }  

    void method2()  

    {  

        System.out.println("fu method_2");  

    }  

    static void method3()  

    {  

        System.out.println("fu method_3");  

    }  

}  

class Zi extends Fu  

{  

    void method1()  

    {  

        System.out.println("zi method_1");  

    }  

    void method4()  

    {  

        System.out.println("zi method_4");  

    }  

    static void  method3()  

    {  

        System.out.println("fu method_3");  

    }  

}  

class DemoDuotai  

{  

    public  static void main(String args[])  

    {  

        Fu aa=new Zi();//创建一个父类引用,指向子类。  

        aa.method1();//发生动态绑定,打印子的  

        aa.method3();//打印父的  

    }  

}</span>  

内存图简要示例



分析:

1.我们知道类的非静态成员函数,都是得靠类的对象来调用的,也就是所谓的this.。那么在这句

Fu ff=new Zi();

ff.method1();


当我们aa.method1()-->this.method1()-->(new Zi()).method1().aa确实是指向Zi类的对象,method1()在运行时一定是被对象调用执行,而不是aa,因为他打印的时候或者访问的时候是访问对象中的那些数据。

2.而静态方法就不同了,他本身不访问对象特有数据。当Fu类和Zi类被加载到内存的时候,静态方法区就已经在里面啦,并且绑定在这个方法所属的类上面了,所以他们只直接用类名可以调用,也就是所谓的静态绑定。他找的是静态区的方法,不参考右边的对象。所以

aa.method3();
只参考这个引用型变量aa是谁的。

总结:

1.编译时期:参考应用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有,编译失败。

2.运行时期:参考对象(new出来的对象)所属的类中是否有调用的方法。(动态绑定)

也就是:成员函数在多台调用时,编译看左边,运行看右边。

3.多态中,成员变量的特点:无聊编译运行,都参考左边(引用型变量所属的类)。

4.多态中,静态函数的特点:无聊编译运行,都参考左边。(静态绑定)

再举个例子来说明编译时(javac)和运行时(java)的区别,代码如下:

[java] view
plaincopy





class Demo  

{  

    private int num;  

    Demo(int num)  

    {  

        this.num=num;  

    }  

    public boolean equals(Object d)  

    {  

        Demo a=(Demo)d;  

        return this.num==a.num;  

    }  

}  

class Person  

{  

  

}  

public class object_equals  

{  

    public static void main(String[] args)  

    {  

        Demo a=new Demo(3);  

        Demo b=new Demo(4);  

        Person c=new Person();  

        System.out.println(a.equals(c));  

    }  

      

}  

结果:编译通过,运行挂掉
分析:

equals是所有类的超类Object的方法,我们把他复写掉,调用equals时,参数 Object d=new Person()这里发生了向上转型,接着

Demo a=(Demo)d 编译的时候是不会出错的,因为此处的d引用变量是绑定的Object这个类;而运行的时候,发生动态绑定也就绑定到了Person类上,把Person类的对象转化成Demo类的对象,出现类型转换错误。

改正:

加上 if(!d instanceof Demo) return ;

判断一下即可.

 多态(polymorphism):在执行期间(而非编译器)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。由于是在运行期的动态实现,直到new出对象才能确定具体调用的哪个方法,因此多态又叫动态绑定,也叫迟绑定。

   实现条件

                1.类之间继承或者实现接口;

            2.方法重写;

            3.父类引用指向子类对象。

   练习

                   猫、狗类实现抽象类动物的enjoy()方法,main方法中实现enjoy()方法的动态调用。

            代码实现:

abstract class Animal {
private String name;
Animal(String name) {
this.name = name;
}
public abstract void enjoy();
}

class Cat extends Animal {
private String eyescolor;
Cat(String name,String eyescolor) {
super(name);
this.eyescolor = eyescolor;
}

public void enjoy() {
System.out.println("猫叫声。。。");
}
}

class Dog extends Animal {
private String furcolor;
Dog(String name,String furcolor) {
super(name);
this.furcolor = furcolor;
}

public void enjoy() {
System.out.println("狗叫声。。。");
}
}
/*
class Bird extends Animal {
Bird() {
super("bird");
}
public void enjoy() {
System.out.println("鸟叫声。。。");
}
}
*/

class Lady {
private String name;
private Animal pet;
Lady(String name, Animal pet) {
this.name = name;
this.pet = pet;
}
public void myPetEnjoy() {pet.enjoy();}
}

public class Test {
public static void main(String[] args) {
Dog g = new Dog("mydog","black");
Cat c = new Cat("mycat","blue");
Bird b = new Bird();
Lady l1 = new Lady("l1",g);
Lady l2 = new Lady("l2",c);
//Lady l3 = new Lady("l3",b);
l1.myPetEnjoy();
l2.myPetEnjoy();
//l3.myPetEnjoy();
}
}


               运行结果:



               内存分析:

                 


             stack中存放临时变量,heap中存放new出的实例对象,为简单起见,图中略去了代码中Dog类对象以及相应的l1对象。

        多态的实现是通过调用方法为运行时动态生成的Cat对象指向的Cat:enjoy()方法实现。

特性:

        多态的存在能够更好的让我们去实现代码的可扩展性(具体练习中可将Bird类、main方法的注释去掉,则会在无需修改其他类的情况下实现鸟叫声的扩展),大大提高了我们程序的灵活性。

总结:

        多态在之前已有过学习,但理解并不是深入,通过这次学习的相应内存分析,还是深刻了很多,对内存也有过一些了解,但之前并没有将这两方面的学习联系起来,分析内存对程序的运行过程有了进一步的了解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: