对象与内存控制

        该文为《疯狂java 突破程序员基本功的16课》读书纪要。

        注意以下代码:

class Test {
	int num1 = num2 + 2;
	static int num2 = 10;
}

        在java中定义成员变量时,必须采用合法的前向引用,即若num1=num2+2,则必须先定论num2,当前,必须采用合法的前向引用的前提是:num1和num2同时为成员变量或类变量(static修饰),如上述代码中,num1为成员变更,num2为类变量时,则无须遵守这一要求。



        有以下代码:

/**
 * 
 */
package com.gsoft.geloin;

import junit.framework.TestCase;

import org.junit.Test;

/**
 * @author Geloin
 * 
 */
public class MyTest extends TestCase {

	@Test
	public void testArrays() throws Exception {
		new Derived();
	}
}

class Base {
	private int i = 2;

	public Base() {
		this.display();
	}

	public void display() {
		System.out.println(i);
	}
}

class Derived extends Base {
	private int i = 22;

	public Derived() {
		i = 222;
	}

	public void display() {
		System.out.println(i);
	}
}

        Derived继承Base,按照JVM规则,调用new Derived()时,系统开始为这个Derived对象分配内存空间,此时,Derived对象有两个i实例变量,也即是说,系统会分配两个i的内存给Derived。与我们的认知不同: 构造器只是负责对Java对象实例变更进行初始化(也就是赋初始值),在执行构造器代码之前,该对象所占用的内存已经被分配下来,这些内存里值都是默认的空值。

        当程序调用new Derived()方法时,系统会先为Derived对象分配内存空间,此时系统内存需要为这个Derived对象分配两块内存,它们分别用于存放Derived对象的两个i实例变量,其中一个i属于Base定义的实例变量,另一个i属于Derived对象定义的实例变量,此时,这两个i实例变量的值都是0。

        接下来,程序在执行Derived的构造器之前,会首先执行Base的构造器,于是执行以下两行代码:

        i = 2;
        this.display();

        即先将Base类中定义的i初始化为2,再调用this.display()方法,此处有一个关键:this代表谁?

        当this在构造器中时,this代表正在初始化的Java对象。此时的情况是:此时的this位于Base构造器内,但目前正在初始化的Java对象是Derived——是Derived()构造器隐式调用了Base()构造器的代码。由此可见,此时的this应该代表Derived对象。

        既然this代码Derived对象,那我们再改写Base构造子,将它变更成以下代码:

	public Base() {
		System.out.println(this.getClass());
		System.out.println(this.i);
		System.out.println(this.getClass());
		this.display();
	}

        再次运行单元测试代码,发现结果如下所示:

class com.gsoft.geloin.Derived
2
class com.gsoft.geloin.Derived
0
        好吧,问题来了,既然this,表示的是Derived,那么为什么打印this.i时,显示的是2呢?
        这里涉及到另外一个概念:编辑时和运行时。编译时顾名思义就是正在编译的时候.而所谓的编译,就是编译器帮你把源代码翻译成机器能识别的代码,简单来说,编译时就是简单的作一些翻译工作;所谓运行时就是代码跑起来了.被装载到内存中去了。

        需要注意的是:当变量的编辑时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定。但通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际所引用的对象决定。

        也就是说,在一个构造子中,调用this.变量名时,无论如何,这个this都是指它所在的类;当调用this.方法时,这个this则表示它实际上的类型。如上代码所示,调用this.i时,取得的是Base类中被初始化的i,而调用this.display()时,调用的则是Derived的display方法。

        显然,通过以上代码,我们又要推翻我们以前的认知了——子类可以调用父类的方法,但父类不可能调用子类的方法——但实际上,如果子类重写的父类的方法,且父类的空构造子调用了该被重写的方法时,则父类能够调用子类的方法——如以上代码所描述。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值