1. 看下面的代码,输出什么呢?
public class Test
{
public static void main(String[] args)
{
System.out.println(NN.a);
System.out.println(MM.b);
}
}
class NN
{
public static final int a = 9;
static
{
System.out.println("init NN");
}
}
class MM
{
public static final int b = (int)(Math.random() * 100);
static
{
System.out.println("init MM");
}
}
输出为:
9
init MM
21
解释一下:
1. 对于final类型的静态变量,如果在编译时就能计算出变量的取值,那么这种变量被看作是编译时的常量。Java程序中对类的编译时常量的使用,被看作是对类的被动使用,不会导致类的初始化。
2. 对于final类型的静态变量,如果在编译时不能计算出变量的取值,那么Java程序中这种变量的使用,被看作是对类的主动使用,会导致类的初始化。
2. 下面的代码,又会输出啥呢?
public class Test
{
public static void main(String[] args)
{
System.out.println(Sub.a);
Sub.method();
}
}
class Base
{
static int a = 1;
static
{
System.out.println("init Base");
}
static void method()
{
System.out.println("method of Base");
}
}
class Sub extends Base
{
static
{
System.out.println("init Sub");
}
}
输出为:
init Base
1
method of Base
解释下:
只有当出现访问的静态变量或静态方法的确在当前类或接口中定义时,才可以看作是对类的主动使用。
3. 下面的代码又输出什么呢?
public class Test
{
public static void main(String[] args)
{
ClassLoader loader = ClassLoader.getSystemClassLoader();
try
{
loader.loadClass("Base");
System.out.println("******");
Class.forName("Base");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public class Base
{
static
{
System.out.println("now init Base");
}
}
分别编译后,运行Test,输出为:
******
now init Base
解释下:
调用ClassLoader类的loadClass()方法加载一个类,不会导致类的初始化;而调用Class类的静态方法forName()时,会导致类的初始化。
4. 再看下面的代码。
public class Test
{
static
{
System.out.println("init Test");
}
public static void main(String[] args)
{
System.out.println(Sub.b);
}
}
class Base
{
static int a = 1;
static
{
System.out.println("init Base");
}
}
class Sub extends Base
{
static int b = 1;
static
{
System.out.println("init Sub");
}
}
输出为:
init Test
init Base
init Sub
1
5. 接着看
public class Test
{
public static void main(String[] args)
{
Base base;
System.out.println("***");
base = new Base();
System.out.println("******");
System.out.println(Sub.b);
}
}
class Base
{
static int a = 1;
static
{
System.out.println("init Base");
}
}
class Sub extends Base
{
static int b = 1;
static
{
System.out.println("init Sub");
}
}
输出为:
***
init Base
******
init Sub
1
Java虚拟机初始化一个类包含以下步骤:
1. 如果这个类还没有被加载和连接,就先进行加载和连接。
2. 如果这个类存在直接的父类,并且这个父类还没有被初始化,就先初始化这个直接父类。
3. 如果类中存在初始化语句,那就依次执行这些初始化语句。
当初始化一个类的直接父类时,也需要重复以上步骤,这会确保当程序主动使用一个类时,这个类及所有父类(包括直接父类和间接父类)都已经被初始化。