# JarClassLoader 类

> 原文： [https://docs.oracle.com/javase/tutorial/deployment/jar/jarclassloader.html](https://docs.oracle.com/javase/tutorial/deployment/jar/jarclassloader.html)

`JarClassLoader` 类扩展了 `java.net.URLClassLoader` 。顾名思义， `URLClassLoader` 旨在用于加载通过搜索一组 URL 访问的类和资源。 URL 可以引用目录或 JAR 文件。

除了子类化 `URLClassLoader` 之外， `JarClassLoader` 还利用了其他两个与 JAR 相关的新 API， `java.util.jar` 包和 `] java.net.JarURLConnection` 类。在本节中，我们将详细介绍 `JarClassLoader` 的构造函数和两个方法。

## JarClassLoader 构造函数

构造函数将 `java.net.URL` 的实例作为参数。传递给此构造函数的 URL 将在 `JarClassLoader` 的其他位置使用，以查找要从中加载类的 JAR 文件。

```
public JarClassLoader(URL url) {
    super(new URL[] { url });
    this.url = url;
}

```

`URL` 对象被传递给超类的构造函数 `URLClassLoader` ，它采用 `URL []` 数组，而不是单个 `URL` ]实例，作为一个论点。

## getMainClassName 方法

一旦用包含 JAR 的应用程序的 URL 构造 `JarClassLoader` 对象，就需要一种方法来确定 JAR 文件中哪个类是应用程序的入口点。这是 `getMainClassName` 方法的工作：

```
public String getMainClassName() throws IOException {
    URL u = new URL("jar", "", url + "!/");
    JarURLConnection uc = (JarURLConnection)u.openConnection();
    Attributes attr = uc.getMainAttributes();
    return attr != null
                   ? attr.getValue(Attributes.Name.MAIN_CLASS)
                   : null;
}

```

您可以从[上一课](run.html)中回忆起 JAR 捆绑应用程序的入口点是由 JAR 文件清单的 `Main-Class` 标头指定的。要了解 `getMainClassName` 如何访问 `Main-Class` 标头值，让我们详细查看该方法，特别注意它使用的新 JAR 处理功能：

## JarURLConnection 类和 JAR URL

`getMainClassName` 方法使用 `java.net.JarURLConnection` 类指定的 JAR URL 格式。 JAR 文件的 URL 语法如下例所示：

```
jar:http://www.example.com/jarfile.jar!/

```

终止`！/` 分隔符表示 URL 引用整个 JAR 文件。分隔符后面的任何内容都引用特定的 JAR 文件内容，如下例所示：

```
jar:http://www.example.com/jarfile.jar!/mypackage/myclass.class

```

`getMainClassName` 方法的第一行是：

```
URL u = new URL("jar", "", url + "!/");

```

此语句构造一个表示 JAR URL 的新 `URL` 对象，将`！/` 分隔符附加到用于创建 `JarClassLoader` 实例的 URL。

## java.net.JarURLConnection 类

此类表示应用程序和 JAR 文件之间的通信链接。它具有访问 JAR 文件清单的方法。 `getMainClassName` 的第二行是：

```
JarURLConnection uc = (JarURLConnection)u.openConnection();

```

在此语句中，在第一行中创建的 `URL` 实例打开 `URLConnection` 。 `URLConnection` 实例然后被转换为 `JarURLConnection` ，因此它可以利用 `JarURLConnection` 的 JAR 处理功能。

## 获取清单属性：java.util.jar.Attributes

使用 `JarURLConnection` 打开 JAR 文件，您可以使用 `JarURLConnection` 的 `getMainAttributes` 方法访问 JAR 文件清单中的标头信息。此方法返回 `java.util.jar.Attributes` 的实例，该实例将 JAR 文件清单中的标题名称与其关联的字符串值进行映射。 `getMainClassName` 中的第三行创建`属性`对象：

```
Attributes attr = uc.getMainAttributes();

```

要获取清单的 `Main-Class` 标头的值， `getMainClassName` 的第四行调用 `Attributes.getValue` 方法：

```
return attr != null
               ? attr.getValue(Attributes.Name.MAIN_CLASS)
               : null;

```

方法的参数 `Attributes.Name.MAIN_CLASS` 指定它是您想要的 `Main-Class` 标头的值。 （ `Attributes.Name` 类还提供静态字段，如 `MANIFEST_VERSION` ， `CLASS_PATH` 和 `SEALED` ，用于指定其他标准清单标题。）

## invokeClass 方法

我们已经看到 `JarURLClassLoader` 如何识别 JAR 捆绑应用程序中的主类。最后一个要考虑的方法是 `JarURLClassLoader.invokeClass` ，它可以调用主类来启动 JAR 捆绑的应用程序：

```
public void invokeClass(String name, String[] args)
    throws ClassNotFoundException,
           NoSuchMethodException,
           InvocationTargetException
{
    Class c = loadClass(name);
    Method m = c.getMethod("main", new Class[] { args.getClass() });
    m.setAccessible(true);
    int mods = m.getModifiers();
    if (m.getReturnType() != void.class || !Modifier.isStatic(mods) ||
        !Modifier.isPublic(mods)) {
        throw new NoSuchMethodException("main");
    }
    try {
        m.invoke(null, new Object[] { args });
    } catch (IllegalAccessException e) {
        // This should not happen, as we have disabled access checks
    }
}

```

`invokeClass` 方法有两个参数：应用程序入口点类的名称和传递给入口点类 `main` 方法的字符串参数数组。首先，加载主类：

```
Class c = loadClass(name);

```

`loadClass` 方法继承自 `java.lang.ClassLoader` 。

加载主类后， `java.lang.reflect` 包的反射 API 用于将参数传递给类并启动它。您可以参考 [The Reflection API](../../reflect/index.html) 上的教程来查看反射。