`
cantellow
  • 浏览: 842541 次
  • 性别: Icon_minigender_1
  • 来自: 草帽海贼团
社区版块
存档分类
最新评论

Class对象与类加载器

    博客分类:
  • Java
阅读更多

class对象是java.lang.Class<T>这个类生成的对象,Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(booleanbytecharshortintlongfloatdouble)和关键字 void 也表示为 Class 对象。 上面这段话是API对class类的解释,不免有点不解。

实际上,每个类都有一个class对象。换言之,每当编写并且编译了一个新类,就会产生一个class对象(被保存在一个同名的.class文件中)。在运行时,当我们想生成这个类的对象时,运行这个程序的Java虚拟机首先检查这个类的class对象是否已经加载。如果尚未加载,JVM就会根据类名查找.class文件,并将其载入,一旦某个类的class对象被载入内存,它就被用来创建这个类的所有对象。

class类用的比较多的方法就是forName(String className)getName() 还有newInstance() 等等,关于他们的详细用法你可以去查查API,我在这里就不赘述。我要说的是他们的返回值,forName(String className)返回的是一个对class对象的引用,getName() 返回的是类名(也可以是接口名),而newInstance()返回的是对一个普通对象的引用。

考虑下面一段代码:

public class TestStatic {

/**
* @param args
*/
public static void main(String[] args) {
   // TODO Auto-generated method stub
   System.out.println("用这种方式加载类:Static test = new Static()");
   Static test = new Static();
   System.out.println("生成test对象的class对象的类加载器是:"+test.getClass().getClassLoader()); 
   System.out.println("生成test对象的class对象的类加载器的委托的父类加载器是:"+test.getClass().getClassLoader().getParent()); 
   System.out.println("判定指定的 Object 是否与此 Class 所表示的对象赋值兼容:"
     +test.getClass().isInstance(test));
   if (test instanceof Static)
    System.out.println("test是类Static的实例!");
   System.out.println();
  
   System.out.println("用这种方式加载类:Class.forName()");
   try
   {
    Class testForName = Class.forName("Static");
    Static testNew =(Static) testForName.newInstance();   
    System.out.println("class对象testForName的类加载器是:"+testForName.getClassLoader());
    System.out.println("class对象testForName的类加载器的委托的父类加载器是:"+testForName.getClassLoader().getParent());
    System.out.println("判定指定的 Object 是否与此 Class 所表示的对象赋值兼容:"
      +testForName.isInstance(test));
    if (testNew instanceof Static)
     System.out.println("testNew是类Static的实例!");
    System.out.println();
   }
   catch(ClassNotFoundException e)
   {
    e.getMessage();
   }
   catch(InstantiationException e)
   {
    e.getMessage();
   }
   catch(IllegalAccessException e)
   {
    e.getMessage();
   }
  
   System.out.println("用这种方式加载类:Static.class");
   Class testClass = Static.class;
   System.out.println("class对象testClass的类加载器是:"+testClass.getClassLoader());
   System.out.println("class对象testClass的类加载器的委托的父类加载器是:"+testClass.getClassLoader().getParent());
   System.out.println("判定指定的 Object 是否与此 Class 所表示的对象赋值兼容:"
     +testClass.isInstance(test));
   try
   {
    Static testClassNew =(Static) testClass.newInstance();
    if (testClassNew instanceof Static)
     System.out.println("testClassNew是类Static的实例!");
   }
   catch(InstantiationException e)
   {
    e.getMessage();
   }
   catch(IllegalAccessException e)
   {
    e.getMessage();
   }
}

}

class Static
{
public Static()
{
   System.out.println("执行构造器!");
}
static
{
   System.out.println("初始化静态变量!");
}
{
   System.out.println("初始化非静态变量!");
}
static void print()
{
   System.out.println("为静态方法配置!");
}
}

 

运行结果为:

 

 

代码多,但其实还是很简单的,我想表达的是:

这里有三种不同的加载类的方式,但是聪明的读者就会发现其实整个过程只有一个class对象,它产生于第一次对它的加载,也就是说,类加载器要加载一个类,它首先检查此类是否已被加载,然后再委托双亲加载器加载此类,它的双亲加载器再委托它的双亲,这样一直委托到启动加载器,启动加载器在从核心API查找此类,如果有就返回此类,否则就他的子加载器就查找此类,如果都没有就抛出ClassNotFound的异常。

第一次加载的时候初始化此类,所以我们看到在第一次加载类的时候输出了“初始化静态变量”,然后第二次用Class.ForName()加载的时候,类加载器检查到这个类已经被加载,那么就返回对这个class对象的引用,所以自始自终,虽然有不止一个的class对象的引用,但是class对象却只有一个!

如果你对加载类的顺序迷惑的话,那么就请看类加载器的一个方法吧:

loadClass

protected Class<?> loadClass(String name,
                             boolean resolve)
                      throws ClassNotFoundException
使用指定的二进制名称来加载类。此方法的默认实现将按以下顺序搜索类:

 

  1. 调用 findLoadedClass(String) 来检查是否已经加载类。

  2. 在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。

  3. 调用 findClass(String) 方法查找类。

如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。

鼓励用 ClassLoader 的子类重写 findClass(String),而不是使用此方法。

ClassLoader具体加载类的顺序是:

# 调用 findLoadedClass 来查看是否存在已装入的类。
# 如果没有,那么采用那种特殊的神奇方式来获取原始字节。
# 如果已有原始字节,调用defineClass将它们转换成Class对象。
# 如果没有原始字节,然后调用findSystemClass查看是否从本地文件系统获取类。
# 如果resolve参数是true,那么调用resolveClass解析Class对象。
# 如果还没有类,返回ClassNotFoundException。
# 否则,将类返回给调用程序。

所以不管用哪种方式加载类,它们实际上都执行了上面这个方法,如果这个类已经加载了,那么就返回对这个class对象的引用,然后用这个class对象可以创建实例,这个实例又可以通过getClass()得到这个class对象的引用,而这个class对象的引用又可以通过getName()得到类名,也可以通过getClassLoader()得到加载这个class对象的加载器。- -!好像我都被搞糊涂了。。呵呵

分享到:
评论
1 楼 C_J 2009-11-07  
所有.java在启动jvm的时候就开始编译,推测class这个对象是static的

相关推荐

    Java虚拟机JVM类加载初始化

    由Java虚拟机自带的默认加载器(根加载器、扩展加载器、系统加载器)所加载的类在JVM生命周期中始终不被卸载。所以这些类的Class对象(我称其为实例的模板对象)始终能被触及!而由用户自定义的类加载器所加载的类会...

    Java基础加强之类加载器

    类加载是指将类的class文件读入内存,并为之创建一个Java.lang.Class对象,也是说当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。  类加载器负责加载所有类,系统为所有被载入内存中的类生成一...

    反射-类加载之间的关联关系

    1、 使用反射方法强制创建某各类或者接口对应的java.lang.Class对象时,会进行类加载,(而类加载会将字节码的class文件读入内存,并为之创建一个Class对象)。 2、 反射让类加载了,当然接着可以调用类的属性和方法...

    类加载器和双亲委派模型加载类、类的加载优先级的详解.docx

    加载就是将字节码的二进制流转化为 方法区 的运行时数据结构,并生成类所对象的Class对象,字节码二进制流可以是我们编译后的class文件,也可以从网络中获取,或者运行时动态生成(动态代理)等等。 那什么时候会...

    Java类加载机制学习1

    (1)双亲模型类加载器的作用:从class文件定义出class对象通过defineClass()方法进行定义类加载器 初始类加载器关系:该类的定义类加载器是该类

    Java 类加载机制详解

    在我们使用一个类之前,JVM需要先将该类的字节码文件(.class文件)从磁盘、网络或其他来源加载到内存中,并对字节码进行解析生成对应的Class对象,这是类加载器的功能。我们可以利用类加载器,实现类的动态加载。 ...

    5分钟快速掌握Java 类加载器

    类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。 一般来说,Java 虚拟机使用 Java 类的方式如下: Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。 类...

    JVM学习笔记(一)——类的加载机制

    ​ 类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动...

    类和对象的生命周期

    目录1、类加载2、使用2.1对象实例化2.1.1 为Student对象...加载: 将类的二进制流加载进VM,储存在方法区,并且生成java.lang.Class对象 类加载器有三种: 跟类加载器(bootstrap class loader) 扩展类加载器(extension

    MD5Util.class

    类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。

    JVM 运行时数据区域,垃圾回收机制,类加载机制三大功能详解.docx

    VM相关的一些内容,比如下面的这三个内容算是比较核心知识点了 运行时数据区域: 在运行时数据区里存储类Class文件元数据...类加载机制: 虚拟机首先需要把编译完成的字节码文件通过类加载器来加载到运行时数据区域

    类加载与类初始化

    \quad\quad将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口。 这个过程需要类加载器参与。 \...

    Java类的加载、链接和初始化

    一、Java的类加载机制回顾与总结: ...代理模式则指的是一个类加载器既可以自己完成Java类的定义工作,也可以代理给其它的类加载器来完成。由于代理模式的存在,启动一个类的加载过程的类加载器和终

    jsx-classnameify-loader:一个 Webpack 加载器,用于搜索 HTML“class”标记并替换为“className”

    替换“class”和“for”属性并分别替换“className”和“htmlFor”的加载器,以及需要驼峰式大小写的任何受支持的属性。 HTML“class”属性在 JSX 中被默默忽略,你应该使用“className”。 我讨厌 XML,我只会在它...

    轻松搞定jvm类加载器

    而**当我们需要用到某个类时,jvm将会加载它,并在内存中创建对应的class对象,这个过程称之为类的加载。**过程如下: 类的加载、连接、初始化 1. 加载 通过类的包名和雷鸣查找到此类的字节码文件,将xx.class文件...

    JVM的类加载过程以及双亲委派模型详解

    主要介绍了JVM的类加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。,需要的朋友可以参考下

    day15-类加载器&反射&模块化1

    声明的所有构造函数的Constructor对象的数组}//Constructor&lt;T&gt; getConstructor(Class&lt;?&gt;... parameter

    case-class-generator:在运行时动态定义和加载Scala类。 有助于将JSON模式快速转换为Scala案例类

    因此,现在我正在使用和生成案例类的字节码,并使用自定义类加载器加载新创建的类。 动态生成的类可用于在运行时实例化新对象,或用作类型参数。 请参阅下面的警告。 支持生成具有以下数据类型的任意字段的类: ...

    类加载器深入解析与阶段分解

    1.类加载 在java代码中,类型的加载、连接与...将类的.class文件的二进制数据读入到内存中,将其放在运行时数据的方法区内,然后在内存中创建一个java.lang.Class用来封装类在方法区内的数据结构(java规范并未说明Cl

Global site tag (gtag.js) - Google Analytics