1. This 指针的用途
2. 实例对象是如何调用实例构造函数以及实例方法的.
3. CLR如何调用一个类的静态方法
4. 抽象类到底能不能有构造函数,为什么抽象类不能有public构造函数。
1. CLR是如何调用实例构造函数以及实例方法的
一个类中可以定义实例字段,方法,也可以定义静态字段和方法,他们的布局如下:
当我们使用new操作符创建一个对象时,会在堆中为这个对象分配内存,那么这个实例对象中都有什么呢
当 CLR实例化一个对象时,首先会初始化一个指向类型对象的指针,然后为所有实例字段初始化为相应的默认值,这个默认值一般是清零,清零只需底层cpu的一个指令就可以了,他的速度是非常快的.然后java虚拟机会调用这个对象的构造函数,将实例字段初始化为正确的值.
现在的问题是,CLR如何为这个对象调用构造函数,因为从上图中可以明显的看出,对象与构造函数在两个不同的地方,对象在堆中,构造函数类型对象中;
事实上,实例构造函数与其他实例方法没有什么区别,从本质来说他们都是方法。因此如果理解了对象是怎么调用构造函数的,也就理解了构造函数是怎么调用实例方法的。
上面说过,对象与方法在两个不同的地方,两者之间肯定有一个桥梁,从而可以通过对象去找到他的方法。这个桥梁就是上面的类型对象指针。
在CLR中,每一个在堆上创建的对象都有一个类型对象指针,CLR负责初始化这个指针,将这个指针指向方法区的类型对象。CLR通过这个类型对象指针,就可以找到这个对象对应的方法。
现在讨论一个完整的过程,请看如下代码:
publicstaticvoid main(String[] args) {
Point p1=new Point(3,3);
}
class Point{
publicintx;
publicinty;
public Point(int x,int y){
this.x=x;
this.y=y;
}
}
1. CLR在堆上分配一块内存,然后初始化类型对象指针,使其指向方法区的Point类型对象。
2. 将Point对象所有实例字段清零。
3. CLR通过类型对象指针找到方法区的Point类型对象,然后遍历找到public Point(int x,int y)构造函数。
4. CLR开始调用这个构造函数。Java虚拟机会为这个实例构造函数隐式的传递一个this指针,这个this指针就是p1对象的地址。通过这个this指针,构造函数就可以访问p1对象的x和y了。如下:
现在我们总结一下,java虚拟机是如何将实例对象与方法区的类型对象联系起来的,这里有两个关键的地方起到了链接作用,一个是类型对象指针,另一个是this指针。
CLR通过类型对象指针找到属于这个对象的方法,通过this指针,实例方法就可以访问实例对象的字段了。
2. CLR是如何调用静态方法的
现在稍加思考就可以明白,为什么静态函数不能使用实例字段,不能调用实例方法。现在也能够明白如下代码为什么不能通过编译了!
classPoint{
publicintx;
publicinty;
public Point(int x,int y){
this.x=x;
this.y=y;
}
publicvoid draw(){
}
publicstaticvoid PP(){
x=4;
}
当调用一个静态方法时,不会向这个方法传递this指针,因此,这个静态方法就不能访问实例对象的实例字段了。再强调一点,一个方法要想访问对象的实例字段,必须通过this指针。
由于静态方法不能够实用实例字段,因此也就不能够调用实例,因为实例方法可能实用实例字段。
3.抽象类的构造方法
现在,再来讨论抽象类可不可以有构造函数,以及为什么抽象类不能有public构造函数。
抽象类中可能会定义实例字段,这些实例字段在使用前必须先被初始化。而初始化实例字段就要用到实例构造函数,因此抽象类可以有实例构造函数,也必须有。
还有,在继承体系中,在创建一个派生类型对象之前,必须先调用基类的构造函数来初始化从基类继承的成员。
因为抽象类不能被实例化,也就是不能new 一个抽象类。为了防止用户不小心写出如下的代码 ,CLR规定抽象类不能有public类型的构造函数,从而上述代码在编译期间就可以发现错误。
因此,我们可以知道,抽象类不能有pubic构造函数,这不是硬性规定。但是一般语言中,为了防止用户不小心new 一个抽象类,都会规定抽象类不能定义public构造函数。