Class的forName方法与ClassLoader的loadClass方法的区别

首先,先要了解整个JVM加载类的流程,可以查看我的另一篇博客:点击打开链接

双亲委派模型:


当一个类加载器接收到类加载请求时,会先将请求传给父加载器,父加载器又会传给自己的父加载器,最终传给启动类加载器,当父加载器无法加载类时(在自己的搜索路径中找不到),子类加载器才会开始加载类。


forName方法

forName函数有两种形式:

public static Class<?> forName(String className) throws ClassNotFoundException     
   
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException //允许指定是否初始化,并且指定类加载器 ,若ClassLoader为null,则表示使用启动类加载器

若采用第一种形式,则默认为执行初始化,类加载器应该是从System Class Loader(JVM实现者提供的用户自定义类加载器,在编程者不指定类加载器情况下默认装载用户类,可以通过ClassLoader.getSystemClassLoader()获取)开始,满足双亲委派模型的工作形式,我们来看看下面的代码:

package pkgtry;
class A
{
  static 
  {
      System.out.println("加载类A");
  }
}
public class Try {
    public static void main(String[] args) {
        try
        {
            Class.forName("pkgtry.A");//参数为类的全限定名,即包名+类名
        }
        catch(ClassNotFoundException e)
        {
            System.out.println("未找到类");
        }
    } 
}

执行结果为:





若我们采用第二种形式,并且将第二个参数设为false,则不会进行初始化,但其他类加载操作还是会进行,来看看下面的代码:

package pkgtry;
class A
{
  static 
  {
      System.out.println("加载类A");
  }
}
public class Try {
    public static void main(String[] args) {
        try
        {
            Class.forName("pkgtry.A",false,ClassLoader.getSystemClassLoader());
        }
        catch(ClassNotFoundException e)
        {
            System.out.println("未找到类");
        }
    } 
}
输出结果为:

没有抛出异常,说明加载成功。

若将第二个参数改为true,输出结果为:



loadClass方法

loadClass函数有两种形式:

protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException //第二个参数表示是否在转载完后进行验证、准备、解析。          
public Class<?> loadClass(String name) throws ClassNotFoundException    

loadClass不是静态方法,而ClassLoader是虚基类,所以只能继承ClassLoader后进行调用,先来调用第二种形式:

package pkgtry;
class A
{
  static 
  {
      System.out.println("加载类A");
  }
}
public class Try {
    public static void main(String[] args) {
        try
        {
            ClassLoader.getSystemClassLoader().loadClass("pkgtry.A");//相当于ClassLoader.getSystemClassLoader().loadClass("pkgtry.A",false);getSystemClassLoader返回值类型继承自ClassLoader
        }
        catch(ClassNotFoundException e)
        {
            System.out.println("未找到类");
        }
    } 
}
结果为:

什么都没有,这是因为loadClass默认情况只是加载类,不会进行加载类后的一些列动作。

接下来调用第一种方式,并将第二个参数指定为true,结果还是一样没有输出,这是因为loadClass不会执行初始化操作,第二个

参数指定为true只会执行类加载后的验证、准备、解析步骤。


两者的使用场景

以下为个人观点

两个函数均可指定类加载器,forName的第二种形式可以指定类加载器,loadClass通过调用者指定类加载器,loadClass可以指定只加载类,类的验证、准备、初始化在第一次使用该类时进行,有些类完成整个装载或初始化的时间比较长,若不急着用该类,可以使用该方法节省时间,forName相较于loadClass,除了加载类,一定会进行验证、准备、解析,我们可以指定是否初始化,当我们急着使用类时,就使用该方法。


接下来给出个双亲委派模型工作的例子:

ClassLoader.getSystemClassLoader()获取的类加载器可以加载用户自定义的类,若我们获取该类加载器后加载java.lang.String,看会不会出现异常,若没有出现异常,说明String类被父类加载器加载,即满足双亲委派的工作方式,否则,会出现异常。

public class Try {
    public static void main(String[] args) {
        try
        {
            ClassLoader.getSystemClassLoader().loadClass("java.lang.String");
        }
        catch(ClassNotFoundException e)
        {
            System.out.println("未找到类");
        }
    } 
}

运行结果如下:


无异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值