系列文描述:
该文章是日常笔记系列的第二篇第三节,记录了类字面常量的特殊性,相信看完肯定有所收获。
关于类字面常量
描述:
最近接触了比较多的反射,了解到类字面常量特殊的地方,故做笔录,同时也和大家分享下类字面常量特殊在哪里。所谓的类字面常量指的是A.class,这是java提供的生成对Class对象的引用。关于类的概念我们都很熟悉,关于类的使用JDK大致为我们做了三步操作,分别是
- 加载,这是由类加载器执行的,用白话来描述就是查找这个类的字节码,然后构建一个Class对象。
- 链接,在这个阶段会先校验类的字节码,并且为类对象的静态域分配好内存空间。
- 初始化,首先会先初始化类的超类(前提是有超类),以及执行静态初始化器和静态初始化块。
了解了三步还不够,我们还要知道jdk其实超级无敌懒的,对类的初始化这一步会延迟到对静态方法或者“非”常静态域进行首次引用时才执行。这里的常静态域指的是被static final修饰的常量,该常量不需要对应类初始化就可以被读取。为了更加清晰的了解到这个过程,可以看以下demo。
代码演示:
package classLoader;import java.util.Random;class Initable1 { static final int staticFinal = 47; static final int staticFinal2 = (int) (Math.random() * 1000); static { System.out.println("Initializing Initable1"); }}class Initable2 { static int staticNonFinal = 147; static { System.out.println("Initializing Initable2"); }}class Initable3 { static int staticNonFinal = 74; static { System.out.println("Initializing Initable3"); }}public class ClassInitialization { public static Random random = new Random(47); public static void main(String[] args) throws ClassNotFoundException { Class initable1 = Initable1.class; System.out.println("After creating Initable1 ref"); System.out.println(Initable1.staticFinal); System.out.println(Initable1.staticFinal2); System.out.println(Initable2.staticNonFinal); Class initable3 = Class.forName("classLoader.Initable3"); System.out.println("After creating Initable3 ref"); System.out.println(Initable3.staticNonFinal); }}复制代码
看具体运行结果前,看官先自己用草稿写下输出答案哈,这样比较有效果。 运行结果如下:
After creating INitable ref47Initializing Initable1604Initializing Initable2147Initializing Initable3After creating Initable3 ref74复制代码
如果答案一样,那么可以直接忽略掉我接下来的解析了哈,因为分析很绕,主要是验证上面的理论!
正如答案中看到的那样,47 在 After creating INitable ref 后才输出,证明了 Class initable1 = Initable1.class 这一行代码运行的时候并没有立即初始化类,而 Initializing Initable1 在47后才输出也验证了另一个观点(47对应的Field staticFinal是被static final 修饰的常静态域),那就是常静态域不需要在类Initable1进行初始化就可以被读取。而在打印结果Initable1.staticFinal2(随机数)之前先打印出了Initable1静态代码块中的输出,意味着直到这一步才真正初始化了类Initable1,于是先执行了静态代码块再输出了Initable1.staticFinal2,同样,对Initable2.staticNonFinal的输出先打印了Initable2静态代码块中的Initializing Initable2也是同样的原因。而在最后使用Class.forName("classLoader.Initable3")取得Initable3类的引用的时候直接打印了代码块中的字符串可以看出,使用Class.forName取得类的引用的时候是立即初始化类的。
结尾说点什么
说好的一周一篇,上个周末沉迷docker的使用导致废了,然后最近又是每天都是差不多十二点下班,所以只能下班后花时间写总结最近的笔记了(  ̄▽ ̄)((≧︶≦)
系列博客可以关注公众号:
个人网站: