java中Class对象详解和类名.class, class.forName(), getClass()比较

Class对象一般使用于反射情况下,通过反射可以在运行期获取对应类的Class类型信息,Class中存储了类的几乎所有信息,当然这些信息是未初始化的信息,比如所有方法,所有的构造函数,所有的字段(成员属性)等等。

Class对象生成方式
1. 类名.class   jvm把类加载进内存但不进行初始化,返回Class对象
2.Class.forName("包名.类名") ,装入类,默认同时进行静态初始化,返回Class对象
   Class.forName("包名.类名",false,类加载器); // False表示不初始化,True则进行初始化
3.实例对象.getClass() 对类进行静态初始化、非静态初始化;返回引用是运行时真正所指对象(向上转型)所属类的Class对象

父子类Class对象是不一样的
比如类A,它的实例对象是a,则A.class和a.getClass()返回结果一致。若A是B的子类,则a.getClass()和B.class不一样。

Class类方法:
getName():String:获得该类型的全名。
getSuperClass():Class:获得该类型的直接父类,如果该类型没有直接父类,那么返回null。
getInterfaces():Class[]:获得该类型实现的所有接口。
isArray():boolean:判断该类型是否是数组。
isEnum():boolean:判断该类型是否是枚举类型。
isInterface():boolean:判断该类型是否是接口。
isPrimitive():boolean:判断该类型是否是基本类型,即是否是int,boolean,double等等。
isAssignableFrom(Class cls):boolean:判断这个类型是否是类型cls的父(祖先)类或父(祖先)接口。
getComponentType():Class:如果该类型是一个数组,那么返回该数组的组件类型。
asSubclass(Class clazz):Class:将调用这个方法的class对象转换成由clazz参数所表示的class对象的某个子类。

// 简单介绍asSubclass使用,其余都是常见方法
List<String> strList = new ArrayList<String>();  
Class<? extends List> strList_cast = strList.getClass().asSubclass(List.class);

有时候我们不能确切地知道一个class对象的类型,典型的情况是Class.forName()获取的class对象:Class.forName()的返回类型是Class<?>,但这显然太宽泛了,假设我们需要List.class类型的class对象,但我们传递给Class.forName的参数是未知的(可能是java.lang.String,也可能是java.util.ArrayList),此时asSubclass排上用场了,如下:
Class.forName("xxx.xxx.xxx").asSubclass(List.class).newInstance();  
当xxx.xxx.xxx是List子类时则正常执行,不是List子类时则抛出ClassCastException。

new ClassName(),就是所谓的静态加载,
Class.forName("ClassName"),就是所谓的动态加载。
区别在于“静态加载”的类在编译的时候就要提供,而动态加载的类在源程序编译时可以缺席,在运行时按需提供

// 通过运行下面示例,观察Class对象生成过程中,ClassInitialTest是否进行了初始化。
public static void main(String[] args) {
    try {
        Class c = ClassInitialTest.class; // ClassInitialTest不会初始化,无任何输出
        System.out.println(".class---" + c);

        // 参数 true表示对类ClassInitialTest进行初始化,否则不会
        Class cl1 = Class.forName("andy.blog.ClassInitialTest", true, LoggerTest.class.getClassLoader());
        System.out.println("param true,Class.forName--- " + cl1); // 输出:---静态的参数初始化---

        Class cl2 = Class.forName("andy.blog.ClassInitialTest", false, LoggerTest.class.getClassLoader());
        System.out.println("param false,Class.forName---" + cl2); // ClassInitialTest不会初始化,无任何输出

        Class cl3 = Class.forName("andy.blog.ClassInitialTest");  // 默认true进行初始化
        System.out.println("Class.forName---" + cl3); // 输出:---静态的参数初始化---

        ClassInitialTest classInitialTest = new ClassInitialTest();
        Class cla = classInitialTest.getClass(); 
        // 输出结果如下:
        // ---静态的参数初始化e---
        // ---非静态的参数初始化e---
        // ---构造函数e---
        System.out.println("实例对象.getClass()---" + cla);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public class ClassInitialTest {
    public ClassInitialTest() {
        System.out.println("---构造函数---");
    }
    static {
        System.out.println("---静态的参数初始化---");
    }
    {
        System.out.println("---非静态的参数初始化---");
    }
}

根据上述执行结果发现,三种方式生成的Class对象是一样的。各种方式初始化,见上面输出部分

我们知道,静态的方法属性初始化,是在加载类的时候初始化。而非静态方法属性初始化,是实例化时候加载。

因此,这段程序说明,三种方式生成Class对象,其实只有一个Class对象。在生成Class对象的时候,首先判断内存中是否已经加载。所以,生成Class对象的过程其实是如此的:
当我们编写一个新的java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。

猜你喜欢

转载自blog.csdn.net/jiangtianjiao/article/details/87921370