如何找到给定类名的包名?

Posted

技术标签:

【中文标题】如何找到给定类名的包名?【英文标题】: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 感谢您的回复。 ;) 【参考方案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数组中不会有条目。

以上是关于如何找到给定类名的包名?的主要内容,如果未能解决你的问题,请参考以下文章

同一个项目中存在完全相同的包名和类名如何解决调用问题

如何在Java中获取当前类名,包括包名?

android 获取当前类的包名与类名

android 获取当前类的包名与类名

android 获取当前类的包名与类名

如何获取到app的包名