我刚刚了解到在C和C ++中解除引用null
有时会产生undefined results。这对我来说非常有趣,就像所有奇怪的编程行为一样(我曾经有人告诉我他们调试了“损坏的RAM - 程序在合法的生产环境中不能像编写的那样运行”)。因为我主要是一名Java开发人员,所以我想知道这种语言是否有可能以这种语言发生?
JLS并未具体说明null
引用的实施方式(3.10.7,4.1,15.8.1),所以我不太确定。但我认为可以通过Unsafe API直接操作内存地址来实现。不幸的是,我对JVM的内部工作原理知之甚少,无法知道这是否可行。
如果 可能,那么恶意程序也可以这样做,这会引起一个有趣的安全问题。
那么:解除引用null
时,Java是否有可能出现未定义的行为,而不是简单地抛出NullPointerException
?
答案 0 :(得分:10)
JLS并未具体说明实现的空引用如何,但它指定了行为。换句话说,没有没有未指明的行为。如果您遇到的行为不是JLS中指定的行为,那就是一个错误。
让我澄清一下:您可以使用本机代码来删除某些结构,让JVM崩溃,但这与任何Java行为无关。但是在典型的JVM实现中,null
行为的实现是您可以打扰的最后一件事。不是,重要的是,如果你从本机代码覆盖任意内存,那么你会丢弃什么。
“未指定的行为”意味着规范本身允许在结果行为中存在差异。 Java不是这种情况。
答案 1 :(得分:5)
您无法从纯Java中的null
获取未定义的行为(除非JVM中存在严重错误!)。 JLS指定任何明确或隐式取消引用null
的尝试都将导致NullPointerException
。没有蠕动空间允许任何与null
的处理相关的未定义行为。
但是,如果您的应用程序包含...或使用... native
方法,则其中一种方法可能会以导致未定义行为的方式错误处理null
。您还可以使用Unsafe
类获取未定义的行为。但这两种情况都意味着您没有使用纯 Java。 (当你走出 pure Java之外时,JLS的保证不再适用!)
(可能发生不可预测事情的一个领域是多线程。但即便如此,也可以定义可能行为的 set 。例如,如果你没有充分同步状态共享你可能会在字段中看到陈旧的值。但是您不会看到完全随机的值...或导致分段违规的错误地址。)
如果可能,那么恶意程序也可能这样做,这将开启一个有趣的安全问题。
恶意程序几乎可以做任何事情。但处理此问题的正确方法是在沙箱中执行您不信任的代码(即可能是恶意代码)。典型的沙箱禁止调用Unsafe
或加载本机库......以及恶意程序可能利用的许多其他内容。
答案 2 :(得分:1)
行为在15.12.4.4 Locate Method to Invoke中定义:
否则,将调用实例方法并且存在目标 参考。 如果目标引用为null,则为NullPointerException 抛出此点。否则,据说目标参考引用 到目标对象,将用作关键字this的值 在调用的方法中。调用的其他四种可能性 然后考虑模式。
取消引用null应抛出NullPointerException。
答案 3 :(得分:1)
具有未定义行为的语言特性的非常概念是C和C ++标准的编写者用来明确标准不需要任何特定行为的东西。这使得C和C ++的各种实现者能够为实现所针对的特定硬件或操作系统做最有效或最方便的事情。这是因为C始终具有优于可移植性的性能。但Java具有相反的优先级;它早期的口号是“写一次,随处运行”。所以Java语言规范没有讨论未定义的行为,并努力定义所有语言特性的行为。
您似乎认为使用空引用可能会在某些情况下以某种方式破坏内存。我认为你将C / C ++指针与Java引用混淆。指针本质上是一个内存地址:通过将其转换为void *
并取消引用它,您可以无限制地破坏内存内容。 Java引用不就像内存地址一样,因为垃圾收集器必须可以自由地将对象移动到内存中的不同位置。将Java引用转换为内存地址是只有JVM才能做到的事情;它永远不会是Java程序本身可以做的事情。由于此转换完全由JVM控制,因此JVM可以确保转换始终有效,并且始终指向它应该和其他任何地方的对象。