为什么Java中没有指针却会出现“NullPointerException”?

15

如果在Java中没有指针的概念,为什么我会遇到一个名为NullPointerException的异常?


34
因为它本应该被称为“NullReferenceException”,但是有人想得不够清晰。 - skaffman
3
这是我对Java的一个小抱怨。 - tzaman
2
@tzaman +1 我学习 Java 的时间越长,就越发现它的不一致性和自相矛盾之处,让我越来越不喜欢它。 - There is nothing we can do
1
因为指针和引用是同一件事情。 - Val
1
不,它们不是一样的。 - There is nothing we can do
显示剩余6条评论
6个回答

11

Java没有像C语言一样可以轻易操纵并加减任意值的通用指针。对于不习惯使用这种指针的人来说,这可能会导致各种问题。

然而,Java仍然需要区分对象和“无对象”。只是异常的名称表示您正在尝试使用一个没有支持对象的对象引用。

你可以随便给它起名字比如NoObjectException或者DereferenceException,或者其他无数的名称,以尽可能地减少人们认为Java具有通用指针的可能性。

但是,NullPointerException是语言创建者选择的名称,可能是因为他们习惯于在C和/或C++中编码。


如果空值能够被包含在类型系统中就好了 :-/ - user166390
@paxidiablo,所以为什么他们要发明final而不使用const。如果你想想,final并不像const那么描述性。 - There is nothing we can do
1
因为const会定义一个常量。final与C++中的const略有不同,因为您可以延迟设置final,一旦设置就无法更改。在我看来,final恰好是正确的术语。一旦设置,就是最终允许具有的值。 - paxdiablo
Java是面向对象的(或者说我被告知是这样)。那么为什么它需要区分对象和“无对象”?在面向对象的系统中,“空指针/引用”将等同于NullObject或UndefinedObject。这个对象很可能不理解发送给它的大多数消息,并以此触发错误,但是没有必要出现“NullPointerException”。真正的原因是答案的最后一句话。“实际上,我编造了‘面向对象’这个术语,我可以告诉你,我当时并没有考虑C++。”(Alan Kay) - Klaws

9

是的,这是我学习Java时遇到的第一件让人恼火的事情之一,哈哈。 它真的应该被称为NullReferenceException、NoObjectException或DereferenceException,就像paxdiablo所提到的那样。 引用甚至不必在内部表示为指针,您也不必关心。 “包括Sun在内的大多数虚拟机都使用句柄而不是指针。句柄是指向指针的指针,所以谁知道他们是如何想到使用它的?” 哦,微软的Java虚拟机实际上确实使用指针而不是句柄,所以想象一下。


但是引用本质上只是一个不可变的指针,至少在概念上是这样的,因此... - Ingo

4

从技术上讲,这是正确的,它实际上应该被称为NullReferenceException。


1
引用和指针是相同的。说必须使用其中一个同义词而不是另一个是无意义的。 - Val

3

因为内部对象变量是指向这些对象的指针。但是,除了在大多数JVM实现上调用System.identityHashCode(object)返回指向对象的指针之外,您无法获得指针值。

编辑:你几乎都是正确的,我几乎是错误的:identityHashCode比仅返回指针要复杂得多。我刚刚看了一下JVM源代码,他们实现了一些哈希代码生成器。然而,在hashCode(一个常数?我不知道)是常数的情况下,它们会返回对象指针。以下是他们的源代码:

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0 ;
  if (hashCode == 0) {
     // This form uses an unguarded global Park-Miller RNG,
     // so it's possible for two threads to race and generate the same RNG.
     // On MP system we'll have lots of RW access to a global, so the
     // mechanism induces lots of coherency traffic.
     value = os::random() ;
  } else
  if (hashCode == 1) {
     // This variation has the property of being stable (idempotent)
     // between STW operations.  This can be useful in some of the 1-0
     // synchronization schemes.
     intptr_t addrBits = intptr_t(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     value = intptr_t(obj) ;
  } else {
     // Marsaglia's xor-shift scheme with thread-specific state
     // This is probably the best overall implementation -- we'll
     // likely make this the default in future releases.
     unsigned t = Self->_hashStateX ;
     t ^= (t << 11) ;
     Self->_hashStateX = Self->_hashStateY ;
     Self->_hashStateY = Self->_hashStateZ ;
     Self->_hashStateZ = Self->_hashStateW ;
     unsigned v = Self->_hashStateW ;
     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
     Self->_hashStateW = v ;
     value = v ;
  }

  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash, "invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}

@Daniel 只是出于好奇,你用什么变量类型来存储这个指针? - There is nothing we can do
指针本身是一个整数。因此,在64位系统上,哈希可能会略有不同(例如将地址的4个高字节异或到4个低字节)。 - Daniel
2
@知道它返回一个int。但它不是指针。它是一个...(等待)...哈希码。在某些JVM中,它是地址的函数,但不一定是这样。行为未由JLS指定。顺便说一句,因为它是哈希码,两个对象可以具有相同的identityHashCode()值。我不会投反对票,但丹尼尔答案的最后部分是不正确的:说identityHashCode值是指针是错误的,而且错误不止一个层面。 - CPerkins
@Daniel,引用不能在内部成为指针,因为指针和引用是同一件事。它们是同义词。 - Val
@Val:在某些系统上,引用是指针,这些指针在元数据中被识别,因此如果GC冻结程序的执行并检查调用堆栈,则可以确定每个指向未固定对象的活动指针/引用的位置。当程序被冻结时,GC可以将对象复制到新位置,并用指向新副本的指针替换每个指向它的指针;一些巧妙的算法可以使这个过程非常便宜。普通指针只能存在于固定的对象中;GC无法知道所有普通指针的位置,但它也不需要知道。 - supercat

0

因为你声明的所有变量(在赋值右侧)都是指向堆空间中某些对象的引用。如果一个引用没有指向任何地方,那么访问该变量会抛出nullpointerexception。


-1
如果您有一个对象,例如一个带有列表属性的对象,并且您没有明确为其分配空间,则运行程序将抛出该错误。
请查看调试器(Eclipse或其他)以查看当您未正确初始化对象时它所包含的内容,然后事情就会变得非常清晰。
我认为这样做是为了区分在内存中具有空间和不具有空间的对象之间的概念。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接