如何找到给定类名的包名?
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
数组中不会有条目。以上是关于如何找到给定类名的包名?的主要内容,如果未能解决你的问题,请参考以下文章