欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

在什么情况下调用类中static final修饰的属性时不需要初始化类

程序员文章站 2022-05-21 21:04:26
...

被final修饰静态字段在操作使用时,不会使类进行初始化,因为在编译期已经将此常量放在常量池。
测试:

class ClassLoadTime{
  static{
    System.out.println("ClassLoadTime类初始化时就会被执行!");
  }
  public static final int MIN = 10; (防止测试类和此类不在一个包,使用public修饰符)
}
 
class ClassLoadDemo{
  public static void main(String[] args){
   System.out.println(ClassLoadTime.MIN);
  }
}
由于编译器编译优化,导致该变量在常量池中,当你用ClassLoadTime.MIN时,只会到常量池中查该数值,不会加载ClassLoaderTime类。

输出:

10
————————————————
版权声明:本文为CSDN博主「冰凌其」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiao1_1bing/article/details/81139926

在看到上述文章的这段内容时有一个疑问,如果我访问的属性类型是一个引用类型的话也不会初始化 ClassLoaderTime类吗?于是用一下代码测试了一下:

public class HungrySingletonPattern {
	static{
		System.out.println("HungrySingletonPattern类初始化时就会被执行!");
	}
	
	public static final HungrySingletonPattern instance = new HungrySingletonPattern();
	public static final String str = "调我不用初始化类!";
	
	private HungrySingletonPattern() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	public static final HungrySingletonPattern getInstance(){
		return instance;
	}
}

public class Test {
	public static void main(String[] args) throws SecurityException, NoSuchFieldException {
		HungrySingletonPattern i = HungrySingletonPattern.instance;
//		String s = HungrySingletonPattern.str;
	}
}

运行结果如下:

HungrySingletonPattern类初始化时就会被执行!

说明这个HungrySingletonPattern类被初始化了!为什么上边的例子不会初始化类,在把类中属性换成引用类型时类就会被初始化呢?上边的例子说是编译器的优化导致的,那么我们就来看看两段代码生成的字节码有什么区别吧:

1. main函数中调用HungrySingletonPattern.str的情况:

 Code:
   stack=1, locals=2, args_size=1
      0: ldc           #21                 // String 调我不用初始化类!
      2: astore_1
      3: return

2. main函数中调用HungrySingletonPattern.instance的情况:

    Code:
      stack=1, locals=2, args_size=1
         0: getstatic     #21                 // Field cn/edu/xidian/singleton/h
ungry/HungrySingletonPattern.instance:Lcn/edu/xidian/singleton/hungry/HungrySing
letonPattern;
         3: astore_1
         4: return

我们发现上述两个例子中生成的字节码唯一的区别就是1中生成的指令是ldc指令,而2中生成的指令是getstatic指令。那么这两个指令都是什么含义呢?

我们通过查询java虚拟机规范发现指令的解释如下:

0xb2   getstatic   获取指定类的静态域,并将其值压入栈顶。
0x12 ldc 将 int,float 或 String 型常量值从常量池中推送至栈顶。

所以我们可以大胆的推测:当类中的基本类型或String类型在被static final修饰时,编译器会将指令直接优化成ldc指令,从而在只获取该属性的值时,不需要初始化它所在的类;而当被static final修饰的属性为引用类型时,由于编译器无法直接将其转化为常量,故在获取该类型的属性时还是需要初始化类的。