为了讲解类的主动引用以及被动引用,我们会根据笔试题来分析。

类的主动引用

先来讲解6道笔试题,弄清楚什么是类的主动引用?

笔试题1:

  1. public class Test1 {
  2. static{
  3. System.out.println("Test1 static");
  4. }
  5. }
  6. public class Main {
  7. public static void main(String[] args) {
  8. Test1 t=new Test1();
  9. }
  10. }

结果:
Test1 static
主动引用定义1:new一个对象的时候会发生类初始化,调用

笔试题2:

  1. public class Test2 {
  2. static final int count = 1;
  3. static{
  4. System.out.println("Test2 static");
  5. }
  6. }
  7. public class Main {
  8. public static void main(String[] args) {
  9. int n=Test2.count;
  10. }
  11. }

结果:
什么都没有
主动引用定义2:调用类中的静态成员,除了final字段,final被调用但是没有初始化类。
没有任何输出,就是因为那个final字段,Java编译器把这样的字段解析成对常量的本地拷贝。
常量是一种特殊的变量,因为编译器把他们当做值(value)而不是域(field)来对待。
如果你的代码中用到了常变量(constant variable),编译器并不会生成字节码来从对象中载入域的值,而是直接把这个值插入到字节码中。
这是一种很有用的优化,但是如果你需要改变final域的值,那么每一块用到的那个与的代码都需要重新编译。

笔试题3:

  1. public class Test3 {
  2. static void Output(){
  3. System.out.println("Output !");
  4. }
  5. static{
  6. System.out.println("Test3 static");
  7. }
  8. }
  9. public class Main {
  10. public static void main(String[] args) {
  11. Test3.Output();
  12. }
  13. }

输出的结果
Test3 static
Output !
主动引用定义3:调用某个类的静态方法,那么这个类一定先初始化。

笔试题4:

  1. public class Test4 {
  2. static final int count = 1;
  3. static{
  4. System.out.println("Test4 static");
  5. }
  6. }
  7. public class Main {
  8. public static void main(String[] args) {
  9. try {
  10. Class aClass = Class.forName("com.oyb.jvm.test01.lesson05.Test4");
  11. } catch (ClassNotFoundException e) {
  12. e.printStackTrace();
  13. }
  14. }

输出的结果:
Test4 static
主动引用定义4:反射调用,触发类初始化
使用java.lang.reflect包方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
通过调用java.lang.Class.forName(String className)

笔试题5:

  1. public class Father {
  2. static{
  3. System.out.println("Initialize class father");
  4. }
  5. }
  6. public class Son extends Father{
  7. static{
  8. System.out.println("Initialize class son");
  9. }
  10. }
  11. public class Main {
  12. public static void main(String[] args) {
  13. Son son=new Son();
  14. }
  15. }

输出结果:
Initialize class father
Initialize class son
类的主动引用定义5:先初始化父类。再初始化子类。
当初始化一个类的时候,如果发现父类没有进行初始化,则需要先触发其父类的初始化

笔试题6:

  1. public class Test6 {
  2. static {
  3. System.out.println("static init ...");
  4. }
  5. public static void main(String[] args) {
  6. System.out.println("main begin ...");
  7. }
  8. }

输出结果:
static init …
main begin …
类的主动引用定义6:当虚拟机启动时,用户需要制定一个执行的主类,虚拟机会首先初始化这个主类。

主动引用总结:

image.png
对类(B类)进行引用时,如果类没有进行过初始化,则先触发其初始化叫做主动引用。
主动引用6中定义:
主动引用定义1:new一个对象的时候会发生类初始化,调用
主动引用定义2:调用类中的静态成员,除了final字段,final被调用但是没有初始化类。
主动引用定义3:调用某个类的静态方法,那么这个类一定先初始化。
主动引用定义4:反射调用,触发类初始化。
主动引用定义5:先初始化父类。再初始化子类。
主动引用定义6:当虚拟机启动时,用户需要制定一个执行的主类,虚拟机会首先初始化这个主类。

类的被动引用

笔试题1:

  1. public class Father {
  2. static int count = 1;
  3. static{
  4. System.out.println("Initialize class father");
  5. }
  6. }
  7. public class Son extends Father {
  8. static{
  9. System.out.println("Initialize class son");
  10. }
  11. }
  12. public class Main {
  13. public static void main(String[] args) {
  14. int n = Son.count;
  15. }
  16. }

执行的结果:
Initialize class father
被动引用定义1:通过子类引用父类的静态变量,不会导致子类初始化。
虽然是以Son.count形式调用的,但是因为count是Father的静态成员变量,所以只初始化Father类,而不初始化Son类。

笔试题2:

  1. public class Test1 {
  2. static{
  3. System.out.println("Initialize class Test1");
  4. }
  5. }
  6. public class Main {
  7. public static void main(String[] args) {
  8. Test1[] e = new Test1[10];
  9. }
  10. }

执行的结果:
没有任何输出
被动引用定义2:通过数组定义类引用类,不会触发类的初始化。

笔试题3:

  1. public class Test2 {
  2. static final int count = 1;
  3. static{
  4. System.out.println("Initialize class Test2");
  5. }
  6. }
  7. public class Main {
  8. public static void main(String[] args) {
  9. int n = Test2.count;
  10. }
  11. }

执行结果:
没有任何输出
被动引用定义3:不会触发定义常量的类的初始化。
常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。
Test2类中定义的count是final对象,其在编译阶段就会存入调用类的常量池中。

被动引用总结

image.png
除了主动引用外,所有引用类的方式都不会触发初始化,称为被动引用。
被动引用定义1:通过子类引用父类的静态变量,不会导致子类初始化。
被动引用定义2:通过数组定义类引用类,不会触发类的初始化。
被动引用定义3:不会触发定义常量的类的初始化