欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

类的加载方式,加载方式

来源: javaer 分享于  点击 10240 次 点评:212

类的加载方式,加载方式


  类加载器(ClassLoader),用来加载 Java 类(.class)到 Java虚拟机(JVM)中的 一个java类。

  Java源程序(.java文件)在经过 Java编译器 编译之后就被转换成 Java字节代码(.class 文件)。

  类加载器负责读取Java字节代码,并将其转换成 java.lang.Class 类的一个实例。 

 

 

  Class.forName(xxx.xx.xx) 返回一个类

首先你要明白在java里面任何class都要装载在虚拟机上才能运行。这句话就是装载类用的(和new 不一样,要分清楚)。

至于什么时候用,可以考虑一下这个问题,给出一个字符串变量,它代表一个类的包名和类名,怎么实例化它?只有提到的这个方法了,不过要再加一点。

A a = (A)Class.forName("pacage.A").newInstance();

这和

A a = new A();

是一样的效果。

 

相关的补充问题

   JVM会执行静态代码段,要记住一个概念,静态代码是和class绑定的,class装载成功就表示执行了静态代码了,以后也就不会再走这段静态代码了。

   Class.forName(xxx.xx.xx) 返回的是一个类

   Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段,动态加载和创建Class 对象,比如想根据用户输入的字符串来创建对象

String str = 用户输入的字符串

Class t = Class.forName(str);

t.newInstance();

 

   在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?

   它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。

   那么为什么会有两种创建对象方式?

   这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想。

   Java中工厂模式经常使用newInstance()方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案。 例如:

 

class c = Class.forName(“Example”);

factory = (ExampleInterface)c.newInstance();

   其中ExampleInterface是Example的接口,可以写成如下形式:

String className = "Example";

class c = Class.forName(className);

factory = (ExampleInterface)c.newInstance();

   进一步可以写成如下形式:

String className = readfromXMlConfig;//从xml 配置文件中获得字符串

class c = Class.forName(className);

factory = (ExampleInterface)c.newInstance();

   上面代码已经不存在Example的类名称,它的优点是,无论Example类怎么变化,上述代码不变,甚至可以更换Example的兄弟类Example2 , Example3 , Example4……,

只要它们继承ExampleInterface也就是factory就可以。

   从JVM的角度看,使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:

1、这个类已经加载;2、这个类已经连接了。

而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载 java API的那个加载器。

   现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。

   分步的好处是显而易见的:可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

   最后用最简单的描述来区分new关键字和newInstance()方法的区别:

   newInstance: 弱类型。低效率。只能调用无参构造。

   new: 强类型。相对高效。能调用任何public构造。

 

forName() 和 ClassLoader 的 loadClass 方法。

    为什么 forName()是会执行static语句?

    因为默认情况它总是初始化这个被装载的类。

   

    关于forName()方法

    这个方法总是返回要加载的类的Class类的实例

1、forName(String className)单参数时, initialize=true

    a.总是使用当前类装载器(也就是装载执行forName()请求的类的类装载器)

    b.总是初始化这个被装载的类(当然也包括:装载、连接、初始化)

2、forName(String className, boolean initialize, ClassLoader loader)

    a.loader指定装载参数类所用的类装载器,如果null则用bootstrap装载器。

    b.initialize=true时,肯定连接,而且初始化了;

    c.false时,绝对不会初始化,但是可能被连接了,有个例外,如果在调用这个forName()前,已经被初始化了,那么返回的类型也肯定是被初始化的(当然,暗含着:

      被同一个loader所装载的,而且该类被初始化了)

    关于用户自定义的类装载器的loadClass()方法

1、loadClass(String name)单参数时, resolve=false

    a.如果该类已经被该类装载器所装载,那么,返回这个已经被装载的类型的Class的实例,否则,就用这个自定义的类装载器来装载这个class,这时不知道是否被连接。绝对不会被初始化

    b.这时唯一可以保证的是,这个类被装载了。但是不知道这个类是不是被连接和初始化了

2、loadClass(String name, boolean resolve)

    a.resolve=true时,则保证已经装载,而且已经连接了。

       resolve=falses时,则仅仅是去装载这个类,不关心是否连接了,所以此时可能被连接了,也可能没有被连接

 

 

 

Class.forName和ClassLoader.loadClass方法都用来动态的加载一个类(都返回一个java.lang.Class对象),区别在于他们使用的ClassLoader和是否执行类的初始化。

1.1      Class.forName

这是一个类方法,该方法除了加载类到JVM中,还会执行类的初始化,即类的静态初始化。

 

使用Class.forName最常用的形式是Class.forName(String qualifiedClassName),如写JDBC程序常用到的加载某个数据库Driver到jvm的代码:

Class.forName("com.mysql.jdbc.Driver");

该函数使用调用者的ClassLoader, 即调用该行代码的类所使用的ClassLoader,返回一个参数名所对应的Class对象。

另外还有一个带三个参数的forName方法, Class.forName(String, boolean, ClassLoader),参数分别用来指定类名,是否初始化和要使用的ClassLoader。

1.2      ClassLoader.loadClass

这是一个实例方法,可以使用一个特定的ClassLoader来加载一个类。该方法不会执行类的初始化,只有当该类第一次被使用时才会执行类的初始化,比如调用该类的一个静态方法或创建该类的一个对象。

1.3      使用哪一个方法

1.3.1       ClassLoader

不同的ClassLoader加载类时会从不同的位置来查找类的.class文件,所以如果要加载的类不在默认的查找位置时,就应该指定要使用的ClassLoader,否则会得到java.lang.ClassNotFoundException的错误。为了灵活性,你应该总是提供一个来参数指定ClassLoader而不是使用默认的ClassLoader,即使用带三个参数的forName或loadClass方法。关于不同的ClassLoader如何查找及加载类请参考ClassLoder一文。

1.3.2       是否要执行类初始化

在上面加载Mysql Driver的例子中,我们本身并不需要创建MySqlDriver对象,只需要加载Driver到JVM,并执行类的初始化(可能执行注册自己到Driver Manger的代码)。所以可以使用forName方法,如果使用ClassLoader.loadClass则需要像下面这样:

ClassLoader cl = …;

cl.loadClass("com.mysql.jdbc.Driver").newInstance(); //创建对象仅仅为了执行类初始化,对象本身并不会被使用。

 

如果已知类的初始化是一个比较耗时的过程,则可以将其初始化推迟到该类被一次使用时。另外执行类的初始化代码失败会抛出java.lang.ExceptionInInitializerError异常。此时加载的类就变得不可用。类的初始化错误往往很难跟踪,所以如果要在一个特定点捕获并处理该异常就可以使用Class.forName并放在一个try块中。但是不要试图重新加载该类,因为一个类只能被同一个ClassLoader加载一次。

 

 

下面给你说几种获取Class对象的方法:

 Class对象的获取

1.所有的引用数据类型(类-类型)的类名、基本数据类型都可以通过.class方式获取其 Class对象(

对于基本数据类型的封装类还可以通过.TYPE 的方式获取其 Class 对象,但要注意:

TYPE 实际上获取的封装类对应的 基本类型 的 Class 对象的引用,

那么你可以判断出int.class==Integer.TYPE 返回 true,int.class==Integer.class 返回 false),

通过这种方式不会初始化静态域,使用.class、.TYPE 的方式获取 Class对象叫做类的字面常量;

2.Class 的 forName(String  name)传入一个类的完整类路径也可以获得 Class 对象,

但由于使用的是字符串,必须强制转换才可以获取泛型的Class<T>的 Class对象,

并且你必须获取这个方法可能抛出的ClassNotFoundException异常。这种方法可以初始化静态域。

3.还可通过类的对象实例下的getClass()方法来获取Class对象,即 实例名.getClass()

 

 

 

1)Class cl=A.class; JVM将使用类A的类装载器,将类A装入内存(前提是:类A还没有装入内存),不对类A做类的初始化工作.返回类A的Class的对象

2)Class cl=对象引用o.getClass();返回引用o运行时真正所指的对象(因为:儿子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象 

3)Class.forName("类名"); .装入类A,并做类的初始化

 

getClass()是动态的,其余是静态的。

.class class.forName()只能返回类内field的默认值,getClass可以返回当前对象中field的最新值

Class.forName(xxx.xx.xx) 返回的是一个类, .newInstance() 后才创建一个对象 Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段 

相关文章

    暂无相关文章
相关栏目:

用户点评