JAVA内存分析中的两道简单小题
来源:互联网 发布:mysql连接池配置文件 编辑:程序博客网 时间:2024/05/20 04:11
这两段程序出自李刚《疯狂JAVA突破程序员基本功的16课》中的两段小代码,非常简单,但初学者很难掌握,考虑到内存分析,以及java虚拟机执行机制
代码一:
class Base {int count = 2;}class Mid extends Base { int count = 20;}public class Sub extends Mid {int count = 200;public static void main(String args[]) {Sub s = new Sub();Mid s2 = s;Base s3 = s;System.out.println(s.count);System.out.println(s2.count);System.out.println(s3.count);}}
上面程序中定义了3个带有父子关系的类,Base派生了Mid,Mid派生了Sub,而且这三个类中都定义了名为count的实例变量,程序创建了一个Sub对象,并将这个Sub对象向上转型,s2,s3同时指向了s对象,那么是不是输出200,200,200??
显然猜错了,程序将会输出200,20,2。这意味着,s,s2,s3这3个变量所指向的java对象拥有三个count实例变量,也就是说需要3块内存存储它们,Sub对象不仅存储了它自身的count实例变量,还需要存储从Mid,Base两个父类那里继承过来的count实例变量,但这3个count实例变量在底层室是有区别的,程序通过Base型变量来访问对象的count值时将输出2,通过Mid型的变量来访问该对象的count实例变量时,将输出20;当直接在sub类中访问实例变量count时,程序显然会输出200,既访问到sub类中定义的实例变量count。为了在sub类中访问Mid类定义的count实例变量,可以在count实例变量之前增加super关键字作为限定。
下面给出内存分析图:
代码二:
class A {private int i = 2;public A() {this.display();}public void display() {System.out.println(i);}}class B extends A {private int i=22;public B() {i = 222;}public void display() {System.out.println(i);}}public class BaseTest {public static void main(String args[]) {new B();}}
看到这段代码,你会想到输出的代码是2?22?还是222?
结果是0,出乎意料吧,这看上去很奇怪!
接下来将详细介绍这个程序的运行过程,从内存分配的角度来分析程序输出的结果,从而更好的把握程序运行的真实过程,
new B()的时候系统开始为这个对象分配内存空间,需要指出的是这个对象并不是只有一个i实力变量,他将拥有两个i实例变量。
注意:关于一个java对象怎样拥有多个同名的实例变量,子类定义的成员变量并不能完全覆盖父类中成员变量!!!
为了解释这个程序,首先需要澄清一个概念:java对象都是由构造器创建的么?很多书籍资料中会说:是的!!!
但实际情况是:构造器只是负责对java对象实例变量执行初始化(也就是赋初始值)!!!
在执行构造器代码之前该对象所占的内存已经被分配下来,这些内存里的值都是默认是空的值----对于基本类型的变量,默认控制是0或false;
对于引用类型的变量,默认空值就是null;
new B()的时候,系统会为B对象分配内存空间,此时系统内存需要为这个B对象分配两块内存,它们分别用于存放B对象的两个i实例变量,其中一个属于A类定义的i实例变量,一个属于B类定义的i实例变量。此时这两个i实例变量的值都为0。
接下来程序在执行B类构造器之前首先会执行A类构造器,表面上看A类的构造器内只有一行代码this.display();但是由于A类定义i实例变量时制定了初值2,因此经过编辑器处理后该构造器应该包含两行代码。
i=2;this.display();
因此A类中定义的i实例变量赋值为2;再调用this.display()方法;此时有一个关键:this代表谁???
回答这个问题之前,先进行一些简单的修改,将Base类的构造器改为如下形式,
public A() {// 直接输出this.i;System.out.println(this.i);this.display();}
现在A构造器表里有2行代码,实际上应该有3行代码
i=2;System.out.println(this.i);this.display();再次运行,结果是2,0;看到这样的结果,可能会有人更加混乱了,此时的this到底代表谁?
笔者在《疯狂java讲义》中已经指出:当this在构造器当中时,this代表正在初始化的java对象,此时的情况是:从源代码来看,此时的this位于B构造器中,但这些代码实际上放在A构造器内执行,是B构造器隐式的调用了A构造器的代码,由此可见,这里的this应该是B对象,而不是A对象.
现在的问题又出现了,既然this代表了B对象,那么直接输出this.i时会输出2呢?这是因为,这个this虽然代表B对象,但它却位于A构造器中,它的编译时类型是A,而它实际上引用了一个B对象。为了证实这一点,再次改写程序。
public A() {i = 2;//直接输出this.iSystem.out.println(this.i);this.display();//输出this实际的类型,将看到是BSystem.out.println(this.getClass());//因为this的编译类型是A,所以不能调用sub();//this.sub();}
看到java的博大精深了吧!!!
- JAVA内存分析中的两道简单小题
- Java程序开发中的简单内存分析
- Java程序开发中的简单内存分析
- Java程序开发中的简单内存分析
- Java程序开发中的简单内存分析
- Java内存简单分析
- Java内存简单分析
- Java中的内存分析
- java中的内存分析
- Java小实例内存分析
- java中简单内存分析:
- Java中的内存泄漏分析
- 简单理解JavaSe基础中的内存分析
- Java对象内存布局 - 小分析
- Java程序内存的简单分析
- Java内存泄漏简单的分析总结
- java-栈、堆内存简单分析
- Java程序内存的简单分析
- 山东理工ACM【1135】C/C++经典程序训练5---图形打印问题
- RadioGroup变为按钮工具条
- 在你步入职业软件开发生涯那天起就该知道的五件事
- java多线程中的 toSring 方法和setPriority方法
- HDU 4268-Alice and Bob( STL: set)
- JAVA内存分析中的两道简单小题
- 线性筛素数 线性筛欧拉函数
- C++课上ppt2 类和数据抽象
- java web项目操作mysql数据库的数据封装
- Python装饰器与面向切面编程
- 杭电oj题目分类
- RichEdit中文格式错误
- 静态数据成员和静态成员函数
- 全面剖析Cocos2d游戏触摸机制 (上)