Một số ví dụ thực tế để hiểu vai trò chính của các xác nhận là gì?
Một số ví dụ thực tế để hiểu vai trò chính của các xác nhận là gì?
Câu trả lời:
Các xác nhận (theo cách của từ khóa khẳng định ) đã được thêm vào trong Java 1.4. Chúng được sử dụng để xác minh tính đúng đắn của bất biến trong mã. Chúng không bao giờ được kích hoạt trong mã sản xuất và là dấu hiệu của lỗi hoặc sử dụng sai đường dẫn mã. Chúng có thể được kích hoạt trong thời gian chạy bằng cách -ea
tùy chọn trên java
lệnh, nhưng không được bật theo mặc định.
Một ví dụ:
public Foo acquireFoo(int id) {
Foo result = null;
if (id > 50) {
result = fooService.read(id);
} else {
result = new Foo(id);
}
assert result != null;
return result;
}
assert
để kiểm tra các tham số phương thức công khai ( docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html ). Điều đó sẽ ném một Exception
thay vì giết chương trình.
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError.
docs.oracle.com/javase/8/docs/technotes/guides/lingu/iêu
Giả sử rằng bạn phải viết một chương trình để điều khiển nhà máy điện hạt nhân. Một điều khá rõ ràng là ngay cả lỗi nhỏ nhất cũng có thể có kết quả thảm khốc, do đó mã của bạn phải không có lỗi (giả sử rằng JVM không có lỗi vì lý do tranh luận).
Java không phải là ngôn ngữ có thể kiểm chứng, điều đó có nghĩa là: bạn không thể tính toán được rằng kết quả hoạt động của bạn sẽ hoàn hảo. Lý do chính cho điều này là con trỏ: chúng có thể chỉ ra bất cứ nơi nào hoặc không nơi nào, do đó chúng không thể được tính là có giá trị chính xác này, ít nhất là không nằm trong một khoảng mã hợp lý. Với vấn đề này, không có cách nào để chứng minh rằng mã của bạn hoàn toàn chính xác. Nhưng những gì bạn có thể làm là chứng minh rằng bạn ít nhất tìm thấy mọi lỗi khi nó xảy ra.
Ý tưởng này dựa trên mô hình Thiết kế theo hợp đồng (DbC): trước tiên bạn xác định (với độ chính xác toán học) phương pháp của bạn sẽ làm gì, sau đó xác minh điều này bằng cách kiểm tra nó trong khi thực hiện thực tế. Thí dụ:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
return a + b;
}
Mặc dù điều này khá rõ ràng để hoạt động tốt, nhưng hầu hết các lập trình viên sẽ không thấy lỗi ẩn bên trong lỗi này (gợi ý: Ariane V bị hỏng vì một lỗi tương tự). Bây giờ DbC định nghĩa rằng bạn phải luôn kiểm tra đầu vào và đầu ra của hàm để xác minh rằng nó hoạt động chính xác. Java có thể làm điều này thông qua các xác nhận:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
final int result = a + b;
assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
return result;
}
Nếu chức năng này bây giờ thất bại, bạn sẽ nhận thấy nó. Bạn sẽ biết rằng có một vấn đề trong mã của bạn, bạn biết nó ở đâu và bạn biết nguyên nhân gây ra nó (tương tự như Ngoại lệ). Và điều thậm chí còn quan trọng hơn: bạn ngừng thực thi ngay khi điều đó xảy ra để ngăn chặn bất kỳ mã nào khác hoạt động với các giá trị sai và có khả năng gây thiệt hại cho bất cứ điều gì nó kiểm soát.
Ngoại lệ Java là một khái niệm tương tự, nhưng chúng không thể xác minh mọi thứ. Nếu bạn muốn kiểm tra nhiều hơn (với chi phí tốc độ thực hiện), bạn cần sử dụng các xác nhận. Làm như vậy sẽ làm phồng mã của bạn, nhưng cuối cùng bạn có thể cung cấp một sản phẩm với thời gian phát triển ngắn đáng ngạc nhiên (bạn càng sớm sửa lỗi, chi phí càng thấp). Và ngoài ra: nếu có bất kỳ lỗi nào trong mã của bạn, bạn sẽ phát hiện ra nó. Không có cách nào để xảy ra lỗi và gây ra sự cố sau này.
Điều này vẫn không đảm bảo cho mã không có lỗi, nhưng nó gần với điều đó hơn các chương trình thông thường.
new IllegalArgumentException
tin nhắn là gì? Ý tôi là, ngoài việc thêm o throws
vào khai báo phương thức và mã để quản lý ngoại lệ đó ở một nơi khác. Tại sao lại assert
bỏ ngoại lệ mới? Hoặc tại sao không phải là một if
thay thế assert
? Không thể thực sự có được điều này :(
a
có thể âm. Khẳng định thứ hai là vô ích; đối với các giá trị int, luôn luôn là trường hợp a + b - b == a. Thử nghiệm đó chỉ có thể thất bại nếu máy tính bị hỏng cơ bản. Để chống lại sự dự phòng đó, bạn cần kiểm tra tính nhất quán trên nhiều CPU.
Các xác nhận là một công cụ giai đoạn phát triển để bắt lỗi trong mã của bạn. Chúng được thiết kế để dễ dàng gỡ bỏ, vì vậy chúng sẽ không tồn tại trong mã sản xuất. Vì vậy, các xác nhận không phải là một phần của "giải pháp" mà bạn cung cấp cho khách hàng. Họ đang kiểm tra nội bộ để đảm bảo rằng các giả định bạn đưa ra là chính xác. Ví dụ phổ biến nhất là kiểm tra null. Nhiều phương pháp được viết như thế này:
void doSomething(Widget widget) {
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
Rất thường xuyên trong một phương thức như thế này, widget sẽ không bao giờ là null. Vì vậy, nếu nó là null, có một lỗi trong mã của bạn ở đâu đó mà bạn cần theo dõi. Nhưng đoạn mã trên sẽ không bao giờ cho bạn biết điều này. Vì vậy, trong một nỗ lực có chủ đích để viết mã "an toàn", bạn cũng đang ẩn một lỗi. Tốt hơn hết là viết mã như thế này:
/**
* @param Widget widget Should never be null
*/
void doSomething(Widget widget) {
assert widget != null;
widget.someMethod(); // ...
... // do more stuff with this widget
}
Bằng cách này, bạn sẽ chắc chắn bắt được lỗi này sớm. (Cũng rất hữu ích khi chỉ định trong hợp đồng rằng tham số này sẽ không bao giờ là null.) Hãy chắc chắn bật các xác nhận khi bạn kiểm tra mã của mình trong quá trình phát triển. (Và thuyết phục đồng nghiệp của bạn làm điều này, thường là rất khó, điều mà tôi thấy rất khó chịu.)
Bây giờ, một số đồng nghiệp của bạn sẽ phản đối mã này, lập luận rằng bạn vẫn nên đưa vào kiểm tra null để ngăn chặn ngoại lệ trong sản xuất. Trong trường hợp đó, khẳng định vẫn hữu ích. Bạn có thể viết nó như thế này:
void doSomething(Widget widget) {
assert widget != null;
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
Bằng cách này, các đồng nghiệp của bạn sẽ rất vui khi kiểm tra null có mã sản xuất, nhưng trong quá trình phát triển, bạn không còn che giấu lỗi khi widget không có giá trị.
Đây là một ví dụ trong thế giới thực: Tôi đã từng viết một phương pháp so sánh hai giá trị tùy ý cho đẳng thức, trong đó một trong hai giá trị có thể là null:
/**
* Compare two values using equals(), after checking for null.
* @param thisValue (may be null)
* @param otherValue (may be null)
* @return True if they are both null or if equals() returns true
*/
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
} else {
result = thisValue.equals(otherValue);
}
return result;
}
Mã này ủy thác công việc của equals()
phương thức trong trường hợp thisValue không null. Nhưng nó giả định equals()
phương thức đáp ứng chính xác hợp đồng equals()
bằng cách xử lý đúng một tham số null.
Một đồng nghiệp đã phản đối mã của tôi, nói với tôi rằng nhiều lớp của chúng tôi có equals()
các phương thức lỗi không kiểm tra null, vì vậy tôi nên đưa kiểm tra đó vào phương thức này. Thật đáng tranh luận nếu điều này là khôn ngoan, hoặc nếu chúng ta nên buộc lỗi, vì vậy chúng ta có thể phát hiện và sửa nó, nhưng tôi đã hoãn lại với đồng nghiệp của mình và đưa vào một kiểm tra null, mà tôi đã đánh dấu bằng một nhận xét:
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
} else {
result = otherValue != null && thisValue.equals(otherValue); // questionable null check
}
return result;
}
Việc kiểm tra bổ sung ở đây, other != null
chỉ cần thiết nếu equals()
phương thức không kiểm tra null theo yêu cầu của hợp đồng.
Thay vì tham gia vào một cuộc tranh luận không có kết quả với đồng nghiệp của tôi về sự khôn ngoan của việc để mã lỗi ở trong cơ sở mã của chúng tôi, tôi chỉ cần đặt hai xác nhận vào mã. Các xác nhận này sẽ cho tôi biết, trong giai đoạn phát triển, nếu một trong các lớp của chúng tôi không thực hiện equals()
đúng, vì vậy tôi có thể sửa nó:
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
assert otherValue == null || otherValue.equals(null) == false;
} else {
result = otherValue != null && thisValue.equals(otherValue);
assert thisValue.equals(null) == false;
}
return result;
}
Những điểm quan trọng cần ghi nhớ là:
Các xác nhận chỉ là các công cụ giai đoạn phát triển.
Điểm của một xác nhận là cho bạn biết nếu có lỗi, không chỉ trong mã của bạn, mà trong cơ sở mã của bạn . (Các xác nhận ở đây sẽ thực sự gắn cờ các lỗi trong các lớp khác.)
Ngay cả khi đồng nghiệp của tôi tự tin rằng các lớp học của chúng tôi được viết đúng, các xác nhận ở đây vẫn sẽ hữu ích. Các lớp mới sẽ được thêm vào mà có thể không kiểm tra null và phương thức này có thể gắn cờ các lỗi đó cho chúng tôi.
Trong quá trình phát triển, bạn phải luôn bật các xác nhận, ngay cả khi mã bạn đã viết không sử dụng các xác nhận. IDE của tôi được đặt để luôn luôn làm điều này theo mặc định cho bất kỳ thực thi mới nào.
Các xác nhận không thay đổi hành vi của mã trong sản xuất, vì vậy đồng nghiệp của tôi rất vui khi kiểm tra null ở đó và phương thức này sẽ thực thi đúng ngay cả khi equals()
phương thức bị lỗi. Tôi rất vui vì tôi sẽ nắm bắt được bất kỳ equals()
phương pháp lỗi nào trong quá trình phát triển.
Ngoài ra, bạn nên kiểm tra chính sách xác nhận của mình bằng cách đưa vào một xác nhận tạm thời sẽ thất bại, do đó bạn có thể chắc chắn rằng bạn được thông báo, thông qua tệp nhật ký hoặc theo dõi ngăn xếp trong luồng đầu ra.
Rất nhiều câu trả lời tốt giải thích những gì assert
từ khóa làm, nhưng ít người trả lời câu hỏi thực sự, "khi nào nên assert
sử dụng từ khóa trong cuộc sống thực?"
Câu trả lời: gần như không bao giờ .
Khẳng định, như một khái niệm, là tuyệt vời. Mã tốt có rất nhiều if (...) throw ...
tuyên bố (và người thân của họ thích Objects.requireNonNull
và Math.addExact
). Tuy nhiên, một số quyết định thiết kế đã hạn chế rất nhiều tiện ích của chính assert
từ khóa .
Ý tưởng lái xe đằng sau assert
từ khóa là tối ưu hóa sớm và tính năng chính là có thể dễ dàng tắt tất cả các kiểm tra. Trong thực tế, assert
kiểm tra được tắt theo mặc định.
Tuy nhiên, điều cực kỳ quan trọng là việc kiểm tra bất biến tiếp tục được thực hiện trong sản xuất. Điều này là do phạm vi kiểm tra hoàn hảo là không thể, và tất cả các mã sản xuất sẽ có lỗi mà các xác nhận sẽ giúp chẩn đoán và giảm thiểu.
Do đó, việc sử dụng if (...) throw ...
nên được ưu tiên, giống như cần thiết để kiểm tra các giá trị tham số của các phương thức công khai và để ném IllegalArgumentException
.
Đôi khi, người ta có thể bị cám dỗ để viết một kiểm tra bất biến mà mất một thời gian dài không mong muốn để xử lý (và thường được gọi là đủ để nó có vấn đề). Tuy nhiên, kiểm tra như vậy sẽ làm chậm thử nghiệm cũng là điều không mong muốn. Kiểm tra tốn thời gian như vậy thường được viết dưới dạng kiểm tra đơn vị. Tuy nhiên, đôi khi nó có thể có ý nghĩa để sử dụng assert
cho lý do này.
Đừng sử dụng assert
đơn giản vì nó sạch hơn và đẹp hơn if (...) throw ...
(và tôi nói điều đó với nỗi đau rất lớn, vì tôi thích sạch sẽ và xinh đẹp). Nếu bạn không thể tự giúp mình và có thể kiểm soát cách ứng dụng của bạn được khởi chạy, thì hãy sử dụng assert
nhưng luôn bật các xác nhận trong sản xuất. Phải thừa nhận rằng đây là những gì tôi có xu hướng làm. Tôi đang thúc đẩy một chú thích lombok sẽ gây ra assert
hành động như thế nào hơn if (...) throw ...
. Bình chọn cho nó ở đây.
. trả giá.)
catch (Throwable t)
. Không có lý do gì để không cố gắng bẫy, đăng nhập hoặc thử lại / khôi phục từ OutOfMemoryError, AssertsError, v.v.
assert
từ khóa là xấu. Tôi sẽ chỉnh sửa câu trả lời của mình để làm rõ hơn rằng tôi đang đề cập đến từ khóa chứ không phải khái niệm.
Đây là trường hợp sử dụng phổ biến nhất. Giả sử bạn đang chuyển sang giá trị enum:
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
}
Miễn là bạn xử lý mọi trường hợp, bạn ổn. Nhưng một ngày nào đó, ai đó sẽ thêm fig vào enum của bạn và quên thêm nó vào câu lệnh switch của bạn. Điều này tạo ra một lỗi có thể gặp khó khăn để bắt, bởi vì các hiệu ứng sẽ không được cảm nhận cho đến khi bạn rời khỏi câu lệnh chuyển đổi. Nhưng nếu bạn viết công tắc của mình như thế này, bạn có thể bắt nó ngay lập tức:
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
default:
assert false : "Missing enum value: " + fruit;
}
AssertionError
xác nhận nếu được kích hoạt ( -ea
). Hành vi mong muốn trong sản xuất là gì? Một thảm họa im lặng và tiềm năng sau này trong vụ hành quyết? Chắc là không. Tôi sẽ đề nghị một rõ ràng throw new AssertionError("Missing enum value: " + fruit);
.
default
để trình biên dịch có thể cảnh báo bạn về các trường hợp mất tích. Bạn có thể return
thay vì break
(điều này có thể cần trích xuất phương thức) và sau đó xử lý trường hợp bị thiếu sau khi chuyển đổi. Bằng cách này bạn có được cả cảnh báo và cơ hội assert
.
Các xác nhận được sử dụng để kiểm tra các điều kiện hậu và "không bao giờ thất bại" các điều kiện trước. Mã chính xác không bao giờ thất bại trong một xác nhận; khi họ kích hoạt, họ sẽ chỉ ra một lỗi (hy vọng tại một địa điểm gần với vị trí thực tế của vấn đề).
Một ví dụ về khẳng định có thể là kiểm tra xem một nhóm phương thức cụ thể được gọi theo đúng thứ tự chưa (ví dụ: hasNext()
được gọi trước next()
trong một Iterator
).
Từ khóa khẳng định trong Java làm gì?
Chúng ta hãy nhìn vào mã byte được biên dịch.
Chúng tôi sẽ kết luận rằng:
public class Assert {
public static void main(String[] args) {
assert System.currentTimeMillis() == 0L;
}
}
tạo ra gần như chính xác mã byte như:
public class Assert {
static final boolean $assertionsDisabled =
!Assert.class.desiredAssertionStatus();
public static void main(String[] args) {
if (!$assertionsDisabled) {
if (System.currentTimeMillis() != 0L) {
throw new AssertionError();
}
}
}
}
nơi Assert.class.desiredAssertionStatus()
là true
khi-ea
được truyền trên dòng lệnh, và sai khác.
Chúng tôi sử dụng System.currentTimeMillis()
để đảm bảo rằng nó sẽ không được tối ưu hóa (assert true;
đã).
Trường tổng hợp được tạo để Java chỉ cần gọi Assert.class.desiredAssertionStatus()
một lần khi tải và sau đó lưu trữ kết quả ở đó. Xem thêm: Ý nghĩa của "tổng hợp tĩnh" là gì?
Chúng tôi có thể xác minh rằng với:
javac Assert.java
javap -c -constants -private -verbose Assert.class
Với Oracle JDK 1.8.0_45, một trường tĩnh tổng hợp đã được tạo (xem thêm: Ý nghĩa của "tổng hợp tĩnh" là gì? ):
static final boolean $assertionsDisabled;
descriptor: Z
flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
cùng với một trình khởi tạo tĩnh:
0: ldc #6 // class Assert
2: invokevirtual #7 // Method java/lang Class.desiredAssertionStatus:()Z
5: ifne 12
8: iconst_1
9: goto 13
12: iconst_0
13: putstatic #2 // Field $assertionsDisabled:Z
16: return
và phương pháp chính là:
0: getstatic #2 // Field $assertionsDisabled:Z
3: ifne 22
6: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
9: lconst_0
10: lcmp
11: ifeq 22
14: new #4 // class java/lang/AssertionError
17: dup
18: invokespecial #5 // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return
Chúng tôi kết luận rằng:
assert
: đó là một khái niệm ngôn ngữ Javaassert
có thể được mô phỏng khá tốt với các thuộc tính hệ thống -Pcom.me.assert=true
để thay thế -ea
trên dòng lệnh và a throw new AssertionError()
.catch (Throwable t)
điều khoản có thể bắt vi phạm khẳng định quá? Đối với tôi, giới hạn tiện ích của họ chỉ trong trường hợp cơ thể của xác nhận là tốn thời gian, điều này rất hiếm.
AssertionError
đầu tiên và suy nghĩ lại.
Một ví dụ trong thế giới thực, từ một lớp Stack (từ Xác nhận trong các bài viết Java )
public int pop() {
// precondition
assert !isEmpty() : "Stack is empty";
return stack[--num];
}
Một xác nhận cho phép phát hiện các khiếm khuyết trong mã. Bạn có thể bật các xác nhận để kiểm tra và gỡ lỗi trong khi tắt chúng khi chương trình của bạn đang được sản xuất.
Tại sao khẳng định một cái gì đó khi bạn biết nó là sự thật? Nó chỉ đúng khi mọi thứ đang hoạt động đúng. Nếu chương trình có một khiếm khuyết, nó có thể không thực sự đúng. Phát hiện điều này sớm hơn trong quá trình cho phép bạn biết có gì đó không ổn.
Một assert
tuyên bố có chứa tuyên bố này cùng với một String
thông điệp tùy chọn .
Cú pháp cho một câu lệnh khẳng định có hai dạng:
assert boolean_expression;
assert boolean_expression: error_message;
Dưới đây là một số quy tắc cơ bản chi phối nơi xác nhận nên được sử dụng và nơi không nên sử dụng. Các xác nhận nên được sử dụng cho:
Xác thực các tham số đầu vào của một phương thức riêng. KHÔNG cho các phương pháp công cộng. public
phương pháp nên ném ngoại lệ thường xuyên khi thông qua các tham số xấu.
Bất cứ nơi nào trong chương trình để đảm bảo tính hợp lệ của một thực tế gần như chắc chắn là đúng.
Ví dụ: nếu bạn chắc chắn rằng nó sẽ chỉ là 1 hoặc 2, bạn có thể sử dụng một xác nhận như thế này:
...
if (i == 1) {
...
}
else if (i == 2) {
...
} else {
assert false : "cannot happen. i is " + i;
}
...
Các xác nhận không nên được sử dụng cho:
Xác thực các tham số đầu vào của một phương thức công khai. Vì các xác nhận có thể không luôn luôn được thực thi, nên sử dụng cơ chế ngoại lệ thông thường.
Xác thực các ràng buộc trên một cái gì đó được nhập bởi người dùng. Giống như trên.
Không nên được sử dụng cho các tác dụng phụ.
Ví dụ, đây không phải là một cách sử dụng đúng vì ở đây khẳng định được sử dụng cho tác dụng phụ của việc gọi doSomething()
phương thức.
public boolean doSomething() {
...
}
public void someMethod() {
assert doSomething();
}
Trường hợp duy nhất mà điều này có thể được biện minh là khi bạn đang cố gắng tìm hiểu xem liệu các xác nhận có được bật trong mã của bạn hay không:
boolean enabled = false;
assert enabled = true;
if (enabled) {
System.out.println("Assertions are enabled");
} else {
System.out.println("Assertions are disabled");
}
Ngoài tất cả các câu trả lời tuyệt vời được cung cấp ở đây, hướng dẫn lập trình Java SE 7 chính thức có một hướng dẫn khá súc tích về cách sử dụng assert
; với một vài ví dụ điển hình về việc khi nào đó là một ý tưởng tốt (và quan trọng là xấu) để sử dụng các xác nhận và cách nó khác với việc ném ngoại lệ.
Khẳng định là rất hữu ích khi phát triển. Bạn sử dụng nó khi điều gì đó không thể xảy ra nếu mã của bạn hoạt động chính xác. Nó dễ sử dụng và có thể tồn tại trong mã mãi mãi, vì nó sẽ bị tắt trong cuộc sống thực.
Nếu có bất kỳ cơ hội nào mà tình trạng có thể xảy ra trong cuộc sống thực, thì bạn phải xử lý nó.
Tôi thích nó, nhưng không biết làm thế nào để bật nó trong Eclipse / Android / ADT. Nó dường như được tắt ngay cả khi gỡ lỗi. (Có một luồng về vấn đề này, nhưng nó đề cập đến 'Java vm', không xuất hiện trong Cấu hình chạy ADT).
Đây là một khẳng định tôi đã viết trong một máy chủ cho dự án Hibernate / SQL. Một bean thực thể có hai thuộc tính boolean hiệu quả, được gọi là isActive và isDefault. Mỗi có thể có một giá trị "Y" hoặc "N" hoặc null, được coi là "N". Chúng tôi muốn đảm bảo ứng dụng khách trình duyệt được giới hạn ở ba giá trị này. Vì vậy, trong setters của tôi cho hai thuộc tính này, tôi đã thêm khẳng định này:
assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;
Lưu ý những điều sau.
Khẳng định này chỉ dành cho giai đoạn phát triển. Nếu khách hàng gửi một giá trị xấu, chúng tôi sẽ bắt sớm và sửa nó, rất lâu trước khi chúng tôi đạt được sản xuất. Các xác nhận là dành cho các khuyết điểm mà bạn có thể nắm bắt sớm.
Khẳng định này là chậm và không hiệu quả. Không sao đâu. Khẳng định là miễn phí để được chậm. Chúng tôi không quan tâm vì chúng là công cụ chỉ phát triển. Điều này sẽ không làm chậm mã sản xuất vì các xác nhận sẽ bị vô hiệu hóa. (Có một số bất đồng về điểm này, mà tôi sẽ nói sau.) Điều này dẫn đến điểm tiếp theo của tôi.
Khẳng định này không có tác dụng phụ. Tôi có thể đã kiểm tra giá trị của mình so với Bộ cuối cùng tĩnh không thể thay đổi, nhưng bộ đó sẽ tồn tại trong sản xuất, nơi nó sẽ không bao giờ được sử dụng.
Khẳng định này tồn tại để xác minh hoạt động đúng của khách hàng. Vì vậy, khi chúng tôi đạt được sản xuất, chúng tôi sẽ chắc chắn rằng khách hàng đang hoạt động đúng, vì vậy chúng tôi có thể tắt xác nhận một cách an toàn.
Một số người hỏi điều này: Nếu khẳng định không cần thiết trong sản xuất, tại sao bạn không loại bỏ chúng khi bạn hoàn thành? Bởi vì bạn vẫn sẽ cần chúng khi bạn bắt đầu làm việc trên phiên bản tiếp theo.
Một số người đã lập luận rằng bạn không bao giờ nên sử dụng các xác nhận, bởi vì bạn không bao giờ có thể chắc chắn rằng tất cả các lỗi đã biến mất, vì vậy bạn cần phải giữ chúng xung quanh ngay cả trong sản xuất. Và do đó, không có điểm nào trong việc sử dụng tuyên bố khẳng định, vì lợi thế duy nhất để khẳng định là bạn có thể tắt chúng đi. Do đó, theo suy nghĩ này, bạn nên (gần như) không bao giờ sử dụng các khẳng định. Tôi không đồng ý. Điều chắc chắn là nếu một bài kiểm tra thuộc về sản xuất, bạn không nên sử dụng một xác nhận. Nhưng bài kiểm tra này không thuộc về sản xuất. Đây là một lỗi để bắt lỗi không thể sản xuất được, vì vậy nó có thể bị tắt một cách an toàn khi bạn hoàn thành.
BTW, tôi có thể đã viết nó như thế này:
assert value == null || value.equals("Y") || value.equals("N") : value;
Điều này tốt cho chỉ ba giá trị, nhưng nếu số lượng giá trị có thể lớn hơn, phiên bản Hashset trở nên thuận tiện hơn. Tôi đã chọn phiên bản Hashset để đưa ra quan điểm của mình về hiệu quả.
HashSet
mang lại bất kỳ lợi thế tốc độ hơn một ArrayList
. Hơn nữa, các sáng tạo tập hợp và danh sách thống trị thời gian tra cứu. Họ sẽ ổn khi sử dụng hằng số. Tất cả đã nói, +1.
Xác nhận về cơ bản được sử dụng để gỡ lỗi ứng dụng hoặc nó được sử dụng để thay thế xử lý ngoại lệ cho một số ứng dụng để kiểm tra tính hợp lệ của ứng dụng.
Khẳng định hoạt động trong thời gian chạy. Một ví dụ đơn giản, có thể giải thích toàn bộ khái niệm rất đơn giản, nằm ở đây - Từ khóa khẳng định làm gì trong Java? (WikiAnswers).
Các xác nhận được tắt theo mặc định. Để kích hoạt chúng, chúng ta phải chạy chương trình với -ea
các tùy chọn (độ chi tiết có thể thay đổi). Ví dụ , java -ea AssertionsDemo
.
Có hai định dạng để sử dụng các xác nhận:
assert 1==2; // This will raise an AssertionError
.assert 1==2: "no way.. 1 is not equal to 2";
Điều này sẽ đưa ra một AssertsError với thông báo được hiển thị quá và do đó tốt hơn. Mặc dù cú pháp thực tế là assert expr1:expr2
nơi expr2 có thể là bất kỳ biểu thức nào trả về giá trị, tôi đã sử dụng nó thường xuyên hơn chỉ để in một tin nhắn.Tóm lại (và điều này đúng với nhiều ngôn ngữ không chỉ Java):
"khẳng định" chủ yếu được sử dụng như một công cụ gỡ lỗi của các nhà phát triển phần mềm trong quá trình gỡ lỗi. Tin nhắn khẳng định sẽ không bao giờ xuất hiện. Nhiều ngôn ngữ cung cấp tùy chọn thời gian biên dịch sẽ khiến tất cả các "xác nhận" bị bỏ qua, để sử dụng trong việc tạo mã "sản xuất".
"ngoại lệ" là một cách thuận tiện để xử lý tất cả các loại điều kiện lỗi, cho dù chúng có biểu hiện lỗi logic hay không, bởi vì, nếu bạn gặp phải tình trạng lỗi để bạn không thể tiếp tục, bạn có thể chỉ cần "ném chúng lên không trung" "Từ bất cứ nơi nào bạn đến, mong đợi một người khác ở ngoài đó sẵn sàng" bắt "họ. Kiểm soát được chuyển trong một bước, trực tiếp từ mã đã ném ngoại lệ, thẳng đến mitt của người bắt. (Và người bắt có thể thấy toàn bộ phần sau của các cuộc gọi đã diễn ra.)
Hơn nữa, những người gọi chương trình con đó không phải kiểm tra xem liệu chương trình con có thành công hay không: "nếu chúng ta ở đây bây giờ, nó đã thành công, vì nếu không nó sẽ ném một ngoại lệ và chúng ta sẽ không ở đây ngay bây giờ!" Chiến lược đơn giản này làm cho thiết kế mã và gỡ lỗi dễ dàng hơn nhiều.
Các ngoại lệ thuận tiện cho phép các điều kiện lỗi nghiêm trọng là chính chúng: "ngoại lệ cho quy tắc". Và, để chúng được xử lý bởi một đường dẫn mã cũng là "một ngoại lệ đối với quy tắc ... " bóng bay! "
Các xác nhận là các kiểm tra có thể bị tắt. Chúng hiếm khi được sử dụng. Tại sao?
result != null
vậy rất nhanh và hầu như không có gì để lưu.Vì vậy, những gì còn lại? Kiểm tra tốn kém cho các điều kiện thực sự dự kiến là đúng. Một ví dụ điển hình là các bất biến của cấu trúc dữ liệu như cây RB. Trên thực tế, trong ConcurrentHashMap
JDK8, có một vài khẳng định có ý nghĩa như vậy cho TreeNodes
.
Đôi khi, séc không thực sự đắt tiền, nhưng đồng thời, bạn khá chắc chắn, nó sẽ vượt qua. Trong mã của tôi, có ví dụ,
assert Sets.newHashSet(userIds).size() == userIds.size();
nơi tôi khá chắc chắn rằng danh sách tôi vừa tạo có các yếu tố độc đáo, nhưng tôi muốn ghi lại và kiểm tra lại nó.
Về cơ bản, "khẳng định đúng" sẽ vượt qua và "khẳng định sai" sẽ thất bại. Hãy xem cách nó sẽ hoạt động:
public static void main(String[] args)
{
String s1 = "Hello";
assert checkInteger(s1);
}
private static boolean checkInteger(String s)
{
try {
Integer.parseInt(s);
return true;
}
catch(Exception e)
{
return false;
}
}
assert
là một từ khóa. Nó được giới thiệu trong JDK 1.4. Có hai loại assert
s
assert
Báo cáo rất đơn giảnassert
câu nói đơn giản .Theo mặc định, tất cả các assert
câu lệnh sẽ không được thực thi. Nếu một assert
tuyên bố nhận được sai, thì nó sẽ tự động đưa ra một lỗi xác nhận.