【问题标题】:Loading java class methods, docs not consistent with method's behavior加载 java 类方法,文档与方法的行为不一致
【发布时间】:2015-01-28 10:29:36
【问题描述】:

所以我在 jdk 7 上运行了这两个类:

abstract class Aclass
{
  public void foo()
  {
  }
  public void bar()
  {
  }
}

还有:

public class Bclass extends Aclass
{
  public void foo(Integer one)
  {
  }
  public void bar(String two)
  {
  }
}

我的目标是加载 Bclass,并且只加载 Bclass,打印出其声明的方法和那些声明的方法的参数。 这是我使用的代码:

public static void main(String[] args)
  {
    try
    {
      Class<?> clazz = Tester.class.getClassLoader().loadClass("full_path.Bclass");
      for (Method method : clazz.getDeclaredMethods())
      {
        System.out.println("Method name: " + method.getName() + " From class: " + method.getDeclaringClass().getCanonicalName() + " with declared methods:");// test
        for (Class<?> param : method.getParameterTypes())
        {
          System.out.println(param.getCanonicalName());
        }
      }
    }
    catch (ClassNotFoundException e)
    {
      e.printStackTrace();
    }
  }

运行此代码会产生以下输出:

Method name: foo From class: complete_path.Bclass with declared methods:
Method name: foo From class: complete_path.Bclass with declared methods:
java.lang.Integer
Method name: bar From class: complete_path.Bclass with declared methods:
Method name: bar From class: complete_path.Bclass with declared methods:
java.lang.String

但在方法 [getDeclaredMethods()] 的 javadoc 中,我看到 but excludes inherited methods ,根据我的测试,这似乎不是这种情况,该方法显然会在重载时加载继承的方法。 还是我做错了什么?

【问题讨论】:

  • getDeclaredMethiods() 的 Javadoc 声明它“排除了继承的方法”。因此,您声称获得的输出是不可能的。我注意到您发布的代码的格式不正确,这很奇怪。大概您没有运行您认为正在运行的代码。也许你叫getMethods()而不是getDeclaredMethods()
  • 嗨,不,这是我正在运行的确切代码,我唯一编辑的是用complete_path. 替换类的输出的完整路径,因为它暴露了太多信息。顺便说一句,你自己试过吗?
  • @JBoy,我在 Java 8 上试过,但输出不同。我只得到getDeclaredMethods() JavaDoc 中定义的子类方法。你确定这是一个完整的例子吗?
  • @EJP 我尝试了相同的代码,但在 java 1.7 上出现了这种情况。显然在 java 8 和 1.5 上(进一步阅读有人尝试过),实现不同。
  • @wassgren 显然这只发生在 java 7 上

标签: java reflection methods


【解决方案1】:

我的目标是加载 Bclass,并且只加载 Bclass ...

这是不可能的。

JVM 规范 (Chapter 5) 非常详细地解释了加载类时必须发生的事情。必须发生的一件事是解决对直接超类和接口的引用。这需要加载相应的类/接口。

如果(由于某种原因)无法加载超类或接口,则子类的加载将失败。


加载java类方法,文档与方法行为不一致

getDeclaredMethods() 的意外行为是另一个问题。这与类加载无关。

根据这个 Q&A - Problem in the GetDeclaredMethods (java) - 您会看到已添加到 Bclass 的合成“桥”方法,而不是从 Aclass 继承的方法。

Java 教程here 中描述了桥接方法。

您可以通过使用javap Bclass 查看Bclass.class 中的代码来确认这一点。在输出中查看那些额外的桥接方法。

[steve@newbox tmp]$ javap Bclass
Compiled from "Bclass.java"
public class Bclass extends Aclass {
  public Bclass();
  public void foo(java.lang.Integer);
  public void bar(java.lang.String);
  public void bar();
  public void foo();
}

可以通过为每个 Method 对象打印 method.isBridge() 来进一步确认(如果需要)。

现在我不明白为什么这个代码需要桥接方法......但这就是它们。


总之:您看到的getDeclaredMethods 的行为与javadocs 一致。您所看到的是最近 Java 实现的鲜为人知(但已记录!)方面的结果。

【讨论】:

  • 为什么相关?所有类加载器都会加载超类。加载类的核心机制是在 JVM 深处实现的,它不能被颠覆……除非你修改 JVM 本身。然后你就会有一些不再实现 Java(tm) 的东西。
  • 我同意你的说法,但这应该在 javadocs 中提及,而不是在 jvm 规范中,因为这将由 java users 使用,而不是由 jvm 的实现者使用。来自 loadClass():Loads the class with the specified binary name,来自 getDeclaredMethods():Returns an array of Method objects reflecting all the methods declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private methods, but excludes inherited methods
  • 顺便说一句,我仍然找不到一个部分来解释在加载类时从超类到子类的“方法合并”,以及为什么这种情况只发生在重载方法中
  • @JBoy - 这不是正在发生的事情。请参阅我的更新答案。
【解决方案2】:

这很奇怪
我想我没有做错任何事,但我运行了你的程序,它只打印Bclass 中的方法。我刚刚更改了包并将AclassBclass 放在同一个文件中,因为只有Bclasspublic。输出:

Method name: foo From class: test2.Bclass with declared methods:
java.lang.Integer
Method name: bar From class: test2.Bclass with declared methods:
java.lang.String
test2.Bclass
test2.Aclass

// Also added  at the end
//   System.out.println(clazz.getCanonicalName());
//   System.out.println(clazz.getSuperclass().getCanonicalName());
// to see if I typed something wrong. 

使用:Sun JDK 1.5.0_22 编译和运行。

【讨论】:

  • 嗨,谢谢你的尝试,所以我想你让 Bclass 成为 Aclass 的内部类。
  • 是:两个文件,Bclass.java 包含public Bclassabstract Aclass,而Tester.java 仅包含main()
  • 但是在单独的文件中运行它时你会遇到同样的问题吗?我的项目的实际结构要求它位于单独的文件中。
  • 在两个文件(一个用于main,另一个用于AclassBlcass)或使用三个文件(每个类在其文件中)运行它没有区别。相同的输出,它只打印Bclass 方法。你的 JRE 是什么?
  • 我没有安装 jre7。我将三个文件上传到 pastebin。首先,检查它们(它们没问题),有人可以下载并在自己的系统中尝试。 Aclass:pastebin.com/UpTFEQ7w,Bclass:pastebin.com/H1vfgnhy,Tester:pastebin.com/d0s9LdEA
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-17
  • 2011-09-07
  • 1970-01-01
  • 2012-04-13
  • 2018-09-13
  • 2010-11-08
  • 2015-07-30
相关资源
最近更新 更多