【问题标题】:Better to reference field instance or parameter in this constructor? [closed]更好地在此构造函数中引用字段实例或参数? [关闭]
【发布时间】:2021-06-16 13:45:41
【问题描述】:

我已经编写了这个“CoreException”Exception 子类。我似乎在构造函数中有三个选项,它们的行为都相同,因为它们都引用同一个对象。

*注意:问题是关于编译器和三个不同源代码选项可能的运行时差异。构造的 Object 可以是任何类。

public class CoreException extends Exception {

    private final Class<?> sourceClass;
    private final Method sourceMethod;

    public CoreException(@NotNull Method method, @NotNull Throwable thr) {
        super("this text is irrelevant", thr);

        this.setStackTrace(thr.getStackTrace());
        this.sourceMethod = method;
        this.sourceClass = this.sourceMethod.getDeclaringClass();
    }

    public Class<?> getSourceClass() { return sourceClass; }
    public Method getSourceMethod() { return sourceMethod; }
}

Class&lt;?&gt; sourceClass 对象的构造似乎有三个相同的选项:

this.sourceClass = this.sourceMethod.getDeclaringClass();
this.sourceClass = sourceMethod.getDeclaringClass();
this.sourceClass = method.getDeclaringClass();

是否有充分的理由使用其中一个而不是其他的,可能是因为性能、可靠性/弹性等方面的边际提升?

或者编译器只是简单地将所有这三个变成完全相同相同的结果?

【问题讨论】:

  • 它们都是同一个对象。
  • 是的。这就是为什么我想知道在这种情况下是否还有其他原因使用一种语法而不是另一种语法。也许对编译器/ jvm 有非常深刻理解的人可能会建议将削减几纳秒或其他东西。 :) (或者编译器只是最终编译成相同的最终状态?)
  • 因为我的意思是前两个不是对作为 arg 提供的对象实例的克隆的引用,第三个是对作为 arg 提供的特定对象实例的引用?
  • 作为第一步,您可以查看生成的字节码。即使你不明白其中的任何一个,你也应该能够看到是否有差异。

标签: java constructor compiler-optimization


【解决方案1】:

在示例中,this.sourceMethodsourceMethod 之间没有区别,生成的字节码将是相同的。这只是您喜欢的口味问题。有些人更喜欢总是使用this.,而另一些人则更喜欢只在需要消除歧义时才使用this.,如果还有一个同名的局部变量。

唯一真正的区别是[this.]sourceMethodmethodsourceMethod 是对象上的一个字段,而method 是一个参数。鉴于method 是一个参数,它在堆栈上,它可能比访问对象的字段sourceMethod 稍快。但是,从总体上看,这种差异可能可以忽略不计,即使不是,JIT 编译器也完全有可能以它们等效的方式对其进行优化。如果你真的需要知道,你应该写一个微基准来衡量这种差异。

就个人而言,我会考虑在使用sourceMethodmethod 之间进行选择,这主要是一种意见。

【讨论】:

  • 非常感谢!这有助于我更好地理解:)
【解决方案2】:

这是个坏主意。您正在尝试通过 API 更新来解决您对 Java 语言感到不舒服的问题。这是行不通的 - 绝大多数异常都不会被包装在 CoreException 中(例如,Java 核心 API 本身抛出的任何东西,或任何第三方库中的任何东西,如 JUnit、JDBI 等)。

您的代码将不再对其他不希望通过您的此类重新路由所有异常的 Java 程序员有意义。

您也无法编写“适合”现有 API(即接口的实现)的代码,因为您需要编写自己的所有相关异常类型的变体(因为它们需要扩展 @ 987654321@ 在层次结构中的某处,java.* 中的异常和任何用 jdbi.* 编写的东西都不会,如果不分叉你使用的每个库,你就无法更新它们。

那我该怎么办?

您粘贴在该异常消息中的信息已经在普通 jane 异常中可用 - 堆栈跟踪的第一行。在消息中重复这些信息是愚蠢的。

如果您有一些日志记录或错误报告系统,但您目前没有此信息,但您希望它有,这是一个真正的问题。您刚刚决定以一种笨拙且不明智的方式解决它。

相反,更新系统可能包含堆栈跟踪的第一行。这通常并不难,但这取决于让你去的地方:“Oof,我真的可以在这里使用方法名称”。

请注意,走自己的路不仅“它在现有的 API 中脱颖而出,就像拇指酸痛一样”,还有更多的缺点。 IDE 将无法识别这一点,您将无法单击消息中的类+方法名来自动跳转到正确的文件。如果需要,您需要匹配 StackTraceElement 打印的“样式”。

您的代码也会导致直接错误的结论。当从 lambda 中抛出异常时,这种方法(具有代表源的 Method 对象)没有多大意义。

因此,中止计划。无论您想通过使 Method sourceMethod 成为所有异常状态的一部分来完成任何事情 - 您要么不希望这样做,要么您可以以不同的方式更好地完成它(并且可能依靠 getStackTrace()[0]) 来传达它。

public Class<?> getSourceClass() {
   return Class.forName(getStackTrace()[0].getClassName());
}

也可以完成这项工作,例如,尽管请注意此代码可能会失败(抛出 ClassNotFoundEx),具体取决于异常的来源。并非所有代码都可以在“它在这个类和这个方法”上下文中轻松捕获(核心内容、本机内容、合成方法、桥接器、动态生成的代码、lambda...)

【讨论】:

  • 这个自定义异常的用例是在特定位置构造和抛出的。此异常的目标是从真正的异常中复制堆栈,然后将硬编码的方法附加到异常。当/如果有意允许 CoreException 到达 Main 类时,主类会检查它收到的所有异常类型是否为 CoreException。
  • 如果它是 CoreException,那么它会检查我的自定义注释,当存在时,由于特定方法的特定异常而导致服务器关闭或重新启动。
  • 永远不会从 lambda 中抛出异常,我不需要方法名称
  • 你还没有回答我的问题
  • @DogeCode 我有 - 我告诉过你需要做什么。这是更新您的日志/打印异常代码。
猜你喜欢
  • 2018-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-16
  • 2017-05-26
  • 1970-01-01
  • 1970-01-01
  • 2010-11-26
相关资源
最近更新 更多