1. java中变量初始化为默认值的时机
口诀: 成员变量自动初始化,方法变量手动初始化! 成员变量堆内存/方法区,方法变量栈内存/方法区!Java中,对于方法变量或者方法常量如果不进行变量或者常量初始化操作(该变量、常量可以是基本变量、基本常量;或者引用变量、引用常量),编译将会出错。
变量的默认值只针对于成员变量和成员常量,成员变量、成员常量(无论是基本类型,还是引用类型)如果没有指定初始化值,则根据不同的修饰按照不同时机初始化为默认值(同样下面的实例化时机也针对于方法变量和方法常量):
- 如果该成员变量是常量且是静态的话,即通过static,final修饰,则在编译成.class文件时会将该常量初始化为默认值保存在.class文件中,此时class文件中保存常量的地方叫做class常量池,class常量池用于存放编译器生成的各种字面量和符号引用,每个class文件都有一个class常量池。之后随着*.class文件一同被加载进JVM方法区内存的运行时常量池,然后再将运行时常量池中的静态常量复制到JVM方法区内存的静态区中,这个过程在“装载、链接、初始化”的链接阶段的准备阶段为静态成员初始化为对应的运行时常量池中的常量值。
- 如果该成员变量是常量的话,即通过final修饰,则在编译成.class文件时会将该常量初始化为默认值保存在.class文件的class常量池中,之后随着*.class文件一同被加载进JVM方法区内存的运行时常量池,然后在“装载、链接、初始化”的初始化阶段随着对象的实例化将其复制到对象的堆内存中保存。
- 如果该成员变量是静态成员的话,即通过static修饰,则在“装载、链接、初始化”的链接阶段的准备阶段为静态成员初始化为默认值,这个值保存在JVM方法区内存的静态区中;
- 如果该成员变量是实例成员的话,则在“装载、链接、初始化”的初始化阶段随着对象的实例化被初始化为默认值,这个值保存在JVM的堆内存中。
2. 实例化和初始化的区别
2.1 实例化
有了面向对象的思想后,才有了实例化的概念。
实例化一定是伴随着new,目标一定是对象,一定是在堆中为目标对象实体开辟堆内存。
不论是通过clone,反射还是反序列化,本质都是通过构造方法去实例化对象的。
比如new Person();就是实例化,实例化的对象是new Person这块堆内存空间。
如:
new Person("主类中的静态成员变量", 100);
2.2 初始化
只要给任何一个变量赋值,就叫做初始化。
初始化就是赋值操作,首次赋值叫做初始化,后续赋值叫做重置。
初始化意味着同一个变量的值可以被不断重置为新值,即变量本身存储的内容是可以重新设置的。
比如Person per = new Person();就是初始化,初始化后的变量是per。
如:
// 对变量per进行初始化,它的变量值是new Person()的堆内存地址
private static Person per = new Person("主类中的静态成员变量", 100);
2.3 两者关系
- 初始化一个变量不一定包括实例化;如下:
// 这条语句只是单纯的对变量a进行赋值操作,即对a进行初始化。 // 这条语句不包含实例化操作。 int a = 10;
- 实例化一个对象同样不一定包括初始化;如下:
// 这条语句只是实例化一个person的匿名对象,不包含任何初始化操作。 new Person();
2.4 案例
class A{
//主动引用触发A的加载,此时a在加载、链接、初始化的初始化阶段被初始化为默认值0;存在A对象的堆中。
private int a;
//同上,此时a被初始化为100,注意基本类型不涉及实例化。
private int b = 100;
//同上,此时per1被初始化为null,存在A对象的堆中,Person对象没有实例化。
private Person per1;
//同上,此时per2被初始化为指向Person堆中的实体地址,存放在A对象的堆中;new Person()操作在堆中新开辟空间保存对象实体。
private Person per2 = new Person();
}
2.5 总结
Person per = new Person();
对于变量per而言是对per的初始化,即赋给per变量一个值,这个值是new Person()实例化后的堆内存地址,这个赋值操作被称为初始化;
new Person()主动引用目标类,开始实例化目标对象,在堆中开辟空间保存对象实体,new 构造方法()返回的堆内存中对象实体的内存地址,这个new操作被称为实例化。
文档信息
- 本文作者:Marshall