💡对象实例化
当使用实例化对象new Object()
时,堆栈都经历了什么?
public class main{
public static void main(String[] args) {
A a = new A();
}
}
class A{
String name;// 全局变量
static {// 静态代码块
System.out.println("hjhcos");
}
{// 普通代码块
this.changeName("hjh");
}
A(){// 构造器
this.changeName("cos");
}
public void changeName(String name){// 成员方法
this.name = name;
}
}
通过调试上面的代码,可以简单地了解在对象实例化时堆栈的相应操作。
当执行到 A a = new A();
时先执行 A()
将 A.<clinit>
压栈,执行类里面的静态代码块然后出栈,这个过程称为类加载。在这个过程中,你不可以使用 this
,因为它还没有在堆里面分配内存(即没有实例化)。
接下来执行 new A()
将 A.<init>()
压栈,在压栈后就已经完成了 类的全局变量赋于默认值 和 this
指向相应堆栈 的操作,因此从意义上来说已经完成了对象实例化(即对象实例在堆里面分配了内存)。为什么我会说它已经完成了对象实例化?
因为这个时候我们可以通过 this
来引用它。
那是不是上面操作完成后可以直接返回对象了?
其实还没有,普通代码块和构造器还没有执行。你可以理解为 A.<init>()
还没有执行完(即没有做出栈操作),因为在 A.<init>()
里面还有执行所有普通代码块和调用对应的一个构造器。
首先它会去执行完所有普通代码块(自上而下地执行每一个普通代码块),你可以在普通代码块里面调用普通方法,但是不可以调用构造器。
然后构造器开始被调用,你可以在构造器的第一行调用其他本类的构造器 通过 this();
传实参的方式调用,也可以当前作用域里面调用普通方法。不管是在哪个里面调用普通方法都会在堆栈里面进行压栈出栈操作的。
最后返回这一系列操作后的对象,可以被标识符 a
引用。
通过上面的描述,总结一下
- 执行到类中静态代码块时,对象还没有实例化(即没有
this
)。 - 执行到普通代码块和构造器,对象已经实例化且全局变量有了默认值(即可以使用
this
)。 - 普通代码块执行在构造器前面。
- 普通代码块和构造器都可以调用普通方法。
- 构造器的第一行代码可以用于调用相同类的其他构造器。
- 先加载类信息(属性和方法,只会加载一次),再在堆中分配空间属性会进行默认初始化,最后把地址赋值给标识符。
- 数据存储在方法区的常量池里面,其地址会引用到堆里面。
💡对象实例化后不再引用
通过上面的描述我们了解到,标识符只是对这个对象进行引用。因此当你对标识符重新赋值并不会改变之前引用的对象,那么问题来了不再被引用的对象,那么它何去何从?
当实例化后的对象不在引用a = null;
时,JVM会在堆和方法区里面回收它所占的资源, 但是需要注意的是a
标识符的类型声明还在方法区里面没有被JVM回收至于什么时候回收?暂时我还不知道,但可以肯定的是作用域消失里面的标识符的类型信息也会相应消失。
💡构造器重新初始对象
在构造器重新初始化本身,会不会调用普通代码块?
class A{
String name;
{// 普通代码块
System.out.println("hjhcos");
}
A(){// 构造器
this("hjh");
System.out.println("cos");
}
A(String name){// 构造器
this.name = name;
System.out.println(name);
}
}
public class main{
public static void main(String[] args) {
A a = new A();
a = null;
}
}
通过调试上面的代码,我发现一个特别好玩的事情 —— 就是不会先去执行普通代码块,而是先去执行 this("hjh");
然后跳到 A(String name)
构造器再去看是否有调用其他构造器,如果没有调用那么直接去执行所有普通代码块。放心你是无法进行递归调用的,因为那样做是无法通过编译器的。
构造器递归调用:在构造器里面调用已经调用过的构造器。
如果你是无意刷到这篇文章并看到这里,希望你给我的文章来一个赞赞👍👍。如果你不同意其中的内容或有什么问题都可以在下方评论区留下你的想法或疑惑,谢谢你的支持!!😀😀