欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

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

发布时间:2025/6/15 编程问答 42 豆豆
生活随笔 收集整理的这篇文章主要介绍了 关于java中多态的理解,涉及到内存空间 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

其实理解了程序在内存里如何搞,如何玩,是很爽的。

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

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方法的注释去掉,则会在无需修改其他类的情况下实现鸟叫声的扩展),大大提高了我们程序的灵活性。

    总结:

            多态在之前已有过学习,但理解并不是深入,通过这次学习的相应内存分析,还是深刻了很多,对内存也有过一些了解,但之前并没有将这两方面的学习联系起来,分析内存对程序的运行过程有了进一步的了解。


    总结

    以上是生活随笔为你收集整理的关于java中多态的理解,涉及到内存空间的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。