目录
引言
要明白Java中变量的初始化,那么首先需要知道Java中存在哪些变量。一般来说,在Java中存在2种变量,一种是类的成员变量,另一种则是定义在函数(方法)里的局部变量。正如Java编程思想中所述,Java保证所有变量在使用前一定被初始化。对于类的成员变量,即使我们不进行初始化,Java也会进行默认初始化。而对于方法中的局部变量,Java以编译时错误的形式贯彻这种保证。
默认初始化
Java中基本数据类型的初始化,如下表所展示的,而对于引用类型,则会初始化为null。
基本数据类型 | 默认值 |
boolean | false |
char | ‘\u0000’(null) |
byte | (byte)0 |
short | (short)0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
相关概念
1.静态变量
使用static修饰的成员变量。
static People p1 = new People();
static int count;
静态变量属于类,可直接通过类名进行调用,也可使用引用(对象名)进行调用。但是静态变量只在类初次加载到JVM时才会进行初始化,并且只初始化一次,无论之后创建了该类的对象有多少个,初始化动作都只做一次,静态代码块也同样如此。
类初次加载到JVM
那么什么叫做类初次加载到JVM呢?这里分2种情况,1)首次生成该类的对象;2)使用到该类的静态数据成员,而此时并未生成该类的对象。这一点与实例变量有所不同,实例变量的初始化在创建该类的对象才会进行。并且每次创建一个对象,这些实例变量都会进行初始化。
2.静态代码块
使用static{ ...//代码}进行表示。
3.实例变量
即没有使用static修饰的成员变量,它属于实例(对象),每次创建对象都会生成该实例变量,这与静态初始化动作(静态变量及静态代码块)是有区别的。
4.非静态代码块
使用{...//代码}进行表示。
代码示例
package com.thinkinginjava.chapter05;
public class ExplicitStatic
{
public static void main(String[] args) {
System.out.println("Inside main()");
Cups cups1 = new Cups();
}
}
class Cup1
{
Cup1(int marker) {
System.out.println("Cup1("+marker+")");
}
void f(int marker) {
System.out.println("f("+marker+")");
}
}
// Cup2的定义
class Cup2
{
Cup2(int marker){
System.out.println("Cup2("+marker+")");
}
void f2(int marker) {
System.out.println("f2("+marker+")");
}
}
class Cup3
{
Cup3(int marker){
System.out.println("Cup3("+marker+")");
}
}
class Cups
{// 用来测试静态代码块 ,静态变量,实例变量,非静态代码块之间的先后执行顺序
Cups(){ // 构造器
System.out.println("Cups() ");
}
Cup3 cup3_2 ;
{// 非静态代码块
cup3_2 = new Cup3(2);
}
Cup3 cup3_1 = new Cup3(1);
static Cup2 cup2_1;
static Cup2 cup2_2;
static { // 静态代码块
cup2_1 = new Cup2(1);
cup2_2 = new Cup2(2);
}
static Cup1 cup1 = new Cup1(1);
// static Cup1 cup1; // 也初始化了但是为null没有调用构造器看不出效果
static Cup1 cup2 = new Cup1(2);
}
变量初始化顺序
对于前者,即类的成员变量,当你只声明变量而没有赋值时,Java会对这类变量自动初始化,当然自动初始化也遵循着一套规则。
不存在继承关系
在没有继承的前提下,他们之间的初始化规则如下。
1)首先初始化由static修饰的代码,静态变量和静态代码块的初始化顺序是谁在前,谁先初始化;
2)实例变量和非静态代码块的初始化顺序也是谁在前谁先初始化;
3)而无论实例变量和非静态代码块是否定义在静态变量和静态代码块之前,都是静态变量和静态代码块先初始化,而后才是实例变量和非静态代码块,最后才是构造器的初始化。
从这里可以看出,类的成员变量一定在类的成员方法(包括构造器)之前得到初始化,无论成员变量的定义代码在构造器的前面还是后面,都是如此。
存在继承关系
那如果引入更复杂的基类和子类之间的继承关系,初始化顺序又该如何呢?
- 先初始化父类(基类)的静态代码;
- 初始化子类的静态代码;
- 初始化父类的非静态代码;
- 初始化父类的构造器;
- 初始化子类的非静态代码;
- 初始化子类的构造器.
父类的静态代码之所以先初始化,是因为子类的静态代码可能会依赖于父类成员能否被正确初始化。
函数内的局部变量
而对于函数内的局部变量,则需要编程人员自己显式地初始化,如果只声明变量而不进行初始化(也就是赋值),则会导致程序编译不过。