This question already has answers here:
What is a NullPointerException, and how do I fix it?
                                
                                    (12个答案)
                                
                        
                4年前关闭。
            
        

目前有一个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被“值= stockQuote.getString(key);”引发-有时但并非每次都如此。我认为这是因为它试图在解析HTML响应之前访问stockQuote?

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

最佳答案

我怀疑这里的主要问题是可见性之一,如@shmosel在上面的注释中所述:由于stockQuote是非最终的,因此不能保证分配的值在构造函数完成后对所有线程都是可见的。使stockQuote最终应该解决此问题。

我可以建议在这样的构造函数中进行工作是个坏主意吗:它很难测试(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,而不必下载“真实的”类(如果使构造函数为非私有的)。

10-08 03:00