Java类初始化、变量初始化
摘自:https://blog.csdn.net/mocas_wang/article/details/109956014
基础知识
- Java常量, final 修饰,值被设定后不能再被修改
- 静态变量里, static 修饰,顾名思义,无须创建对象,便可在内存中申请一个存储空间进行存储
- 成员变量, 也称实例变量,它随着当前对象的建立而建立,随着对象的销毁而销毁,存在于对象所在的堆内存中
- 构造器,创建class对象时执行
- 静态初始化块 ,执行优先级高于非静态的初始化块,它会在对象装载到 jvm的时候执行一次,执行完成便销毁,只能初始化 static 修饰的变量
- 非静态初始化块,执行的时候如果有静态初始化块,先执行静态初始化块再执行非静态初始化块,在每个对象生成时都会被执行一次,它可以初始化类的实例变量。但它会在构造函数执行之前被执行。
类的初始化顺序
Java中类初始化顺序,依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器。。
变量的初始化问题
类成员变量初始化
对于类的成员变量。不管程序有没有显示的初始化,Java 虚拟机都会先自动给它初始化为默认值。
规则为:
1、整数类型(byte、short、int、long)的基本类型变量的默认值为0。
2、单精度浮点型(float)的基本类型变量的默认值为0.0f。
3、双精度浮点型(double)的基本类型变量的默认值为0.0d。
4、字符型(char)的基本类型变量的默认为 “/u0000”。
5、布尔性的基本类型变量的默认值为 false。
6、引用类型的变量是默认值为 null。
7、数组引用类型的变量的默认值为 null。春关键数组变量的实例后,如果没有没有显示的为每个元素赋值,Java 就会把该数组的所有元素初始化为其相应类型的默认值。
局部变量初始化
局部变量声明以后,Java 虚拟机不会自动的为它初始化为默认值。因此对于局部变量,必须先经过显示的初始化,才能使用它。
如果编译器确认一个局部变量在使用之前可能没有被初始化,编译器将报错。
那么,加了修饰的java成员变量是如何初始化的呢?在何时?在程序中的什么位置?
public class TestC {
/*
* 定义成员变量
* 尾数为1表示定义时进行初始化赋值
* 尾数为2表示在代码块中进行初始化赋值
* 尾数为3表示在构造函数中进行初始化赋值
* 尾数为4表示在静态代码块中进行初始化赋值
* 尾数为5表示不初始化赋值
*/
/*
* 普通成员变量
*/
int field_a1 = 5;
int field_a2;
int field_a3;
//报错:不能再静态代码块中使用非静态变量。 Cannot make a static reference to the non-static field field_a4
//int field_a4;
int field_a5;
/*
* final 成员变量
*/
final int field_b1 = 5;
final int field_b2;
final int field_b3;
//报错:不能再静态代码块中使用非静态变量。Cannot make a static reference to the non-static field field_b4
//final int field_b4;
//报错:未初始化 。The blank final field field_b5 may not have been initialized
//final int field_b5;
/*
* static成员变量
*/
static int field_c1 = 5;
static int field_c2;
static int field_c3;
static int field_c4;
static int field_c5;
/*
* static final 成员变量
*/
static final int field_d1 = 5;
//报错:未初始化 。The blank final field field_d2 may not have been initialized
//static final int field_d2;
//报错:未初始化 。The blank final field field_d3 may not have been initialized
//static final int field_d3;
static final int field_d4;
//报错:未初始化 。The blank final field field_d5 may not have been initialized
//static final int field_d5;
//代码块
{
field_a2 = 5;
field_b2 = 5;
field_c2 = 5;
//field_d2 = 5;
}
//静态代码块
static{
//field_a4 = 5;
//field_b4 = 5;
field_c4 = 5;
field_d4 = 5;
}
//构造函数
public TestC(){
field_a3 = 5;
field_b3 = 5;
field_c3 = 5;
//field_d3 = 5;
}
}
然后我们对这个程序生成的.class文件进行反编译,看看他是如何运行的。
下面是TestC.jad文件。反编译文件
public class TestC
{
public TestC()
{
field_a1 = 5;
field_a2 = 5;
field_c2 = 5;
field_a3 = 5;
field_c3 = 5;
}
int field_a1;
int field_a2;
int field_a3;
int field_a5;
final int field_b1 = 5;
final int field_b2 = 5;
final int field_b3 = 5;
static int field_c1 = 5;
static int field_c2;
static int field_c3;
static int field_c4 = 5;
static int field_c5;
static final int field_d1 = 5;
static final int field_d4 = 5;
}
- 对于不加修饰的普通成员变量,无论我们在什么地方对其进行初始化赋值,系统都会默认在构造函数中进行赋值。
- 对于final变量,无论我们在什么地方进行赋值,系统会默认final变量是在类中进行初始化。
- 对于static,系统会根据我们的需求,而在不同位置进行初始化。
通过报错,我们可以发现。
- final变量必须进行初始化。否则就会报编译错误。The blank final field field_d5 may not have been initialized
- static成员变量的初始化发生在类被类加载器(classLoader)加载的时候系统会对没有初始化的静态成员变量在静态区进行默认赋值。
- 普通成员变量的初始化发生在JVM为类生成实例开辟空间的时候进行默认初始化赋值。
总结
final修饰的变量必须显性进行初始化。初始化有三种方式:
-
直接初始化
-
动态代码块
-
构造函数
- 如果一个变量既被final修饰又被static修饰,那么这个变量一定要被初始化(满足final特性),另外要么直接初始化要么在静态代码块中进行初始化(满足static特性)。
- final修饰的变量的初始化先于static修饰的变量,static修饰的变量先于普通变量的初始化。
- 如果是final级别的,那么虽然也是根据代码次序的前后顺序进行初始化,但是它是在编译阶段进行宏替换,即在编译阶段就知道了值,因此在加载运行阶段对这一先后顺序完全感知不到。
- 但是如果是static级别的,代码的先后顺序会造成程序运行结果的不同。因为它们都是在加载进行的时候进行初始化的。
归结如下:
- final修饰的变量在编译阶段完成
- static修饰的变量在一个静态代码块中进行初始化
- 普通变量在构造函数中进行初始化