如果只有一个完全限定的名称,如何获取java类的二进制名称?

reflection类和方法以及类加载器等需要使用所谓的“二进制”类名。

问题是,如果一个人只有一个完全限定的名称,即一个人在源代码中使用的名称,那么如何获得二进制名称。

例如:

package frege; public static class RT { .... public static class X { .... } } 

该类的完全限定名称为frege.RT.X 。 然而,要获得类对象,需要编写:

 Class.forName("frege.RT$X") 

并不是

 Class.forName("frege.RT.X") // fails with ClassNotFoundException 

因为X恰好是frege.RT的内部类。

一种可能但又笨拙的解决方案是替换. 使用$来自后退,一个接一个,直到Class.forName()不再抛出ClassNotFoundException或者没有更多. 取代。

有没有更好/众所周知/标准的解决方案? 我查看了API文档中的ClassCLassLoaderjava.lang.reflect但没有找到任何可用的东西。

现在听起来你想从规范名称中获取完全限定名称(FQN)。 由于这与简单名称的工作不同,我将添加第二个答案。

如果会导致规范名称冲突,Sun javac命令将不会编译类。 但是,通过单独编译,您仍然可以获得具有相同规范名称的两个不同类。

一个例子:

文件src1 \ com \ stack \ Test.java

 package com.stack; public class Test { public static class Example { public static class Cow { public static class Hoof { } } } public static void main(String[] args) throws Exception { Class cl1 = Class.forName("com.stack.Test$Example$Cow$Hoof"); Class cl2 = Class.forName("com.stack.Test.Example.Cow.Hoof"); System.out.println(cl1.getName()); System.out.println(cl1.getSimpleName()); System.out.println(cl1.getCanonicalName()); System.out.println(); System.out.println(cl2.getName()); System.out.println(cl2.getSimpleName()); System.out.println(cl2.getCanonicalName()); } } 

文件src2 \ com \ stack \ Test \ Example \ Cow \ Hoof.java

 package com.stack.Test.Example.Cow; public class Hoof { } 

然后编译并执行:

 set CLASSPATH= mkdir bin1 bin2 javac -d bin1 -sourcepath src1 src1\com\stack\Test.java javac -d bin2 -sourcepath src2 src2\com\stack\Test\Example\Cow\Hoof.java set CLASSPATH=bin1;bin2 java com.stack.Test 

产生输出:

 com.stack.Test$Example$Cow$Hoof Hoof com.stack.Test.Example.Cow.Hoof com.stack.Test.Example.Cow.Hoof Hoof com.stack.Test.Example.Cow.Hoof 

因此,两个类具有相同的规范名称但具有不同的FQN。 即使两个类具有相同的FQN和相同的规范名称,如果它们通过不同的类加载器加载,它们仍然可以是不同的。

要解决您的问题,我会看到您可以采取的几种方法。

首先,您可以指定匹配具有最少嵌套量的类,从而指定FQN中“$”的最小数量。 更新事实certificate,Sun javac与此完全相反,并且匹配具有最多嵌套的类。

其次,您可以测试所有可能的FQN,并在有多个FQN时抛出exception。

第三,接受唯一的唯一映射是使用FQN,然后只在指定的类加载器中,并适当地重新处理应用程序。 我发现使用线程上下文类加载器作为默认的类加载器很方便。

一个简单的名称省略了很多信息,并且可以有许多具有相同简单名称的类。 这可能会使这变得不可能。 例如:

 package stack; /** * * @author Simon Greatrix */ public class TestLocal { public Object getObject1() { class Thing { public String toString() { return "I am a Thing"; } } return new Thing(); } public Object getObject2() { class Thing { public String toString() { return "I am another Thing"; } } return new Thing(); } public Object getObject3() { class Thing { public String toString() { return "I am a rather different Thing"; } } return new Thing(); } /** * @param args */ public static void main(String[] args) { TestLocal test = new TestLocal(); Object[] objects = new Object[] { test.getObject1(), test.getObject2(), test.getObject3() }; for(Object o : objects) { System.out.println("Object : "+o); System.out.println("Simple Name : "+o.getClass().getSimpleName()); System.out.println("Name : "+o.getClass().getName()); } } } 

这会产生输出:

 Object : I am a Thing Simple Name : Thing Name : stack.TestLocal$1Thing Object : I am another Thing Simple Name : Thing Name : stack.TestLocal$2Thing Object : I am a rather different Thing Simple Name : Thing Name : stack.TestLocal$3Thing 

如您所见,所有三个本地类都具有相同的简单名称。

我认为可以肯定的是,规范名称指定了唯一的类。 如上所述,javac不允许您使用单个编译单元创建具有相同规范名称的两个类。 如果您有2个编译,那么您可能会遇到关于加载哪个类的麻烦,但此时我会更担心库的包名与您的包名冲突,除了恶意之外的所有人都可以避免。

出于这个原因,我认为可以安全地假设你不会遇到那种情况。 ClassNotFoundException说一句,对于那些感兴趣的人,我实现了OP的建议(将$ s ClassNotFoundException ),并且只是在它找不到具有该规范名称的任何类的情况下抛出ClassNotFoundException ,或者如果它找到两个或更多具有该名称的人。

  /** * Returns the single class at the specified canonical name, or throws a {@link java.lang.ClassNotFoundException}. * * 

Read about the issues of fully-qualified class paths vs the canonical name string * discussed here. */ public static Class classForCanonicalName(String canonicalName) throws ClassNotFoundException { if (canonicalName == null) { throw new IllegalArgumentException("canonicalName"); } int lastDotIndex = canonicalName.length(); boolean hasMoreDots = true; String attemptedClassName = canonicalName; Set resolvedClasses = new HashSet<>(); while (hasMoreDots) try { Class resolvedClass = Class.forName(attemptedClassName); resolvedClasses.add(resolvedClass); } catch (ClassNotFoundException e) { continue; } finally { if(hasMoreDots){ lastDotIndex = attemptedClassName.lastIndexOf('.'); attemptedClassName = new StringBuilder(attemptedClassName) .replace(lastDotIndex, lastDotIndex + 1, "$") .toString(); hasMoreDots = attemptedClassName.contains("."); } } if (resolvedClasses.isEmpty()) { throw new ClassNotFoundException(canonicalName); } if (resolvedClasses.size() >= 2) { StringBuilder builder = new StringBuilder(); for (Class clazz : resolvedClasses) { builder.append("'").append(clazz.getName()).append("'"); builder.append(" in "); builder.append("'").append( clazz.getProtectionDomain().getCodeSource() != null ? clazz.getProtectionDomain().getCodeSource().getLocation() : "" ).append("'"); builder.append(System.lineSeparator()); } builder.replace(builder.length() - System.lineSeparator().length(), builder.length(), ""); throw new ClassNotFoundException( "found multiple classes with the same canonical names:" + System.lineSeparator() + builder.toString() ); } return resolvedClasses.iterator().next(); }

它仍然让我非常catch(NoClass) continue是,“预期”流程是为了catch(NoClass) continue代码,但是如果你曾告诉eclipse或者intelJ在抛出任何exception时自动中断,你会知道这种行为是课程的标准杆。