为什么这段代码有时会抛出 NullPointerException?

2022-01-08 00:00:00 hashmap java

考虑以下 Java 源代码:

Consider the following Java source:

if( agents != null ) {
  for( Iterator iter = agents.keySet().iterator(); iter.hasNext(); ) {
    // Code that uses iter.next() ...
    //
  }
}

agents 是一个 HashMap.

为什么 for 语句有时会抛出 NullPointerException?

Why does the for statement sometimes throw a NullPointerException?

谢谢.

推荐答案

线程安全

如果您的代码是多线程的,那么这是可能的.例如:

If your code is multi-threaded, then it is possible. For example:

public class C {
  private Hashtable agents = new Hashtable();

  public iterate() {
    if( agents != null ) {
      for (Iterator iter = agents.keySet().iterator(); iter.hasNext();) {
        // Code goes here
      }
    }
}

如果另一个线程在 if 语句执行后立即将 agents 设置为 null(但在 for 循环之前),然后你会得到一个 NullPointerException.通过使用访问器(结合惰性初始化)来避免这种情况.

If another thread sets agents to null immediately after the if statement executes (but before the for loop), then you will get a NullPointerException. Avoid this by using accessors (combined with lazy initialization).

另外,正如其他人所提到的,如果可能,请避免使用泛型来支持此类循环构造.有关详细信息,请参阅其他答案.

Also, as others have mentioned, avoid such looping constructs in favour of generics, if possible. See other answers for details.

访问器提供保护

如果您始终使用以下模式,您的源代码中将永远不会出现 NullPointerException(另一方面,第三方代码可能存在导致您的代码间接失败的问题,这是无法轻易避免的).

If you always use the following pattern you will never have NullPointerExceptions in your source code (third-party code, on the other hand, might have issues that cause your code to fail, indirectly, which cannot be easily avoided).

public class C {
  private Hashtable agents;

  private synchronized Hashtable getAgents() {
    if( this.agents == null ) {
      this.agents = new Hashtable();
    }

    return this.agents;
  }

  public iterate() {
    Hashtable agents = getAgents();

    for (Iterator iter = agents.keySet().iterator(); iter.hasNext();) {
      // Code goes here
    }
  }
}

遍历代理的代码不再需要检查 null.由于许多原因,此代码更加健壮.您可以用 Hashmap(或任何其他抽象数据类型,例如 ConcurrentHashMap<K,V>)替换 Hashtable.

The code that iterates over the agents no longer needs to check for null. This code is much more robost for many reasons. You can substitute Hashmap (or any other abstract data type, such as ConcurrentHashMap<K,V>) for Hashtable.

开闭原则

如果您觉得自己的时间特别慷慨,您可以这样做:

If you were feeling especially generous with your time you could go as far as:

public class C {
  private Hashtable agents;

  private synchronized Hashtable getAgents() {
    if( this.agents == null ) {
      this.agents = createAgents();
    }

    return this.agents;
  }

  public iterate() {
    Iterator i = getAgentKeyIterator();

    while( i.hasNext() ) {
      // Code that uses i.next() ...
    }
  }

  protected Hashtable createAgents() {
    return new Hashtable();
  }

  private Iterator getAgentKeyIterator() {
    return getAgentKeys().iterator();
  }

  private KeySet getAgentKeys() {
    return getAgents().keySet();
  }
}

这将允许子类(由其他开发人员编写)替换他们自己正在使用的抽象数据类型的子类(允许系统更大的灵活性以符合 开放-封闭原则),无需修改(或复制/浪费)您的原创作品.

This would allow subclasses (written by other developers) to substitute their own subclass of the abstract data type being used (allowing the system greater flexibility in keeping with the Open-Closed Principle), without having to modify (or copy/waste) your original work.

相关文章