13

我有一个 java 项目,它使用两个具有相同类 ( com.sun.mail.imap.IMAPFolder) 的导入 jar。有没有办法在导入类时明确说明要使用哪个 jar?使用:

import com.sun.mail.imap.IMAPFolder; 

似乎按构建路径顺序使用该类,但由于某种原因似乎并非如此

Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
Caused by: java.lang.NoSuchMethodError: com.sun.mail.imap.IMAPFolder.idle()V
        at com.woodbury.GCM.HelperGmailMonitor.doEmail(HelperGmailMonitor.java:104)
        at com.woodbury.GCM.Launch.listen(Launch.java:16)
        at com.woodbury.GCM.Launch.main(Launch.java:10)
        ... 5 more

在运行时。我正在eclipse中构建项目。

4

5 回答 5

30

当一个类被加载时,与请求的完全限定名称相匹配的第一个实现对相关对象可见ClassLoader是返回的。具有相同完全限定名称的任何其他实现都被有效地隐藏了ClassLoader

这在标准 Java SE 应用程序中意味着,在类路径中列出的第一个代码库(即 jar)与所需的类一起提供,并且所有其他代码库的同一完全限定类的实现都被隐藏了。

例子:

假设 A.jar 包含已编译的类

package com.stackoverflow.example;
public class Hello {
     public static String getGreeting(){
         return "Hello, A!"
     }
}

假设 B.jar 包含已编译的类

package com.stackoverflow.example
public class Hello {
     public static String getGreeting(){
         return "Hello, B!"
     }
}

请注意,在上述两个类中都具有相同的完全限定名称。

假设主类是

   import com.stackoverflow.example.Hello;

   public class ExampleMain {
       public static void main(String[] args){
            System.out.println(Hello.getGreeting());
       }
   }

如果我要调用我的程序

java -cp A.jar:B.jar ExampleMain

输出是:Hello, A!

如果我像这样反转类路径

java -cp B.jar:A.jar ExampleMain

输出是:Hello, B!

于 2013-10-12T23:41:06.543 回答
11

你不能只在你的 Java 源代码中做你所要求的。Java 不是为此而设计的。

这是一种糟糕的情况,只能使用自定义类加载器可靠地处理,每个加载器都提供您需要的一个 jar。由于您首先要问这个问题,因此这可能不是您应该采用的方式,因为这会带来很多新的耗时问题。

我强烈建议你找出为什么你的类路径中有同一个 jar 的两个不同版本,并重新编写你的程序,这样你只需要一个版本。

于 2013-10-13T08:35:45.847 回答
0

是的,有办法解决这个问题。在我的场景中,我有两个具有相同名称和相同路径的类,而 Eclipse 总是导入错误的类。我所做的是更改构建路径中的 jar 顺序,eclipse 将选择构建路径中的第一个。

于 2014-10-17T18:30:12.973 回答
0

如果您使用的是 IDE,则可以设置将文件导出到类加载器的顺序。

我在eclipse上工作,我使用maven。当我使用 maven 安装项目时,它产生了许多额外的 jars(我没有在我的依赖项中定义),并且有一个文件org.w3c.dom.Element存在于 2 个 jar 文件中,并且同一文件的第三个实例也在 JRE7 中。

为了确保选择正确的文件,我所要做的就是转到Java Build Path -> Order and Export。选择我希望类加载器给予更多偏好的 Jar 文件,然后使用“向上”按钮将其向上移动。

这就是它的外观。 Eclipse - Java 构建路径 -> 排序和导出 请注意,此图像适用于 Eclipse。但是对于其他 IDE,肯定会有类似的方法来解决这个问题。

于 2016-04-21T14:22:54.580 回答
-1

1) 一般来说:是的,您可以在不同的 .jar 文件中拥有相同的类:您只需使用完全限定的包名称来消除它们的歧义。“日期”类(存在于 java.util 和 java.sql 中)就是一个很好的例子。

2)如果你有两个不同的.jar 文件,它们具有相同的完全限定的包名......很可能,你有一个冲突。即使您可以通过使用类加载器来绕过 InvocationTargetException,您仍然可能会遇到其他问题。在这种情况下,听起来您的两个 .jar 文件可能有两个不同的 JavaMail API 实现。我不知道。

3) 最安全的选择是满足所有程序的引用而不冒冲突的风险。我相信如果您从 Oracle 的 JavaMail 网页中获取“官方”.jar,您可以这样做:

https://java.net/projects/javamail/pages/Home

'希望有帮助!

于 2013-10-13T02:44:05.487 回答