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

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

首先,Class是一个java类,跟JavaAPI中定义的诸如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

 

【RTTI】java Class类详解的更多相关文章

  1. Java String类详解

    Java String类详解 Java字符串类(java.lang.String)是Java中使用最多的类,也是最为特殊的一个类,很多时候,我们对它既熟悉又陌生. 类结构: public final ...

  2. Java 枚举类详解

    1. 枚举类定义 在某些情况下,一个类的对象是有限而且固定的,比如季节类,它只有4个对象,这种实例有限而且固定的类,在Java里被称为枚举类. 2. 早期实现枚举的方式 public static f ...

  3. java Random类详解

    java Random类位于java.util包下,主要用来生成随机数,本文详解介绍了Random类的用法,希望能帮到大家 Random类 (java.util) Random类中实现的随机算法是伪随 ...

  4. Java Calender 类详解

    一.   如何创建 Calendar 对象 Calendar 是一个抽象类, 无法通过直接实例化得到对象. 因此, Calendar 提供了一个方法 getInstance,来获得一个Calendar ...

  5. JAVA - 大数类详解

    写在前面 对于ACMer来说,java语言最大的优势就是BigInteger,Bigdecimal,String三个类. 这三个类分别是高精度整数,高精度浮点数和字符串,之所以说这个是它的优势是因为j ...

  6. Java 枚举类 详解

    1.枚举是什么? Java中的枚举其实是一种语法糖,在 JDK 1.5之后出现,用来表示固定且有限个的对象.比如一个季节类有春.夏.秋.冬四个对象:一个星期有星期一到星期日七个对象.这些明显都是固定的 ...

  7. Java重要类详解之ArrayList类

    https://blog.csdn.net/shengmingqijiquan/article/details/52634640 一.ArrayList概述 ArrayList 是一个数组队列,相当于 ...

  8. Java常用类详解

    目录 1. String类 1.1 String的特性 1.2 String字面量赋值的内存理解 1.3 String new方式赋值的内存理解 1.4 String 拼接字面量和变量的方式赋值 1. ...

  9. JAVA CyclicBarrier类详解

    一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrie ...

随机推荐

  1. SqlServer2008必须开启哪些服务

    SQL Server 2008 大概有下面这些服务 SQL Active Directory Helper 服务支持与 Active Directory 的集成SQL Full-text Filter ...

  2. Windows学习总结(10)——Windows系统中常用的CMD命令详解

    1.ping命令 ping是电脑网络故障诊断中的常用的命令,它的作用是用来检查网络是否通畅或者网络连接速度.我们来看一下PING命令的具体表述. 日常的诊断过程中我们最常用到的就是诊断连接是否通畅. ...

  3. ansible plugins简介

    ansible插件是增强ansible的核心功能的代码片段,ansible使用插件架构来实现丰富,灵活和可扩展的功能集. Ansible提供了许多方便的插件,您可以轻松编写自己的插件. 下边简单介绍A ...

  4. COJ 1156 Switching bulbs

    一道模拟题目 对于所有0 还是 1 我们都可以想象做均为 0 的状态 v[i]表示原来的值 但是对于原来为1的要加上其所在的值作为初始值 然后转化后 a[i] = -v[i]  , 如果原来为0 , ...

  5. [luoguP2387] 魔法森林(LCT + 并查集)

    传送门 并查集真是一个判断连通的好东西! 连通性用并查集来搞. 把每一条边按照 a 为关键字从小到大排序. 那么直接枚举,动态维护 b 的最小生成树 用 a[i] + 1 ~ n 路径上最大的 b[i ...

  6. dubbo 学习1

    1.高性能优秀的服务框架,应用可通过高性能的RPC实现服务的输出和输入功能,可以和spring框架无缝集成. 2.主要核心部件 a.remoting 网络通信框架 实现了sync-over-asnc ...

  7. nyoj_518_取球游戏_201404161738

    取球游戏 时间限制:1000 ms  |  内存限制:65535 KB 难度:2   描述 今盒子里有n个小球,A.B两人轮流从盒中取球,每个人都可以看到另一个人取了多少个,也可以看到盒中还剩下多少个 ...

  8. MYSQL中插入数据以及修改数据的部分方法

    #插入/增加:使用INSERT #修改:使用ALTER #修改数据类型ALTER TABLE table02 MODIFY COLUMN cname VARCHAR(100);ALTER TABLE ...

  9. Sublime Text 3显示文本编码

    在Settings上加入"show_encoding":true 进入这个选项:[Preferences]->[Settings] 搞定之后,在右下角可以看见文本编码

  10. 24、Java并发性和多线程-信号量

    以下内容转自http://ifeve.com/semaphore/: Semaphore(信号量) 是一个线程同步结构,用于在线程间传递信号,以避免出现信号丢失(译者注:下文会具体介绍),或者像锁一样 ...