关于Class.forName()与ClassLoader.loadClass()与new的区别

这篇文章解决三个问题:

1.Class.forName()与ClassLoader.loadClass()的区别;

2.new与Class.forName()的区别;

3.有了new,为什么还要Class.forName();

一、Class.forName()与ClassLoader.loadClass()的区别

首先了解Java类的加载机制,什么是类的加载?

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。

类的加载过程包括加载、链接(包括验证、准备、解析)、初始化五个阶段。这几个阶段发生的顺序是确定的,但是解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始。注意这几个阶段是按顺序开始,不是按顺序进行或完成,这些阶段通常都是互相交叉地混合进行,即在一个阶段执行的过程中调用或激活另一个阶段。

加载:获取类的二进制字节流,将字节流所代表的今天存储结构转化为方法区的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。

验证:确保被加载的类的正确性,如字节流是否符合Class文件格式的规范、对字节码描述的信息进行语义分析、数据流和控制流分析确定程序语义是合法并符合逻辑的、确保解析动作能正确执行。

准备:为类的静态变量在方法区中分配内存,不包括实例变量,实例变量会在对象实例化的时候随对象一块分配到Java堆中。这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显示地赋予的值,因为这个时候尚未开始执行任何Java方法,而静态赋值语句是在程序编译后,存放于类构造器方法之中的,所以静态赋值语句将在初始化阶段才会执行。

解析:虚拟机将常量池内的符号引用替换为直接引用的过程,主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以使任何字面量。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

初始化:为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化,也就是执行静态代码,而且以后不会再执行这段静态代码了。在Java中对类变量进行初始值设定有两种方式:①声明类变量是指定初始值②使用静态代码块为类变量指定初始值。

了解了上面加载、链接、三个阶段。

现在讲Class.forName() 和 ClassLoader.loadClass() 的区别:

Class.forName(String name)

该方法内部调用另一个重载方法forName(String name, boolean initialize, ClassLoader loader),这个重载方法使用给定的ClassLoader加载类并返回类的Class对象。这个重载方法加载类的过程包括加载、链接,而且它的第二个参数可以控制是否进行初始化,也就是是否执行static块。

ClassLoader.loadClass()

loadClass()方法得到的Class对象是还没有链接的,适合在类加载时不需要一些初始化的情况。

二、forName的作用

首先你要明白在java里面任何class都要加载在虚拟机上才能运行,forName()方法就是加载类的,并且通过Class.forName("Package.A").newInstance()可以得到和new A()一样的效果,也就是说第一种方法就是把第二种方法分解成了两步,即先调用 forName 方法加载类,然后实例化,而第二种方法时把这两步合并了。

三、为什么有了new,还要使用Class.forName().newInstance()

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

Java中工厂模式经常使用newInstance()方法来创建对象。例如:

class c = Class.forName("Student");
factory = (StudentInterface)c.newInstance();

其中StudentInterface是Student的接口,上述代码可以写成如下形式:

String className = "Student";
class c = Class.forName("className");
factory = (StudentInterface)c.newInstance();

进一步可写成如下形式:

String className = readfromXMLConfig;
class c = Class.forName(className);
factory = (StudentInterface)c.newInstance();

这样,上述代码就不存在Student的类名了,无论你的className怎么变化,只要实现了StudentInterface,上述代码就可以运行。这就达到了解耦、可扩展性。

觉得不错的点个赞!!!~~~

 

参考文章:

https://blog.csdn.net/fengyuzhengfan/article/details/38086743
https://shuidexiongdi.iteye.com/blog/2065796
https://www.cnblogs.com/bokzmm/p/7545734.html
http://www.cnblogs.com/ityouknow/p/5603287.html

 

 

你可能感兴趣的:(java)