NullPointerException - 在正确实例化之前尝试访问对象?

时间:2015-12-09 21:31:56

标签: java android

目前有一个Android应用程序的问题,我认为该线程正在尝试使用对象,然后才能正确实例化。

这是我班级的构造函数:

String name;
String price;
String percentChange;
String peRatio;

private String ticker;
private JSONObject stockQuote;
public Stock(String ticker) {
    this.ticker = ticker;

    // Build query and URL.
    String url = "http://query.yahooapis.com/v1/public/yql?q=";
    String query = "select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(\"" +
            ticker +
            "\")&env=store://datatables.org/alltableswithkeys&format=json";

    url += query;

    stockQuote = fetch(url);
    name = get("symbol");
    price = get("Ask");
}

这里的fetch()函数用于从HTTP响应中检索JSONObject:

private JSONObject fetch(String myurl) throws IOException {
    InputStream is = null;
    String contentAsString = "";

    try {
        URL url = new URL(myurl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(10000 /* milliseconds */);
        conn.setConnectTimeout(15000 /* milliseconds */);
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        // Starts the query
        conn.connect();
        int response = conn.getResponseCode();
        is = conn.getInputStream();

        // Convert the InputStream into a string
        contentAsString = readIt(is);

        // Makes sure that the InputStream is closed after the app is
        // finished using it.
    } finally {
        if (is != null) {
            is.close();
        }
    }

    JSONObject queryResult = null;

    try {
        JSONObject json = new JSONObject(contentAsString);
        queryResult = json.getJSONObject("query").getJSONObject("results").getJSONObject("quote");
    } catch (JSONException e) {
        System.err.println("Failed to decode JSON");
    }

    return queryResult;
}

最后是get()方法,用于从JSONObject中检索字符串:

public String get(String key) {

    String value = null;

    try {
        value = stockQuote.getString(key);
    } catch (JSONException e) {
        System.err.println("Couldn't find " + key);
    }

    return value;
}

NullPointerException被" value = stockQuote.getString(key)抛出;" - 有时,但不是每次。我认为这是因为它在解析HTML响应之前尝试访问stockQuote?

我猜这是一个明显的解决方案,但我以前没遇到过这个问题。有什么想法吗?

1 个答案:

答案 0 :(得分:2)

我怀疑这里的主要问题是可见性,正如@shmosel在上面的评论中所说的那样:因为stockQuote是非最终的,所以一旦确定了所有线程,就不能保证所分配的值可见。构造函数完成。制作stockQuote final应该可以解决这个问题。

我是否可以建议在构造函数中完成这样的工作是一个坏主意:它使测试变得困难(Miško Hevery关于这篇文章的文章是一个很好的阅读),而你正在调用外星人构造函数中的方法,不鼓励(例如,通过Java Concurrency In Practice)。

相反,您可以将作品(下载代码)移动到静态工厂方法中,这样可以更好地修复竞争条件:

public static Stock create(String ticker) {
  // Build query and URL.
  String url = "http://query.yahooapis.com/v1/public/yql?q=";
  String query = "select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(\"" +
        ticker +
        "\")&env=store://datatables.org/alltableswithkeys&format=json";

  url += query;

  JSONObject stockQuote = fetch(url);

  return new Stock(ticker, stockQuote);
}

private Stock(String ticker, JSONObject stockQuote) {
  this.ticker = ticker;
  this.stockQuote = stockQuote;

  if (stockQuote == null) throw new NullPointerException();

  name = get("symbol");
  price = get("Ask");
}

通过这种方式,您可以在Stock可用之前拥有stockQuote的实例,因此在致电NullPointerException时无法获得get。 (get也应该final,因此它也不是外来方法。

(当然,如果您需要继承Stock,这不会那么容易。)

您还可以更轻松地测试Stock课程,因为您可以直接注入JSONObject,而不必下载"真实"一个(如果你使构造函数非私有)。