一个类成员变量的空间是在类加载的时候就已经分配好了,并且默认初始化了(只有局部变量是没有默认初始化的)。
java成员分两种,类成员和实例成员。成员初始化可以在声明时就初始化,但是其缺点是对于比较复杂的初始化稍显不足。不过也有几种代替方法。
静态语句块初始化
一个静态语句块就是一个被包括在{}中的正常的语句块,语句块前面有一个static关键字
示例:
static {
//初始化静态变量的代码
}
一个类会有任意数量的静态语句块,且它们能出现在类体的任何位置。运行时系统可以确保静态语句块按照其在源代码种出现的顺序被调用。
此处静态语句块有一个替代方法-私有静态方法:
class Whatever {
public static varType myVar = initializeClassVariable();
private static varType initializeClassVariable() {
// initialization code goes here
}
}
private static方法的优点是,如果稍后还需要重新初始化,该方法可以被重用。
实例变量初始化语句块
通常,会将实例变量的初始化的代码放到构造函数内。此处同样有两个替代方法:初始化语句块和final方法。实例变量的初始化语句块和静态初始化语句块类似,只是少了static关键字
{
// whatever code is needed for initialization goes here
}
java编译器拷贝初始化语句到每一个构造函数中。所以该方法可以被用于共享初始化代码在多个构造函数中。
一个final方法不能被重载在子类中
此处是一个final方法用于初始化实例变量
class Whatever {
private varType myVar = initializeInstanceVariable();
protected final varType initializeInstanceVariable() {
// 初始化代码
}
}
注意这里的初始化方法被声明为protected final被声明为final的原因是这样的:如果允许子类覆盖,那么在父类初始化就会调用子类重写后的方法,这样就违背了初始化逻辑。
下面来一个面试题:
问主函数的输出?
package com.company;
public class Father {
static int aSVal = getVal();
static int bSVal = 10;
int aVal = getIntValFather();
int bVal = 10;
static {
System.out.println("father静态----语句块!");
bSVal = 20;
}
{
System.out.println("father非静态----语句块!");
bVal = 30;
}
public Father() {
System.out.println("father构造函数!");
}
private static int getVal() {
System.out.println("father静态----赋值函数!");
return 10;
}
protected final int getIntValFather() {
System.out.println("father非静态----赋值函数!");
return 100;
}
package com.company;
public class Son extends Father {
static int aSVal = getValSon();
static int bSVal = 10;
int aVal = getIntValSon();
int bVal = 10;
static {
System.out.println("son静态----语句块!");
bSVal = 20;
}
{
System.out.println("son非静态----语句块!");
bVal = 30;
}
public Son() {
System.out.println("Son构造函数!");
}
private static int getValSon() {
System.out.println("son静态----赋值函数!");
return 10;
}
protected final int getIntValSon() {
System.out.println("son非静态----赋值函数!");
return 100;
}
}
package com.company;
public class Main {
public static void main(String[] args) {
// write your code here
new Son();
System.out.println("********************华丽的分割线****************");
new Son();
}
}
father静态----赋值函数!
father静态----语句块!
son静态----赋值函数!
son静态----语句块!
father非静态----赋值函数!
father非静态----语句块!
father构造函数!
son非静态----赋值函数!
son非静态----语句块!
Son构造函数!
********************华丽的分割线****************
father非静态----赋值函数!
father非静态----语句块!
father构造函数!
son非静态----赋值函数!
son非静态----语句块!
Son构造函数!
Process finished with exit code 0
总结:从这个题可以看出,对象创建过程中,第三步的初始化里面的具体顺序是怎么一回事。
注意静态初始化和静态语句块是在类第一次被调用的时候就使用了,而不一定必须是生成对象的时候。
将题目改为如下输出,就可以证明上述描述。
package com.company;
public class Main {
public static void main(String[] args) {
// write your code here
int val = Son.bSVal;
System.out.println("********************华丽的分割线****************");
new Son();
}
}
father静态----赋值函数!
father静态----语句块!
son静态----赋值函数!
son静态----语句块!
********************华丽的分割线****************
father非静态----赋值函数!
father非静态----语句块!
father构造函数!
son非静态----赋值函数!
son非静态----语句块!
Son构造函数!