关于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出对象才能确定具体调用的哪个方法,因此多态又叫动态绑定,也叫迟绑定。
2.方法重写;
3.父类引用指向子类对象。
stack中存放临时变量,heap中存放new出的实例对象,为简单起见,图中略去了代码中Dog类对象以及相应的l1对象。
多态的实现是通过调用方法为运行时动态生成的Cat对象指向的Cat:enjoy()方法实现。
特性:
多态的存在能够更好的让我们去实现代码的可扩展性(具体练习中可将Bird类、main方法的注释去掉,则会在无需修改其他类的情况下实现鸟叫声的扩展),大大提高了我们程序的灵活性。
首先简要说明下程序运行时,内存的结构。堆区栈区,常量区,静态方法区和非静态方法区。
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内存的理解
- java关于多态的一些理解
- 关于Java多态的理解。
- 关于java常用的内存理解
- 关于java中的内存的理解总结
- 【Java】关于JVM运行时内存空间、JVM垃圾回收机制
- Java中关于多态的理解
- 关于java多态的理解
- java数组的定义初始化关于内存的理解
- 关于java中的多态理解
- 关于java继承与多态的深入理解
- 关于Java 多态的理解及案例
- 关于Java占用内存的研究
- 关于java中的synchronized关键字的理解
- 关于JAVA内存泄漏问题注意事项
- 讨论关于Java占用内存的研究
- 关于java反射的一些理解
- 关于Java占用内存的研究
- 关于VO、PO的理解-java的(PO,VO,TO,BO,DAO,POJO)解释(转载)