类的初始化:JVM只会在 “首次主动使用” 一个类/接口时,才会初始化它们。
主动使用
通过 new 构造类的实例
package init;
class Test{
static {
System.out.println("test static ...");
}
}
public class run {
public static void main(String[] args) {
new Test(); //首次主动使用
new Test();
}
}
static 代码块会在类初始化时自动加载,如果以上代码输出了 test1 static ...
说明 test1
类被初始化了。
以上代码 new 了两次 Test1
对象,但是只打印了一次,因为只有在首次使用时,才会初始化该类。
访问类/接口的静态成员(属性,方法)
package init;
class Test{
static {
System.out.println("test static ...");
}
static int i = 10;
static void testMethod() {
System.err.println("test static testMethod .....");
}
}
public class run {
public static void main(String[] args) {
// Test.i = 1;
// System.err.println(Test.i);
Test.testMethod();
}
}
特殊情况
-
如果成员变量即是
static
又是final
, 即常量,则不会被初始化! -
上一种情况中,如果常量的值是一个 随机值,则又会被初始化。
package init; class Test{ static final int i = (int)(Math.random() * 1000); static { System.err.println("test static ......"); } } public class run { public static void main(String[] args) { System.err.println(Test.i); } }
反射使用的类
package init;
class Test{
static {
System.out.println("test static ...");
}
}
public class run {
public static void main(String[] args) throws Exception {
Class.forName("init.Test");
new Test().getClass();
/** 上面两种可以初始化,下面的不行 */
Test.class.getClass();
}
}
初始化一个子类时,该子类的父类也会被初始化
package init;
public class Father {
static {
System.err.println("father static .....");
}
}
package init;
public class son extends Father{
static {
System.err.println("son static ......");
}
}
package init;
public class run {
public static void main(String[] args){
new son();
}
}
结果:
father static .....
son static ......
动态语言
动态语言在执行过程中所涉及的类,也会被初始化(动态代理)
被动使用
除了主动使用以外,其他都是被动使用
package init;
class Test{
static {
System.err.println("test static ......");
}
}
public class Test4 {
public static void main(String[] args) {
Test[] t = new Test[4];
}
}
通过数组定义引用类,为类的被动使用,不会触发该类的初始化。
探究
为什么调用静态常量不会初始化类
class Test {
/**
* final static 称为常量
* 常量产生的时机:
* 时间: 编译期间
* 地点: (调用这个常量的方法 所在类[Run]的常量池)常量池
*/
final static int i = 10;
static {
System.out.println("test static .....");
}
}
/**
* Run的常量池中保存了Test中产生的常量[i = 10]
*/
public class Run {
public static void main(String[] args) {
System.err.println(Test.i);
}
}
通过查看 Run 类的 class 文件后发现,输入语句直接输出了10,与 Test 中的 10 没有任何关系。
public class Run {
public Run() {
}
public static void main(String[] args) {
System.err.println(10);
}
}