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

Class clazz = Class.forName("package.A");

A a = (A) clazz.newInstance();

这和


A a = new A();



是一样的效果。 



接下来介绍一下ClassLoader.loadClass()与Class.forName()的关系:

Classloader的作用,概括来说就是将编译后的class装载、加载到机器内存中,为了以后的程序的执行提供前提条件。每个被ClassLoader加载的class文件,最终都会以Class类的实例被程序员引用,我们可以把Class类当作是普通类的一个模板,JVM根据这个模板生成对应的实例,最终被程序员所使用。

我们看到在Class类中有个静态方法forName,这个方法和ClassLoader中的loadClass方法的目的一样,都是用来加载class的,但是两者在作用上却有所区别。
Class<?> loadClass(String name)
Class<?> loadClass(String name, boolean resolve)
我们看到上面两个方法声明,第二个方法的第二个参数是用于设置加载类的时候是否连接该类,true就连接,否则就不连接。

说到连接,不得不在此做一下解释,在JVM加载类的时候,需要经过三个步骤,装载、连接、初始化(切记这里的初始化是类的初始化,可不是new对象)。装载就是找到相应的class文件,读入JVM,初始化就不用说了(见下一篇博文),最主要就说说连接。

连接分三步,第一步是验证class是否符合规格,第二步是准备,就是为类变量分配内存同时设置默认初始值,第三步就是解释,而这步就是可选的,根据上面loadClass方法的第二个参数来判定是否需要解释,所谓的解释根据《深入JVM》这本书的定义就是根据类中的符号引用查找相应的实体,再把符号引用替换成一个直接引用的过程。想具体了解就翻翻《深入JVM吧》。

我们再来看看那个两个参数的loadClass方法,在JAVA API 文档中,该方法的定义是protected,那也就是说该方法是被保护的,而用户真正应该使用的方法是一个参数的那个,一个参数的loadclass方法实际上就是调用了两个参数的方法,而第二个参数默认为false,因此在这里可以看出通过loadClass加载类实际上就是加载的时候并不对该类进行解释,因此也不会初始化该类。见代码:

public class Test2 {
	public static void main(String[] args) {
		TestOrder tOrder = new TestOrder();	
		try {
			tOrder.getClass().getClassLoader().loadClass("com.qimiguang.Test");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

public class Test {
    static {
        System.out.println("test静态代码段");
    }
}


是不 会输出"test静态代码段"的(静态代码块是跟初始化绑定的)。


而Class类的forName方法则是相反,使用forName加载的时候就会将Class进行解释和初始化, forName也有另外一个版本的方法,可以设置是否初始化以及设置ClassLoader:

Class. forName( "com.common.Test" , false, new Test2().getClass().getClassLoader());

这时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就可以。 

从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载(new之前没有被加载的意思)。但是使用newInstance()方法的时候,就必须保证:1、这个类已经加载;2、这个类已经连接了。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载java API的那个加载器。 

现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。