Java中的“this”可以为空吗?

Posted

技术标签:

【中文标题】Java中的“this”可以为空吗?【英文标题】:Can "this" ever be null in Java? 【发布时间】:2011-04-16 21:56:41 【问题描述】:

在类方法中看到这一行,我的第一反应是嘲笑编写它的开发人员。但后来,我想我应该首先确保我是对的。

public void dataViewActivated(DataViewEvent e) 
    if (this != null)
        // Do some work

那条线会评估为假吗?

【问题讨论】:

总是先嘲讽,后质疑。道歉比重新抓住一个在硫磺火中摧毁某人的黄金机会更容易。 +1 表示“硫磺乱舞”。 你知道有什么好笑的吗?由于编译器错误,这可能在 C# 中发生! @Blindy 会给代码示例 +1。 在 C# 中它可以为空。在某些边缘情况下。我也有同样的冲动:嘲笑这个傻瓜,但后来我才冷静下来。看看这里:***.com/questions/2464097/… 【参考方案1】:

不,它不能。如果您使用的是this,那么您就在实例中,所以this 不为空。

JLS 说:

当用作主要表达式时,关键字 this 表示一个值,该值是对调用实例方法的对象(第 15.12 节)或正在构造的对象的引用。

如果您从对象调用方法,则该对象存在,或者您之前将有一个 NullPointerException(或者它是一个静态方法,但您不能在其中使用 this)。


资源:

JLS - this keyword

【讨论】:

我对 Java 了解不多,但在 C++ 中,实例方法的 this 可能为 NULL。所以我不太相信这是 Java 的充分理由。 当发现foonull 时,会抛出类似foo.bar() 的空指针异常。它确实发生在进入方法之前,但真正的故事是没有方法可以尝试调用。 @KennyTM:在 Java 中就足够了。如果您使用 this 关键字并且它可以编译,那么当您观察它时它不为空。但正如其他人所说,在尝试调用该方法时不会阻止 NPE,例如但这完全超出了您作为方法的控制范围,并且该方法中的 null 检查不会改变任何内容。 @Kenny:不是没有undefined behavior,不过如果你知道你的实现细节,你可以使用它。 我不会订阅这个明确的答案,即使它完全有道理。我有一个 android 应用程序的崩溃报告,其中 this == null 在变量从另一个线程被空化后立即调用实例方法。即使变量为空,实际调用也会通过,并且在尝试读取实例成员时崩溃:)【参考方案2】:

这就像问自己“我还活着吗?” this 永远不能为空

【讨论】:

我还活着?!天哪,我不知道了 你说的好像this != null 是不言而喻的。它不是 - 例如,在 C++ 中,this 很可能是 NULL,用于非虚拟方法。 @nikie 在某种意义上,这是不言而喻的。即使在 C++ 中,任何发生这种情况的程序都有未定义的行为。 GCC 上的虚函数也可能发生这种情况:ideone.com/W7RmU。 @JohannesSchaub-litb 带有 null ref 的 c++ 程序的行为未定义是不正确的。只要您不取消引用它,它就会被定义【参考方案3】:

No never,关键字'this'本身代表该类在该类范围内的当前活动实例(对象),您可以使用它访问其所有字段和成员(包括构造函数) ) 及其父类的可见部分。

而且,更有趣的是,尝试设置它:

this = null;

考虑一下?怎么可能,岂不是像砍掉你坐的树枝一样。由于关键字“this”在类的范围内可用,因此只要你说 this = null;在类中的任何位置,那么您基本上是在要求 JVM 在某些操作中间释放分配给该对象的内存,这是 JVM 不允许发生的,因为它需要在完成该操作后安全返回。

此外,尝试this = null; 将导致编译器错误。原因很简单,Java(或任何语言)中的关键字永远不能被赋值,即关键字永远不能是赋值操作的左值。

其他例子,你不能说:

true = new Boolean(true);
true = false;

【讨论】:

我发现这个是因为我想看看我是否可以使用this = null。我的实例在 android 中,我想删除一个视图并将处理视图的对象设置为 null。然后我想使用remove() 方法,它会删除实际视图,然后处理程序对象将变得无用,所以我想将其设为空。 不确定我是否同意您的说法。 (至少中间部分;左值部分很好......虽然我很确定实际上有一些破碎的语言允许你分配例如 true = false (甚至我不会称之为破碎的语言也可能允许它与反射或类似的诡计))。无论如何,如果我有一些对象 foo 并且我要做(忽略它是非法的)foo.this = null,foo 仍将指向内存,因此 JVM 不会垃圾收集它。 @Foon 很好,这个问题正是关于 Java 语言的,此外,我还没有遇到任何允许设置 this=null 的语言。也就是说,如果确实存在这样的语言,那么我非常怀疑这些语言中的“this”是否具有相同的上下文和意识形态。【参考方案4】:

如果您使用-target 1.3 或更早版本进行编译,则外部 this 可能是null。或者至少它曾经...

【讨论】:

我想通过反射我们可以将外部 this 设置为 null。当某人在引用 Outer.this.member 时遇到空指针异常时,这可能是一个愚人节玩笑 @irreputable 可能对反序列化感兴趣以选择外部/内部对象实例化的顺序 @SamGinrich 哦,这是一个有趣的观点,似乎没有其他答案可以解决。在 Java 序列化中,至少,在反序列化期间初始化的顺序字段由流确定。 OTOH,如果这引起了问题,那么您有更大的问题。 @irreputable Actual 在替换具有硬尺寸限制的标准 ObjectInpput/OutputStream 时出现了订单问题。事实证明,反射可以暂时创建一个带有 Outer.this == null 的内部对象“独立”,并稍后绑定 Outer 对象(如果不这样做,行为肯定是未定义的!)【参考方案5】:

没有。要调用类实例的方法,该实例必须存在。该实例作为参数隐式传递给方法,由this 引用。如果thisnull,那么就没有实例可以调用其方法。

【讨论】:

【参考方案6】:

语言强制执行它是不够的。 VM 需要强制执行它。除非 VM 强制执行它,否则您可以编写一个编译器,在调用用 Java 编写的方法之前不强制执行 null 检查。 实例方法调用的操作码包括将 this ref 加载到堆栈上,请参阅:http://java.sun.com/docs/books/jvms/second_edition/html/Compiling.doc.html#14787。用 this 代替 null ref 确实会导致测试为假

【讨论】:

【参考方案7】:

普通的this 在真正的Java 代码中永远不可能是null1,而您的示例使用普通的this。有关详细信息,请参阅其他其他答案。

合格的this 应该永远不会是null,但有可能打破这一点。考虑以下几点:

public class Outer 
   public Outer() 

   public class Inner 
       public Inner() 

       public String toString() 
           return "outer is " + Outer.this;  // Qualified this!!
       
   

当我们要创建Inner的实例时,我们需要这样做:

public static void main(String[] args) 
    Outer outer = new Outer();
    Inner inner = outer.new Inner();
    System.out.println(inner);

    outer = null;
    inner = outer.new Inner();  // FAIL ... throws an NPE

输出是:

outer is Outer@2a139a55
Exception in thread "main" java.lang.NullPointerException
        at Outer.main(Outer.java:19)

表明我们尝试创建带有 null 引用的 Inner 到其 Outer 的尝试失败了。

事实上,如果你坚持“纯 Java”信封,你就无法打破它。

但是,每个Inner 实例都有一个隐藏的final 合成字段(称为"this$0"),其中包含对Outer 的引用。如果您真的很棘手,可以使用“非纯”的方式将null 分配给该字段。

您可以使用Unsafe 来执行此操作。 您可以使用本机代码(例如 JNI)来执行此操作。 您可以使用反射来做到这一点。

无论您采用哪种方式,最终结果都是Outer.this 表达式的计算结果为null2

简而言之,合格的this有可能null。但如果您的程序遵循“纯 Java”规则,则不可能


1 - 我对诸如手动“编写”字节码并将它们作为真正的 Java 传递、使用 BCEL 或类似方法调整字节码、或跳入本机代码并使用保存的寄存器等技巧打折扣。 IMO,那不是 Java。假设,这样的事情也可能由于 JVM 错误而发生......但我不记得每一次看到的错误报告。

2 - 实际上,JLS 并没有说明行为将是什么,它可能取决于实现......除其他外。

【讨论】:

【参考方案8】:

在静态类方法中,this 未定义,因为this 与实例相关联,而不是类。我相信尝试在静态上下文中使用 this 关键字会导致编译器错误。

【讨论】:

【参考方案9】:

当您在 null 引用上调用方法时,Java VM 将抛出 NullPointerException。这是规范,所以如果你的 Java VM 严格遵守规范,this 永远不会是 null

【讨论】:

【参考方案10】:

如果方法是静态的,则没有任何this。如果方法是虚拟的,那么this 不能为空,因为为了调用该方法,运行时需要使用this 指针来引用vtable。如果方法不是虚拟的,那么,是的,this 可能为空。

C# 和 C++ 允许使用非虚拟方法,但在 Java 中所有非静态方法都是虚拟的,因此 this 永远不会为空。

【讨论】:

【参考方案11】:

tl;dr,“this”只能从非静态方法调用,我们都知道非静态方法是从某种不能为空的对象调用的。

【讨论】:

以上是关于Java中的“this”可以为空吗?的主要内容,如果未能解决你的问题,请参考以下文章

在Java中使用for循环,我必须指定不为空吗?

Java varargs 可以为空吗? [复制]

C# 8 中的不可为空的引用类型在运行时可以为空吗?

Oracle 序列可以为空吗?

TypeScript 中的可选参数可以为空吗?

整数列可以为空吗?