Class类相关知识介绍

Class类相关知识介绍


java提供了两种方式来生成对Class对象的引用:

方式1:Class clazz = Class.forName("类的全限定名");

方式2:Class clazz = 类名.class;

两者最大的区别:

(1):方式1是饿汉式加载,而方式2是懒汉式加载的,也就是说在调用方式1这句代码时,该类中的所有static类型变量或者static块都将会加载;但是调用方式2这句代码并不会默认加载所有的static变量或者static块,我们必须手动让他加载;

(2):方式1在找不到要加载类的时候会抛出ClassNotFound异常;而方式2是在编译期间进行检查的,不需要放入try语句块中,因此更加安全,此外他根除了对forName方法的调用,因此相对来说更加高效;

下面用代码来观察两者的区别:

方式1:

[java] view plain copy
  1. package com.hzw.day31;  
  2.   
  3. public class GetClass {  
  4.     public static void main(String[] args) {  
  5.         try {  
  6.             Class clazz = Class.forName("com.hzw.day31.Test");  
  7.         } catch (ClassNotFoundException e) {  
  8.             e.printStackTrace();  
  9.         }  
  10.     }  
  11. }  
  12. class Test  
  13. {  
  14.     public static int age = 23;  
  15.     public static final String name = "shanxi";  
  16.     static   
  17.     {  
  18.         System.out.println("Test static block");  
  19.     }  
  20. }  
输出结果是:

Test static block

可以看到的首先输出的是static 代码块中的内容,表明使用Class.forName()方式会将所有static变量和static块加载进去,即使你不用;

方式2:

[java] view plain copy
  1. public class GetClass {  
  2.     public static void main(String[] args) {  
  3.         Class clazz = Test.class;  
  4.     }  
  5. }  
  6. class Test  
  7. {  
  8.     public static int age = 23;  
  9.     public static final String name = "shanxi";  
  10.     static   
  11.     {  
  12.         System.out.println("Test static block");  
  13.     }  
  14. }  
输出结果是:

可以发现并没有输出任何结果,这也就证明了方式2是懒汉式加载的,他的初始化操作被延迟到了对静态方法的首次调用才执行;

如果一个static final是"编译器常量"的话,那么这个值也不需要初始化类也能加载进去的,比如下面的例子:

[java] view plain copy
  1. public class GetClass {  
  2.     public static void main(String[] args) {  
  3.         System.out.println(Test.name);//①  
  4.         System.out.println(Test.score);//②  
  5.         System.out.println(Test.age);//③  
  6.     }  
  7. }  
  8. class Test  
  9. {  
  10.     public static int age = 23;  
  11.     public static final String name = "shanxi";  
  12.     public static final Integer score = 85;  
  13.     static   
  14.     {  
  15.         System.out.println("Test static block");  
  16.     }  
  17. }  

单独运行①输出的结果是: shanxi

单独运行②输出的结果是: 

Test static block
85

单独运行③输出的结果是:

Test static block
23
可以看出虽然两者都是static final 类型的,但是name作为"编译常量",他是不需要对类Test进行初始化就可以读取,因而不会执行Test中的静态代码块;但是score虽然作为static final变量,但是他并不是"编译常量",需要初始化Test类之后才可以,因而会首先执行static代码块,随后输出score的值;对于非final的static域,那么对他访问之前要先进行链接(为这个域初始化空间)和初始化(初始化该存储空间),也就明白了单独运行③会首先输出static块的值原因;
补充:

对于采用.class方式来创建对Class对象的引用时,不会自动的初始化该Class对象,为了使用这样的类实际的准备工作包括下面三步:

(1)加载: 这个是由类加载器完成的,该步骤将查找字节码,并利用这些字节码创建一个Class对象;

(2)链接: 在链接阶段将验证类中的字节码,为静态域分配存储空间,如果有必要将解析这个类创建的对其他类的所有引用;

(3)初始化: 如果该类存在超类,则对其初始化,执行静态初始化器和静态初始化块;