【问题标题】:How to find the package name given a class name?如何找到给定类名的包名?
【发布时间】:2012-02-03 07:18:49
【问题描述】:

给定一个类名作为字符串,我如何在运行时获取它的包名?我没有包名+类名的完全限定名。只是类名。

我希望在Class.forName() 方法中使用包名。

我可以找到第一个匹配的包名(如果多个包具有相同的类)。

有什么想法吗?

更新

我没有要处理的 Class 实例。我的要求是使用Class.forName() 方法创建一个类。但我只是将类名作为字符串。我需要一些方法来遍历包并确定我拥有的类是否属于包。

异常的堆栈跟踪是

Exception in thread "main" java.lang.ClassNotFoundException: MyAddressBookPage
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:169)

【问题讨论】:

  • 您不需要 Class.forName 的类实例来工作。阅读我的答案中的 cmets
  • 你为什么要投票给所有人?我们只是想提供帮助。如果每个人都给出了相似的答案,那么这意味着您无法很好地描述您的问题,并且首先是您的错。..
  • 您在类名与完全限定类名方面的意思的示例可能有助于消除混淆,例如“我只有‘Baz’,没有‘com.foo.bar.Baz’。
  • @Cemre,不是他,是我。我投票给他们是因为他们没有回答他的问题。不要把它当作个人! :) 一旦将它们编辑为有用的答案,我将返回并删除不再适用的反对票。但目前,它们只是不好的答案。这就是我们从没有答案到网络上最佳答案的旅程开始的方式。再说一次,不要把它当作个人。
  • 抱歉造成误会。看到我已经很低的声誉下降有点吓人。 @Grundlefleck 感谢您的回复。 ;)

标签: java class reflection package


【解决方案1】:

在类上调用 getPackageName()。它返回完全限定的包名称。此外,值得一提的是,这是 Java 9 的一项功能。

【讨论】:

    【解决方案2】:

    最简单的解决方案之一:

    Test.class.toString().replace("class", "").replace(".","/").replace(" ", "")
    

    【讨论】:

    • 这不能回答问题。给定一个带有类名的字符串,例如"ArrayList",您需要找到包名,例如"java.util"
    【解决方案3】:

    唯一的方法是导航类路径中的目录/jar 和一个带有类名的条目。

    这里有一些代码几乎可以满足您的需求。此代码是我拥有的一个类的一部分,该类搜索用于创建接口实例的实现/工厂方法。抱歉,没有时间更改它以查找命名类,应该很容易更改,并且正如我在其他地方提到的,您不需要加载类,只需检查名称即可。

    public Class<?> locateImplementation(Class<?> type) {
         Class<?> c = null;
         String[] cp = System.getProperty("java.class.path").split(File.pathSeparator);
    
         for (int i = 0; (c == null) && (i < cp.length); ++i) {
             File f = new File(cp[i]);
             if (f.exists() && f.canRead()) {
                 if (isJar(f)) {
                     try {
                         c = searchJar(type, new FileInputStream(f));
                     } catch (Throwable t) {
                         // Nothing to worry about
                     }
                 } else {
                     c = searchFile(type, f);
                 }
             }
         }
    
         return c;
     }
    
    private boolean isClass(String path) {
        return path.matches(".+\\.class$") && !path.contains("$");
    }
    
    private Class<?> searchFile(Class<?> type, File f) {
        return searchFile(type, f, f.getPath());
    }
    
    private Class<?> searchFile(Class<?> type, File f, String root) {
        Class<?> implementation = null;
    
        if (f.isDirectory()) {
            File[] files = f.listFiles();
            for (int i = 0; i < files.length; i++) {
                implementation = searchFile(type, files[i], root);
                if (implementation != null) {
                    break;
                }
            }
        } else if (isClass(f.getPath())) {
            String path = f.getPath().substring(root.length() + 1);
            Class<?> c = getClass(path);
            if ((c != null) && !c.isInterface() &&
                    type.isAssignableFrom(c)) {
                implementation = c;
            }
        }
        return implementation;
    }
    
    private Class<?> getClass(String name) {
        Class<?> c;
        String className = name.replaceAll("[/\\\\]", ".")
            .replaceFirst("^\\.", "").replace(".class", "");
        try {
            c = Class.forName(className);
        } catch (Throwable e) {
            c = null;
        }
    
        return c;
    }
    
    
    private Class<?> searchJar(Class<?> type, InputStream in)
            throws Exception {
        ZipInputStream zin = new ZipInputStream(in);
        Class<?> implementation = null;
    
        ZipEntry ze;
        while ((implementation == null)
                && ((ze = zin.getNextEntry()) != null)) {
            String name = ze.getName();
            if (name.endsWith("class")
                    && name.matches("^com.xxx.+")
                    && !name.contains("$")) {
                try {
                    Class<?> c = getClass(name);
                    if ((c != null) && !c.isInterface()
                            && type.isAssignableFrom(c)) {
                        implementation = c;
                    }
                } catch (Throwable t) {
                    // Nothing to worry about
                }
            }
        }
    
        return implementation;
    }
    
    private boolean isJar(File f) {
        return f.getPath().endsWith(".jar");
    }
    

    【讨论】:

    • 所以我将不得不使用 I/O 来查找目录中的文件?没有办法使用反射来搜索包?
    • 别这么想,你要找的类可能还没有加载,所以不可用。
    【解决方案4】:

    这很可能是一种非常低效、臃肿、不便的方式来完成您想要实现的目标,希望已经有一种开箱即用的单一方式来实现它......但它应该可以工作.

    基本上,扫描类路径中的每个类,直到找到getSimpleName() 与您拥有的类名匹配的类。

    我建议查看 Google Classpath Explorer 以帮助管理执行此操作的具体细节。

    它可能看起来像这样:

     ClassPath classpath = new ClassPathFactory().createFromJVM();
     RegExpResourceFilter regExpResourceFilter = new RegExpResourceFilter(".*", ".*\\.class");
     String[] resources = classpath.findResources("", regExpResourceFilter);
    

    resources 是一个字符串数组,例如 'com/foo/bar/Baz.class'。您现在可以简单地循环并找到匹配的条目,并将它们从斜线转换为点线,去掉“.class”等。在尝试匹配内部类时要小心,因为它们中会有一个“$”字符。

    此外,据我所知,这不会导致这些类被加载。

    【讨论】:

    • 我会小心加载每个类,除了增加加载的类的数量之外,任何静态初始化代码都会被执行,包括你不想要的代码,例如尝试在 Windows 上做 XWindows 特定的东西。如果您有一些先验知识,您可以随时限制搜索
    • 只要它有效,我不担心性能。这不是一个关键项目。 Google Classpath Explorer 似乎可以完成这项工作:)
    • 这看起来很棒,但是当我加载所有内容并运行它时,应用程序挂起。 findResources() 需要 rootPackageName 但当我在其中加载任何内容(即“com.foo.bar”或“com/foo/bar”)时,代码会执行但resources 会返回空。知道这里会发生什么吗?
    • rootPackageName 的格式为“com/foo/bar”。我仍然无法让它在我的 Android 应用程序中工作,但能够让它在常规 Java 应用程序中正常工作。我想知道是否有一些关于 Android 的东西阻止了它的工作。有什么想法吗?
    【解决方案5】:
     final Package[] packages = Package.getPackages();
        final String className = "ArrayList";
    
        for (final Package p : packages) {
            final String pack = p.getName();
            final String tentative = pack + "." + className;
            try {
                Class.forName(tentative);
            } catch (final ClassNotFoundException e) {
                continue;
            }
            System.out.println(pack);
            break;
        }
    

    【讨论】:

    • 这是一个很好的答案,但有一个警告:AFAICT 仅在该包中的至少一个类已经加载时才有效。否则packages数组中不会有条目。
    猜你喜欢
    • 2015-08-23
    • 2011-04-21
    • 2017-05-27
    • 2010-12-02
    • 1970-01-01
    • 2021-08-21
    • 2014-10-16
    • 1970-01-01
    相关资源
    最近更新 更多