1、简介
在java世界里,一切皆对象。从某种意义上来说,java有两种对象:实例对象和Class对象。每个类的运行时的类型信息就是用Class对象表示的。它包含了与类有关的信息。其实我们的实例对象就通过Class对象来创建的。Java使用Class对象执行其RTTI(运行时类型识别,Run-Time Type Identification),多态是基于RTTI实现的。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。 每一个类都有一个Class对象( 类A,类A的对象a1,用双等号操作符来比较对象:a1.getClass()==A.class;应该返回的是true。),每当编译一个新类就产生一个Class对象,基本类型 (boolean, byte, char, short, int, long, float, and double)有Class对象,数组有Class对象,就连关键字void也有Class对象(void.class)。Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。
Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。一个类被加载到内存并供我们使用需要经历如下三个阶段:
加载,这是由类加载器(ClassLoader)执行的。通过一个类的全限定名来获取其定义的二进制字节流(Class字节码),将这个字节流所代表的静态存储结构转化为方法去的运行时数据接口,根据字节码在java堆中生成一个代表这个类的java.lang.Class对象。
链接。在链接阶段将验证Class文件中的字节流包含的信息是否符合当前虚拟机的要求,为静态域分配存储空间并设置类变量的初始值(默认的零值),并且如果必需的话,将常量池中的符号引用转化为直接引用。
初始化。到了此阶段,才真正开始执行类中定义的java程序代码。用于执行该类的静态初始器和静态初始块,如果该类有父类的话,则优先对其父类进行初始化。
所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载)。当程序创建第一个对类的静态成员的引用时,就会加载这个类。使用new创建类对象的时候也会被当作对类的静态成员的引用。因此java程序程序在它开始运行之前并非被完全加载,其各个类都是在必需时才加载的。这一点与许多传统语言都不同。动态加载使能的行为,在诸如C++这样的静态加载语言中是很难或者根本不可能复制的。
在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载。如果尚未加载,默认的类加载器就会根据类的全限定名查找.class文件。在这个类的字节码被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良java代码。一旦某个类的Class对象被载入内存,我们就可以它来创建这个类的所有对象。
其实对于任意一个Class对象,都需要由它的类加载器和这个类本身一同确定其在就Java虚拟机中的唯一性,也就是说,即使两个Class对象来源于同一个Class文件,只要加载它们的类加载器不同,那这两个Class对象就必定不相等。这里的“相等”包括了代表类的Class对象的equals()、isAssignableFrom()、isInstance()等方法的返回结果,也包括了使用instanceof关键字对对象所属关系的判定结果。所以在java虚拟机中使用双亲委派模型来组织类加载器之间的关系,来保证Class对象的唯一性。
2、获取Class对象
1)Class.forName
forName(String className)和 forName(String name,boolean initialze, ClassLoader loader)方法。
该方法返回给定串名相应的Class对象。若给定一个类或接口的完整路径名,那么此方法将试图定位、装载和连接该类。若成功,返回该类对象。否则,抛出ClassNotFoundException异常。例如,下面代码段返回名为java.lang.Thread的运行Class描述器。Class t = Class.forName("java.lang.Thread");此方法是需要指定类加载器的,当用到仅有一个String参数的forName方法时,Class对象将默认调用当前类加载器作为加载器和将第二参数为true。第二个参数说明:如果是false时,调用forName方法只是在命令类加载器载入该类,而不初始化该类的静态区块,只有当该类第一次实例化时,静态区块才被调用。当为true时,则载入时就调用静态区块。
2)利用对象调用getClass()方法获取该对象的Class实例。
3)使用类字面常量
//注意,使用这种办法生成Class类对象时,不会使JVM自动加载该类(如String类)。
//类对象的初始化阶段被延迟到了对静态方法或者非常数静态域首次引用时才执行。而其他办法会使得JVM初始化该类。
Class obj = String.class;
Class obj1 = int.class;
//包装类中的TYPE等价于基本类型的.class
//int.class == Integer.TYPE
package com.liusq;
public class Test {
static {
System.out.println("静态代码块");
}
{
System.out.println("动态代码块");
}
public Test(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Class<?> clazz1 = Test.class;
System.out.println("-----------");
try{
Class<?> clazz2 = Class.forName("com.liusq.Test");
}catch (ClassNotFoundException e){
e.printStackTrace();
}
System.out.println("-----------");
Class<?> clazz3 = new Test().getClass();
}
}
打印结果:
静态代码块 ----------- -----------
动态代码块 构造方法
3、主要方法
——getClassLoader()
获取该类的类加载器。
——getField(String)
获得某个类的指定的公共(public)字段。
——getFields()
获得某个类的所有的公共(public)字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。
——getDeclaredField(String)
获得某个类自己声明的指定字段。
——getDeclaredFields()
获得某个类的自己声明的字段,即包括public、private、proteced和默认,但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。
——getMethod(String,Class[])
返回某个类指定的公有成员方法对象。
——getMethods()
返回某个类的所有公有成员方法对象数组,包括已声明的和从父类继承的方法。
——getDeclaredMethod(String,Class[])
返回某个类的指定已说明的一个方法对象。
——getDeclaredMethods()
获得某个类的自己声明的方法,即包括public、private、proteced和默认,但是不包括父类声明的任何方法。
——getInterfaces()
返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
——getModifiers()
返回该类或接口的修饰符的int值(Modifier.toString(XXX.getModifiers()))。
——getName
获取全限定的类名(包括包名),即类的完整名字。
——getSimpleName()
获取类名(不包括包名)
——getCanonicalName()
获取全限定的类名(包括包名), 主要用于输出(toString)或log打印,大多数情况下和getName一样,但是在内部类、数组等类型的表示形式不同 。
——getSuperclass()
返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
——isArray()
如果Class对象表示一个数组则返回true,否则返回false。
——isInstance(Object)
此方法是Java语言instanceof操作的动态等价方法。
——isInterface()
判定指定的Class对象是否表示一个接口类型。
——isPrimitive()
判定指定的Class对象是否表示一个Java的基类型。
——newInstance()
返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
——toString()
将对象转换为字符串。
来源:oschina
链接:https://my.oschina.net/90liusq/blog/4314928