个性化阅读
专注于IT技术分析

Java中的ClassLoader

本文概述

Java ClassLoader

Java ClassLoader是一个抽象类。它属于java.lang包。它从不同的资源加载类。 Java ClassLoader用于在运行时加载类。换句话说, JVM在运行时执行链接过程。根据需要将类加载到JVM中。如果已加载的类依赖于另一个类, 则也将加载该类。当我们请求加载一个类时, 它将类委托给它的父类。这样, 可以在运行时环境中维护唯一性。执行Java程序至关重要。

Java中的ClassLoader

Java ClassLoader基于三个原则:委派, 可见性和唯一性。

  • 委托原则:将类加载请求转发给父类加载器。仅当父级找不到或加载该类时, 才加载该类。
  • 可见性原则:它允许子类加载器查看父ClassLoader加载的所有类。但是父类加载器看不到子类加载器加载的类。
  • 唯一性原则:它允许一次加载一个类。它是通过委派原理实现的。它确保子ClassLoader不会重新加载已由父级加载的类。

ClassLoader的类型

在Java中, 每个ClassLoader在加载类文件的位置都有一个预定义的位置。 Java中有以下几种类型的ClassLoader:

Bootstrap类加载器:它从rt.jar和其他核心类加载标准JDK类文件。它是所有类加载器的父级。它没有任何父母。当我们调用String.class.getClassLoader()时, 它返回null, 并且基于它的任何代码都将引发NullPointerException。它也称为Primordial ClassLoader。它从jre / lib / rt.jar加载类文件。例如, java.lang包类。

扩展类加载器:它将类加载请求委托给其父级。如果无法成功加载类, 那么它将从jre / lib / ext目录或其他任何目录(作为java.ext.dirs)加载类。它由JVM中的sun.misc.Launcher $ ExtClassLoader实现。

系统类加载器:它从CLASSPATH环境变量加载应用程序特定的类。可以在使用-cp或classpath命令行选项调用程序时进行设置。它是Extension ClassLoader的子级。它由sun.misc.Launcher $ AppClassLoader类实现。所有Java ClassLoader都实现java.lang.ClassLoader。

Java中的ClassLoader

Java中ClassLoader的工作方式

当JVM请求一个类时, 它通过传递类的完全分类名称来调用java.lang.ClassLoader类的loadClass()方法。 loadClass()方法调用findLoadedClass()方法来检查该类是否已加载。需要避免多次加载该类。

如果已经加载了该类, 则它将请求委派给父ClassLoader以加载该类。如果ClassLoader没有找到该类, 它将调用findClass()方法在文件系统中查找这些类。下图显示了ClassLoader如何使用委托在Java中加载类。

Java中的ClassLoader

假设我们有一个特定于应用程序的类Demo.class。加载此类文件的请求将传输到Application ClassLoader。它委托其父扩展ClassLoader。此外, 它委托给Bootstrap ClassLoader。引导程序在rt.jar中搜索该类, 因为该类不存在。现在, 请求将其传输到Extension ClassLoader, 后者将搜索目录jre / lib / ext并尝试在此处找到该类。如果在那里找到该类, 则Extension ClassLoader会加载该类。应用程序ClassLoader从不加载该类。当扩展ClassLoader没有加载时, Application ClaasLoader从Java中的CLASSPATH加载它。

可见性原则指出, 子ClassLoader可以看到父ClassLoader加载的类, 反之亦然。这意味着如果Application ClassLoader加载了Demo.class, 在这种情况下, 尝试使用Extension ClassLoader显式加载Demo.class会抛出java.lang.ClassNotFoundException。

根据唯一性原则, 父级加载的类不应再由Child ClassLoader加载。因此, 可以编写违反委托和唯一性原则并自行加载类的类加载器。

简而言之, 类加载器遵循以下规则:

  • 它检查该类是否已经加载。
  • 如果未加载该类, 请要求父类加载器加载该类。
  • 如果父类加载器无法加载类, 请尝试在该类加载器中加载它。

考虑以下示例:

public class Demo
{
public static void main(String args[]) 
{
System.out.println("How are you?");
}
}

使用以下命令编译并运行以上代码:

javac Demo.java
java -verbose:class Demo

-verbose:class:用于显示有关JVM正在加载的类的信息。当使用类加载器动态加载类时, 这很有用。下图显示了输出。

Java中的ClassLoader

我们可以看到, 应用程序类(Demo)所需的运行时类首先被加载。

加载类时

只有两种情况:

  • 当执行新的字节码时。
  • 当字节码静态引用一个类时。例如, System.out。

静态与动态类加载

使用“ new”运算符静态加载类。动态类加载通过使用Class.forName()方法在运行时调用类加载器的功能。

loadClass()和Class.forName()之间的区别

loadClass()方法仅加载类, 但不初始化对象。而Class.forName()方法在加载对象后对其进行初始化。例如, 如果你使用ClassLoader.loadClass()加载JDBC驱动程序, 则类加载器不允许加载JDBC驱动程序。

java.lang.Class.forName()方法返回与给定字符串名称的类或接口耦合的Class Object。如果找不到该类, 则抛出ClassNotFoundException。

在此示例中, 将加载java.lang.String类。它打印类名称, 程序包名称以及String类的所有可用方法的名称。在下面的示例中, 我们使用Class.forName()。

Class <?>:表示可以是任何类型的Class对象(?是通配符)。 Class类型包含有关类的元信息。例如, String.class的类型为Class <String>。如果要建模的类未知, 请使用Class <?>。

getDeclaredMethod():返回一个数组, 其中包含Method对象, 这些对象反映了此Class对象表示的类或接口的所有已声明方法, 包括公共, 受保护, 默认(程序包)访问和私有方法, 但不包括继承的方法。

getName():以String形式返回此Method对象表示的方法名称。

import java.lang.reflect.Method;
public class ClassForNameExample 
{
public static void main(String[] args)
{
try 
{
Class<?> cls = Class.forName("java.lang.String");
System.out.println("Class Name: " + cls.getName());
System.out.println("Package Name: " + cls.getPackage());
Method[] methods = cls.getDeclaredMethods();
System.out.println("-----Methods of String class -------------");
for (Method method : methods) 
{
System.out.println(method.getName());
}
}
catch (ClassNotFoundException e) 
{
e.printStackTrace();
}
}
}

输出量

Class Name: java.lang.String
Package Name: package java.lang
-----Methods of String class -------------
value
coder
equals
length
toString
hashCode
getChars
------
------
------
intern
isLatin1
checkOffset
checkBoundsOffCount
checkBoundsBeginEnd
access$100
access$200

赞(0)
未经允许不得转载:srcmini » Java中的ClassLoader

评论 抢沙发

评论前必须登录!