溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java程序的臟數(shù)據(jù)問題實(shí)例分析

發(fā)布時間:2022-01-10 15:50:24 來源:億速云 閱讀:113 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“Java程序的臟數(shù)據(jù)問題實(shí)例分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Java程序的臟數(shù)據(jù)問題實(shí)例分析”吧!

  臟數(shù)據(jù)(Out-of-date data),指過時的數(shù)據(jù)。
  如果在您的Java程序中存在臟數(shù)據(jù),將或多或少地給軟件系統(tǒng)帶來一些問題,如:無法實(shí)時地應(yīng)用已經(jīng)發(fā)生改變的配置,軟件系統(tǒng)出現(xiàn)一些莫名其妙的、難以重現(xiàn)的、后果嚴(yán)重的錯誤等等。盡量避免臟數(shù)據(jù)的存在是非常有價值的。本文希望能在這方面給同行們一點(diǎn)幫助。

Fragment 1. 緩存技術(shù)的臟數(shù)據(jù)問題
  /**
  * A report printer is used to print a report.
  *
  * @version 1.0 9/9/2003
  * @author Bill
  */
  public class ReportPrinter {
  /**
  * Constructs a ReportPrinter instance.
  */
  public ReportPrinter() {
  // do something...
  }

  /**
  * Prints a printable.
  *
  * @param printable the specified printable object
  */
  public void print(Printable printable) {
  Graphics g = getGraphics();
  g.setFont(getReportFont(printable.getFont());

  printable.print(g);
  }

  /**
  * Returns the corresponding report font of a java font.
  *
  * @param javaFont the specified java font
  * @return the corresponding report font
  */
  private Font getReportFont(font javaFont) {
  Font reportFont = fontMap.get(javaFont);

  if(reportFont == null) {
  reportFont = loadFont(javaFont);
  fontMap.put(javaFont, reportFont);
  }

  return reportFont;
  }

  /**
  * Loads the corresponding report font of a java font.
  *
  * @param javaFont the specified java font
  * @param the corresponding report font
  */
  protected static Font loadFont(Font javaFont) {
  Font reportFont = null;

  // do something...

  return reportFont;
  }

  /**
  * The font map(java font->report font).
  */
  private static HashMap fontMap = new HashMap();
  }

  Fragment 1中,由于裝載一個java font所對應(yīng)的report font開銷較大,使用了緩存技術(shù)來避免這種開銷。這是一種常見的提高性能的方式,而且在一般情況下運(yùn)行良好。但是Fragment 1的設(shè)計與實(shí)現(xiàn)可能是不完備的,因為極有可能一個java font所對應(yīng)的report font在系統(tǒng)啟動之后發(fā)生變化,在這種變化發(fā)生之后,只有重啟軟件系統(tǒng)才能裝載之,這常常是最終用戶的抱怨之一。更可怕的是,類似的這種臟數(shù)據(jù)的存在還可能帶來其它嚴(yán)重的、無法想象的后果。
  如何避免使用緩存技術(shù)所帶來的臟數(shù)據(jù)問題呢?
  在設(shè)計、實(shí)現(xiàn)和測試時,應(yīng)該清晰定義緩存數(shù)據(jù)的更新:
  i. 不考慮緩存數(shù)據(jù)的更新,重啟軟件系統(tǒng)是一種必要的方式;
  ii. 不考慮緩存數(shù)據(jù)的更新,緩存數(shù)據(jù)不可能成為臟數(shù)據(jù)(但在軟件系統(tǒng)中,往往“不可能”會在一次又一次的重構(gòu)之后變?yōu)椤翱赡堋?;
  iii. 考慮緩存數(shù)據(jù)的更新,當(dāng)源數(shù)據(jù)變化時,實(shí)時更新緩存數(shù)據(jù)。

Fragment 2. Singleton模式的臟數(shù)據(jù)問題
  /**
  * A storage usage handler is used to query the storage usage of users.
  *
  * @version 1.0 9/9/2003
  * @author Bill
  */
  public class StorageUsageHandler {
  /**
  * Returns a StorageUsageHandler instance.
  *
  * @return the single StorageUsageHandler instance
  */
  public static StorageUsageHandler getStorageUsageHandler() {
  if(handler == null) {
  handler = new StorageUsageHandler();
  }

  return handler;
  }

  /**
  * Constructs a StorageUsageHandler instance.
  */
  private StorageUsageHandler() {
  users = Context.getAllUsers();
  }

  /**
  * Returns the storage sizes of all the users.
  *
  * @return the storage sizes
  */
  public long[] getSizes() {
  long sizes[] = new long[users.size()];

  for(int i = 0; i < users.size(); i++) {
  sizes[i] = getOneSize(users.get(i));
  }
  }

  /**
  * Returns the storage size of a user.
  *
  * @param user the specified user
  * @return the storage size
  */
  protected long getSize(User user) {
  // do something...

  return  0;
  }

  /**
  * The StorageUsageHandler singleton.
  */
  private static StorageUsageHandler handler;

  /**
  * The users.
  */
  private List users;
  }

  您看出了問題所在嗎?
  Fragment 2中,由于沒有必要次次實(shí)例化StorageUsageHandler而帶來不必要的開銷,采用了Singleton模式以保證StorageUsageHandler只被實(shí)例化一次。
  在實(shí)例化SotrageUsageHandler時,StorageUsageHandler的類成員users將被賦值。由于不存在任何對users重新賦值的方法,一直駐留在軟件系統(tǒng)中的users將不會發(fā)生任何變化。在軟件系統(tǒng)啟動之后,增加、刪除或修改用戶的操作經(jīng)常會發(fā)生,而一旦發(fā)生這類操作,users就成為了臟數(shù)據(jù),F(xiàn)ragment 2將無法正常工作。
  如何避免使用Singleton模式所帶來的臟數(shù)據(jù)問題呢?
  對于Singleton類的類成員:
  i. 對于與Singleton類外部無依賴關(guān)系的類成員,不存在這種問題;
  ii. 對于依賴于Singleton類外部的類成員,且該類成員不存在更新機(jī)制,最好是將其去掉,需要時從Singleton類外部直接獲??;如果這種辦法不可行,應(yīng)提供機(jī)制以確保在使用該類成員之前,該類成員已經(jīng)被更新過。

Fragment 3. 類使用的臟數(shù)據(jù)問題
  /**
  * A storage usage handler is used to query the storage usage of users.
  *
  * @version 1.0 9/9/2003
  * @author Bill
  */
  public class StorageUsageHandler implements AdminHandler {
  /**
  * Constructs a StorageUsageHandler instance.
  */
  private StorageUsageHandler() {
  users = Context.getAllUsers();
  }

  /**
  * Returns the storage sizes of all users.
  *
  * @return the storage sizes
  */
  public long[] getSizes() {
  long sizes[] = new long[users.size()];

  for(int i = 0; i < users.size(); i++) {
  sizes[i] = getOneSize(users.get(i));
  }
  }

  /**
  * Returns the storage size of a user.
  *
  * @param user the specified user
  * @return the storage size
  */
  protected long getSize(User user) {
  // do something...

  return  0;
  }

  /**
  * Displays the storage usage of users.
  *
  * @param req the http servlet request
  * @param res the http servlet response
  *
  * @throws IOException
  * @throws ServletException
  */
  public void process(HttpServletRequest req, HttpServletResponse res)
  throws IOException, ServletException {

  res.setContentType("text/html");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Pragma","no-cache");
  res.setDateHeader("Expires", 0);

  PrintWriter writer = new PrintWriter(res.getOutputStream());
  long sizes[] = getsizes();
  writer.println("Storage Usage");
  writer.println("

");

  for(int i = 0; i < sizes.length; i++) {
  writer.print("

");
  }

");
  writer.print(users.get(i) + ": " + sizes[i]);
  writer.println("

  writer.println("");
  writer.flush();
  writer.close();
  }

  /**
  * The users.
  */
  private List users;
  }

  /**
  * An admin servlet as a http servlet to process the admin http servlet
  * request and response.
  *
  * @version 1.0 9/9/2003
  * @author Bill
  */
  public class AdminServlet extends HttpServlet {
  /**
  * Initiates the configuration.
  *
  * @param config the servlet config
  *
  * @throws ServletException
  */
  private void initConfig(ServletConfig config) throws ServletException {
  // do something...

  handlerMap.put("__storage_Usage__", new StorageUsageHandler());
  }

  /**
  * Processes the http servlet request and response.
  *
  * @throws IOException
  * @throws ServletException
  */
  public void service(HttpServletRequest req, HttpServletResponse res)
  throws IOException, ServletException {

  AdminHandler handler = handlerMap.get(req.getParameter("handler"));

  if(handler == null) {
  // do something...

  return;
  }

  handler.process(req, res);
  }

  /**
  * The admin handler map(handler name->handler).
  */
  private HashMap handlerMap = new HashMap();
  }

  您一定看出了問題所在吧!
  Fragment 3中,由于StorageUsageHandler并不遵循Singleton模式,盡管StorageUsageHandler的類成員users只能在實(shí)例化StorageUsageHandler時被賦值,但是在單線程模式下,只要保證每次所使用的StorageUsageHandler實(shí)例是新實(shí)例化的,基本上還是沒有問題的。
  問題在于,在初始化AdminServlet的過程中,StorageUsageHandler被實(shí)例化并存儲起來。此后,除非servlet container重新裝載AdminServlet,否則將無法重新實(shí)例化StorageUsageHandler,也將無法更新StorageUsageHandler的類成員users。這樣,在發(fā)生了增加、刪除或修改用戶的操作之后,users將成為臟數(shù)據(jù)。
  如何避免類使用所帶來的臟數(shù)據(jù)問題呢?
  i. 對于與類外部無依賴關(guān)系的類成員,不存在這種問題;
  ii. 對于依賴于類外部的類成員,且該類成員不存在更新機(jī)制。最好是將其去掉,需要時從類外部直接獲??;如果這種辦法不可行,應(yīng)提供機(jī)制以確保在使用該類成員之前,該類成員已經(jīng)被更新過;如果這種辦法還不可行,請清晰地說明類的使用方式,以防止不當(dāng)?shù)念愂褂冒l(fā)生。

到此,相信大家對“Java程序的臟數(shù)據(jù)問題實(shí)例分析”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI