为了更加能方便的理解我们的静态函数、变量和成员函数、变量,通过两个问题来详细描述一下我们静态和成员的特点。
问题一:
class Demo03{
public static void main(String[] args){
A a=new A();
}
}
class A{
A a=new A();
}
我们发现平时在创建对象时,我们都是在实体类的外面创建该实体类的对象,现在是在该实体类的内部创建了一个对象,而且还是在该类的函数外面,这里就相当于创建的对象用作该类的成员变量,我们先来看一下运行后的结果:
我们发现运行后,出现了了StackOverflowError这个错误,这叫栈溢出异常,之前在介绍递归的时候,提到过这个错误,就是因为不断调用自身而没有函数的弹栈导致运行函数的栈内存满了,就产生了这样的错误。那么在这里是为什么会出现这样的异常呢?我们在前面介绍构造函数的时候,提到对象的加载的流程,在主类中创建一个对象就相当于调用了该实体类A相应的构造函数,但是我们在该实体类A的构造函数外面创建的对象,当然在创建完对象之后,还要在堆内存中对相应的成员变量进行默认的初始化,而这里在实体类A中创建的对象相当于该类的成员变量,因此在默认初始化的时候不断的去创建对象再进行初始化,而导致的构造函数不断进栈出现栈溢出错误。那么如何避免这样的错误出现呢,我们可以用init,因为在此函数只会在对象创建的时候只调用一次。
package Excise2;
public class Demo02 {
public static void main(String[] args) {
A a = new A();
}
}
class A{
A a;
public void init(){
this.a=new A();
}
}
问题二:
class Demo03{
public static void main(String[] args){
B b=new B();
System.out.println(b==b.b);
System.out.println(b.b==b.b.b);
System.out.println(b.b.b.b.b.b.b==b.b.b.b.b.b.b.b.b.b.b.b.b);
}
}
class B{
static B b=new B();
}
这里与上面的问题不同之处在于将实体类B中创建的对象用static修饰了,那么就是这个区别,就与上面产生了不同的运行结果:
通过运行发现,这里不仅没有出现错误还运行出了结果,原因是该实体类B在创建自己的对象时将创建的对象用static修饰了,那么表明此对象是全局静态的变量,在前面讲到过静态变量和函数是随着类加载的,那么在类加载到方法区后就已经创建了一个对象,在主类里面创建对象后,是不用对静态的变量进行默认初始化的,所以就不会存在一直调用构造函数使栈溢出的结果,那后面打印的结果是如何得来的呢?如图:
首先在方法区的静态区里会创建一个B的对象b且是全局的,然后主函数进栈再创建一个同名的对象b,此时两个对象的地址和存储位置是不同的,b.b是用主类中的对象去调用其他地方的对象,查找三部曲:1.该对象会先去它所在的函数里面找有没有要调用的东西,如果没有的话;2.去堆内存找相应的要调用的东西,如果还没有;3,就去该项目下的静态方法区中查找,如果还没有的话那就是真的没有了。
所以这里先在自己所在的函数中找除了自己叫b的其他叫b的对象,没有就去堆内存中找b的对象,还是没有就去静态区找,此时找到了并拿到了该b的地址0x123,然后当前主类的b拿到的地址是0x234,b==b.b比较的是两个对象的地址,不同则结果为false;而b.b==b.b.b和b.b.b.b.b.b.b==b.b.b.b.b.b.b.b.b.b.b.b.b两边所拿到的地址都是一样的都是0x123,因此都是true。
这就是静态和成员的特点,希望通过这两个例子,能对你理解静态变量和函数的加载有更好的帮助。