zoukankan      html  css  js  c++  java
  • 【RTTI】java Class类详解

    RTTI (Run-Time Type Information)运行时类信息

    Java的Class类是java反射机制的基础,通过Class类我们可以获得关于一个类的相关信息,下面我们来了解一下有关java中Class类的相关知识!

    首先,Class是一个java类,跟Java API中定义的诸如Thread、Integer类、我们自己定义的类是一样,也继承了Object(Class是Object的直接子类)。总之,必须明确一点,它其实只是个类,只不过名字比较特殊。更进一步说,Class是一个java中的泛型类型。
    Java.lang.Class是一个比较特殊的类,它用于封装被装入到JVM中的类(包括类和接口)的信息当一个类或接口被装入的JVM时便会产生一个与之关联的java.lang.Class对象,可以通过这个Class对象对被装入类的详细信息进行访问。(Java中Class对象和类的实例对象是两个不同的概念,不能混淆!

    Class类的官方定义:public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement

           Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
           Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
           虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
          基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
          每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
          一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象:
    事实上,Class对象就是用来创建类的所有的“普通”对象的。  类是程序的一部分,每个类都有一个Class对象。换言之,每当编写并且编译了一个新类,就会产生一个Class对象(恰当地说,是被保存在一个同名的.class文件中)。在运行时,当我们想生成这个类的对象时,运行这个程序的 Java虚拟机(JVM)首先检查这个类的Class对象是否已经加载。如果尚未加载,JVM就会根据类名查找.class文件,并将其载入。  一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有(实例)对象. 示例详见:http://wenku.baidu.com/link?url=U5y6yldkqx1G6Eo90lwCDLyp9t5FBPXQ2E0hCS_1My4Tqtqao_CxlYZW16_n9pUNjr_3vCPAO-XwJb2gIIQcVOEt0KjGc6EtsdWx9XwvLze

    一、要想对JVM中Class类封装的信息进行访问,首先要获取对应类的Class对象,而获取“某一个类”所对应的“Class对象可以通过如下三种途径:

    1. 通过Object类的getClass方法来获取
    java.lang.Object中定义有getClass方法:public final Class getClass()
    所有Java对象都具备这个方法
    ,该方法用于返回调用该方法的对象所属类关联的Class对象,例如:

    Date date1 = new Date();  
    Date date2 = new Date();  
    Class c1 = date1.getClass();  
    Class c2 = date2.getClass();  
    System.out.println(c1.getName()); // java.util.Date  
    System.out.println(c1 == c2); // true  

    上面的代码中,调用Date对象date1的getClass方法将返回用于封装Date类信息的Class对象。

    这里调用了Class类的getName方法:public String getName(),这个方法的含义很直观,即返回所封装的类的名称。

    需要注意的是,代码中的date1和date2的getClass方法返回了相同的Class对象(c1==c2的值为true)。这是因为,对于相同的类,JVM只会载入一次,而与该类对应的Class对象也只会存在一个,无论该类实例化了多少对象。
    另外,需要强调的是,当一个对象被其父类的引用或其实现的接口类型的引用所指向时,getClass方法返回的是与对象实际所属类关联的Class对象。例如:

    List list = new ArrayList();  
    System.out.println(list.getClass().getName()); // java.util.ArrayList 

    上面的代码中,语句list.getClass()方法返回的是list所指向对象实际所属类java.util.ArrayList对应的 Class对象而并未java.util.List所对应的Class对象。有些时候可以通过这个方法了解一个对象的运行时类型,例如:

    HashSet set = new HashSet();  
    Iterator it = set.iterator();  
    System.out.println(it.getClass().getName()); //java.util.HashMap$KeyIterator 

    从代码可以看出,HashSet的iterator方法返回的是实现了Iterator接口的HashMap内部类(KeyIterator)对象。
    因为抽象类和接口不可能实例化对象,因此不能通过Object的getClass方法获得与抽象类和接口关联的Class对象。
    2. 使用.class的方式
    使用类名加“.class”的方式即会返回与该类对应的Class对象。例如:

    Class clazz = String.class;  
    System.out.println(clazz.getName()); // java.lang.String  

    这个方法可以直接获得与指定类关联的Class对象,而并不需要有该类的对象存在。
    3. 使用Class.forName方法

    Class有一个著名的static方法forName:public static Class forName(String className) throws ClassNotFoundException

    该方法可以根据字符串参数所指定的类名获取与该类关联的Class对象。如果该类还没有被装入,该方法会将该类装入JVM。
    该方法声明抛出ClassNotFoundException异常。顾名思义,当该方法无法获取需要装入的类时(例如,在当前类路径中不存在这个类),就会抛出这个异常。
    例如,如果当前类路径中存在Foo类:

    package org.whatisjava.reflect;  
    public class Foo {  
        public Foo() {  
            System.out.println("Foo()");  
        }  
        static {  
            System.out.println("Foo is initialized");  
        }  
    }  

    运行下面的代码:
    Class clazz = Class.forName("org.whatisjava.reflect.Foo");
    控制台会有如下输出:
    Foo is initialized
    Class.forName("org.whatisjava.reflect.Foo")首先会将reflection.Foo类装入JVM,并返回与之关联的Class对象。JVM装入Foo类后对其进行初始化,调用了其static块中的代码需要注意的是forName方法的参数是类的完 整限定名(即包含包名)
     区别于前面两种获取Class对象的方法:使用Class.forName方法所要获取的与之对应的Class对象的类可以通过字符串的方式给定。该方法通常用于在程序运行时根据类名动态的载入该类并获得与之对应的Class对象。
    通过上面的文章相信你对java的反射机制有了一定的认识,同时也对java中Class类的用法有了比较清晰的理解,在我们实际工作的过程中,我们不断的运用java知识来解决实际生活中的问题的时候我们就能对java反射机制有一个更深入的理解!

    二、代码示例

    1.ClassTest.java

    /** 
     * java中Class类的使用 
     */  
    import java.io.*;  
    import java.lang.reflect.*;  
    public class ClassTest1 {  
        public ClassTest1(){  
              
        }  
        public static void main(String[] args) throws Exception{  
            ClassTest1 test=new ClassTest1();  
            ClassTest1 test1=test.getClass().newInstance();  
            //test1=test;  
            test.printMessage();  
            test1.printMessage();  
            System.out.println(test.hashCode());  
            System.out.println(test1.hashCode());  
              
            Method[] method=test1.getClass().getMethods();  
              
            for(Method m :method){  
                System.out.println(m.getDeclaringClass());  
                System.out.println(m.getName());  
            }  
        }  
        public void printMessage(){  
            System.out.println("Created successful!");  
        }  
    }  

    运行结果:

    Created successful!
    Created successful!
    366712642
    1829164700
    class test.Main3
    main
    class test.Main3
    printMessage
    class java.lang.Object
    wait
    class java.lang.Object
    wait
    class java.lang.Object
    wait
    class java.lang.Object
    equals
    class java.lang.Object
    toString
    class java.lang.Object
    hashCode
    class java.lang.Object
    getClass
    class java.lang.Object
    notify
    class java.lang.Object
    notifyAll

    2.TestClass.java 

    package test;
    
    import java.util.*;
    import java.lang.reflect.*;
    
    // Class类
    public class Main2 {
        public static void main(String[] args) throws Exception {
            try {  
                //  Class.forName()  
                Class testTypeForName = Class.forName("test.TestClassType");  
                System.out.println("testForName---" + testTypeForName); 
                
                //  类名.class  
                Class testTypeClass = TestClassType.class;  
                System.out.println("testTypeClass---" + testTypeClass); 
                
                //  实例.getClass()  
                TestClassType testGetClass = new TestClassType();  
                System.out.println("testGetClass---" + testGetClass.getClass());  
                
                System.out.println("下面通过三种不通过new的方式创建实例对象:类对象.newInstance()");  
                
                //再一次实例化,再一次执行非静态的参数初始化   
                
                //Class.forName("").newInstance() 返回Object类型
                TestClassType instance1 =  (TestClassType)Class.forName("test.TestClassType").newInstance();  
                
                
                TestClassType instance2 =  TestClassType.class.newInstance(); 
                
                TestClassType instance3 =  testGetClass.getClass().newInstance(); 
                
            } catch (ClassNotFoundException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }
    }
    class TestClassType {  
        // 构造函数  
        public TestClassType() {  
            System.out.println("----构造函数---");  
        }  
        // 静态的参数初始化  
        static {  
            System.out.println("---静态的参数初始化---");  
        }  
        // 非静态的参数初始化  
        {  
            System.out.println("----非静态的参数初始化---");  
        }  
      
    }  

    运行结果:

    ---静态的参数初始化---
    testForName---class test.TestClassType
    testTypeClass---class test.TestClassType
    ----非静态的参数初始化---
    ----构造函数---
    testGetClass---class test.TestClassType
    下面通过三种不通过new的方式创建实例对象:类对象.newInstance()
    ----非静态的参数初始化---
    ----构造函数---
    ----非静态的参数初始化---
    ----构造函数---
    ----非静态的参数初始化---
    ----构造函数---

    分析:根据结果可以发现,三种生成的Class对象一样的,并且三种生成Class对象只打印一次“静态的参数初始化”。 
    我们知道,静态的方法属性初始化,是在加载类的时候初始化。而非静态方法属性初始化,是new类实例对象的时候加载。
    因此,这段程序说明,三种方式生成Class对象,其实只有一个Class对象。在生成Class对象的时候,首先判断JVM中是否已经加载。

    JVM虚拟机为每种类型管理一个独一无二的Class对象,也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。

    本文参考:http://blog.sina.com.cn/s/blog_6c9536b90100rqh4.html

                        http://blog.csdn.net/yuebinghaoyuan/article/details/7244123

     
  • 相关阅读:
    命令模式(Command Pattern)
    外观模式(Facade Pattern)
    Hash函数的安全性
    单向散列函数
    装饰者模式(Decorator Pattern)
    尝试设计LFSR加密器,并用CAP4验证随机性
    对称密码-流密码
    组合模式(Composite Pattern)
    桥接模式(Bridge Pattern)
    适配器模式(Adapter Pattern)
  • 原文地址:https://www.cnblogs.com/chengdabelief/p/7442880.html
Copyright © 2011-2022 走看看