1. 自动初始化
变量使用之前进行初始化是良好的编程习惯。和C/C++相比,Java提供了完善的初始化机制,对于类的成员变量如果没有手动进行初始化,Java会进行默认初始化,C++不会做这种事情:
public class InitializeDefault {
byte a;
short b;
int c;
long d;
float e;
double f;
char g;
boolean h;
public static void main(String[] args) {
InitializeDefault t = new InitializeDefault();
System.out.println("byte = " + t.a + "\n"
+ "short = " + t.b + "\n"
+ "int = " + t.c + "\n"
+ "long = " + t.d + "\n"
+ "float = " + t.e + "\n"
+ "double = " + t.f + "\n"
+ "char = " + (int)t.g + "\n"
+ "boolean = " + t.h + "\n");
}
}
以上代码的输出为:
byte = 0
short = 0
int = 0
long = 0
float = 0.0
double = 0.0
char = 0
boolean = false
所有变量都被设置成默认值了。对于函数内部的局部变量,和C/C++相同,Java不会自动对它们进行初始化,但是Java会提示编译时错误。
对于自动进行的初始化操作,总是在用户主动初始化(如构造函数)之前发生,这是显然的。
2. 手动初始化
成员变量手动初始化有3种方式:定义变量时直接初始化、初始化块、构造函数。直接初始化、初始化块在构造函数调用之前执行。
public class InitializeManual {
InitializeManual(int a, short b) {
this.a = a;
this.b = b;
System.out.println("constructor called");
}
// 定义时直接初始化
int a = f1();
short b = 99;
int f1() {
System.out.println("func f1 called");
return 99;
}
// 初始化块
{
System.out.println("initialization block called");
a = 50;
}
public static void main(String[] args) {
InitializeManual t = new InitializeManual(1, (short) 2);
System.out.println(t.a + " " + t.b);
}
}
输出结果为:
func f1 called
initialization block called
constructor called
1 2
变量a调用函数f1()进行初始化,从结果可见定义变量时直接赋值、初始化块先于构造函数执行。直接赋值、初始化块的进行顺序取决于在类内的位置,从上向下依次执行,上例中成员变量a直接赋值操作在初始化块之前,所以先调用f()函数。
3. 静态变量
静态变量对于一个类只有一个实例,与C/C++中的全局变量很像。静态变量的初始化先于非静态变量进行。虚拟机加载一个类时,静态变量就开始初始化了。什么时候一个类要被加载呢?有多种情况:(1)定义了一个类的对象;(2)访问类的静态数据或函数。还有其他几种使用到类的情况都会触发加载操作。加载只进行一次,静态变量也只初始化一次。
public class InitializeStatic {
static int a;
int b;
// 构造函数
InitializeStatic() {
System.out.println("construct called");
a = 10;
b = 20;
}
// 初始化块
{
System.out.println("non static init block");
b = 2;
}
// 静态初始化块
static {
System.out.println("static init block");
a = 1;
c = 2;
}
// 直接赋值
static int c = f();
static int f() {
System.out.println("static func f called");
return 99;
}
public static void main(String[] args) {
InitializeStatic t = new InitializeStatic();
System.out.println("a = " + t.a + " b = " + t.b + " c = " + t.c);
}
}
以上代码的输出为:
static init block
static func f called
non static init block
construct called
a = 10 b = 20 c = 99
代码中new了一个InitializeStatic对象,先进行静态成员的初始化,然后进行非静态成员的初始化,最后进行构造函数。静态变量可以在定义时初始化或在静态初始化块中初始化,这两种代码的初始化顺序取决于在类内部的声明顺序,由上而下执行,和非静态变量的初始化顺序类似。
如果在main函数中只访问InitializeStatic的静态成员变量,不创建对象,即把InitializeStatic的main函数改成如下形式:
public static void main(String[] args) {
System.out.println("InitializeStatic.a = " + InitializeStatic.a);
}
则InitializeStatic的输出为:
static init block
static func f called
InitializeStatic.a = 1
这时只进行了静态成员变量的初始化操作,非静态变量还没有初始化。
4. 继承初始化
继承时的初始化比较复杂。所有静态变量的初始化先于非静态变量的初始化,即先进行基类和派生类的静态初始化,然后进行基类初始化,最后进行派生类初始化。基类和派生类静态变量都有直接初始化和初始化块两种方式,初始化顺序按声明顺序,见上文第3点。基类初始化和派生类初始化见第1、2点。class Base {
{
System.out.println("base init block");
}
static {
System.out.println("base static init block");
}
Base() {
System.out.println("base constructor");
}
}
class Derive extends Base {
{
System.out.println("derive init block");
}
static {
System.out.println("derive static init block");
}
Derive() {
System.out.println("derive constructor");
}
}
public class InitializeInherit {
public static void main(String[] args) {
Derive d = new Derive();
}
}
以上代码输出为:
base static init block
derive static init block
base init block
base constructor
derive init block
derive constructor