JAVA中的斷言是什么?有什么陷阱?


如何在 Java 中使用斷言

什么是 Java 斷言?

在 JDK 1.4之前,開發人員經常使用注釋來記錄關於程序正確性的假設。然而,注釋作為測試和調試假設的機制是無用的。編譯器忽略注釋,因此無法使用它們進行 bug 檢測。開發人員在更改代碼時也經常不更新注釋。

在 JDK 1.4中,斷言被引入作為測試和調試代碼假設的新機制。實質上,斷言是在運行時執行的可編譯實體,假設你已經為程序測試啟用了它們。可以通過編寫斷言來通知 bug 發生的地方,這樣可以大大減少調試失敗程序的時間。

如何用 Java 編寫斷言

編寫斷言的表達式:

assert BooleanExpr;

如果 BooleanExpr 的計算結果為 true,則不會發生任何事情,並繼續執行。但是,如果表達式計算結果為 false,那么將拋出 AssertionError

舉個例子

    public static void main(String[] args) {
        int a = 10;
        assert a>100;//false
    }

此斷言表明開發人員認為變量 a 包含一個大於100的值。但是,情況顯然不是這樣;

assert 語句的執行導致拋出 AssertionError

運行后沒有反應??

有的小伙伴發現自己的IDE並沒有拋出Error 這是因為沒有顯示開啟,啟用斷言
開啟方法: vm options 加入 -ea

此時我們運行項目 發現拋出了異常

Exception in thread "main" java.lang.AssertionError
	at Scratch.main(scratch_4.java:4)

希望獲得更多信息?

此時我們已經知道了斷言的基本用法 但是拋出Error后我們並不知道是什么問題導致的 還需要去翻看代碼找到報錯的地方, 如果我們希望獲得更多有用的信息 我們可以這樣修改Assert語句:

assert BooleanExpr : expr;

expr 是任何可以返回值的表達式(包括方法調用)但是不能調用具有 void 返回類型的方法。一個有用的表達式是一個字符串,用它來描述失敗的原因

舉個例子

public static void main(String[] args) {
        int a = 10;
        assert a>100 : "a < 100"; 
    }

運行:

Exception in thread "main" java.lang.AssertionError: a < 100
	at Scratch.main(scratch_4.java:5)

無論哪個例子,在不使用-ea (啟用斷言)選項的情況下運行都不會產生輸出。當斷言未啟用時,它們不會執行,盡管它們仍然存在於類文件中。

前置條件和后置條件

前置條件: 是在執行某些代碼之前必須求值為 true 的條件

后置條件: 是在執行某些代碼后必須求值為 true 的條件

前置條件

前置條件檢查:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

class PNG
    {
        /**
         *  Create a PNG instance, read specified PNG file, and decode
         *  it into suitable structures.
         *
         *  @param filespec path and name of PNG file to read
         *
         *  @throws NullPointerException when <code>filespec</code> is
         *          <code>null</code>
         */
        PNG(String filespec) throws IOException
        {
            //在非私有構造方法中 使用前置條件
            if (filespec == null)
                throw new NullPointerException("filespec is null");
            try (FileInputStream fis = new FileInputStream(filespec))
            {
                readHeader(fis);
            }
        }

        private void readHeader(InputStream is) throws IOException
        {  
                //在私有方法中使用前置條件檢查
            assert is != null : "null passed to is";
        }
    }

    class Scratch
    {
        public static void main(String[] args) throws IOException
        {
            PNG png = new PNG((args.length == 0) ? null : args[0]);
        }
    }

后置條件

后置條件檢查:

public class AssertDemo
{
   public static void main(String[] args)
   {
      int[] array = { 20, 91, -6, 16, 0, 7, 51, 42, 3, 1 };
      sort(array);
      for (int element: array)
         System.out.printf("%d ", element);
      System.out.println();
   }

   private static boolean isSorted(int[] x)
   {
      for (int i = 0; i < x.length - 1; i++)
         if (x[i] > x[i + 1])
            return false;
      return true;
   }

   private static void sort(int[] x)
   {
      int j, a;
      // For all integer values except the leftmost value ...
      for (int i = 1; i < x.length; i++)
      {
         // Get integer value a.
         a = x[i];
         // Get index of a. This is the initial insert position, which is
         // used if a is larger than all values in the sorted section.
         j = i;
         // While values exist to the left of a's insert position and the
         // value immediately to the left of that insert position is
         // numerically greater than a's value ...
         while (j > 0 && x[j - 1] > a)
         {
            // Shift left value -- x[j - 1] -- one position to its right --
            // x[j].
            x[j] = x[j - 1];
            // Update insert position to shifted value's original position
            // (one position to the left).
            j--;
         }
         // Insert a at insert position (which is either the initial insert
         // position or the final insert position), where a is greater than
         // or equal to all values to its left.
         x[j] = a;
      }
      //在 sort ()返回給它的調用者之前,我使用 assert 檢查 x 被排序的后置條件。
      assert isSorted(x): "array not sorted";
   }
}

陷阱

assert關鍵字用法簡單,但是使用assert往往會讓你陷入越來越深的陷阱中。應避免使用。筆者經過研究,總結了以下原因:

1、 assert關鍵字需要在運行時候顯式開啟才能生效,否則你的斷言就沒有任何意義。而現在主流的Java IDE工具默認都沒有開啟-ea斷言檢查功能。這就意味着你如果使用IDE工具編碼,調試運行時候會有一定的麻煩。並且,對於Java Web應用,程序代碼都是部署在容器里面,你沒法直接去控制程序的運行,如果一定要開啟-ea的開關,則需要更改Web容器的運行配置參數。這對程序的移 植和部署都帶來很大的不便。

2、用assert代替if是陷阱之二。assert的判斷和if語句差不多,但兩者的作用有着本質的區別:assert關鍵字本意上是為測試 調試程序時使用的,但如果不小心用assert來控制了程序的業務流程,那在測試調試結束后去掉assert關鍵字就意味着修改了程序的正常的邏輯。

3、assert斷言失敗將面臨程序的退出。這在一個生產環境下的應用是絕不能容忍的。一般都是通過異常處理來解決程序中潛在的錯誤。但是使用斷言就很危險,一旦失敗系統就掛了。

總結

assert既然是為了調試測試程序用,不在正式生產環境下用,那應該考慮更好的測試JUint來代替其做用,JUint相對assert關鍵的所提供的功能是有過之而無不及。當然完全可以通過IDE debug來進行調試測試

因此,應當避免在Java中使用assert關鍵字,除非哪一天Java默認支持開啟-ea的開關,這時候可以考慮。對比一下,assert能給你帶來多少好處,多少麻煩,這是我們選擇是否使用的的原則,讀者可以自行取舍.

關注公眾號:java寶典
a


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM