Mục lục bài họcĐang ở d01-b4
Math, Wrapper Classes, Casting and Overflow
Math, wrapper classes, casting, and the edge of int
Math is a class of static methods, so you call them on the class name. The four in the AP subset are Math.abs, Math.pow, Math.sqrt and Math.random. Math.pow and Math.sqrt always return a double, even for Math.pow(2, 3), which prints as 8.0.
Math.random() returns a double in the interval from 0 (included) to 1 (excluded). The pattern for a random integer from low to high inclusive is:
(int) (Math.random() * (high - low + 1)) + low
The + 1 is there because the count of integers from low to high inclusive is high - low + 1.
Casting with (int) truncates toward zero; it does not round. It also binds tightly, so (int) 7.9 / 2 is 7 / 2 = 3, while (int) (7.9 / 2) is (int) 3.95 = 3 as well but for a different reason — change the numbers and the two differ. Integer division discards the remainder, so 1 / 2 * 3.0 is 0.0.
Wrapper classes Integer and Double let a primitive be treated as an object, and they carry the constants Integer.MAX_VALUE and Integer.MIN_VALUE. Arithmetic that passes those limits does not throw an exception: it wraps around silently.
Math là lớp toàn phương thức static, nên gọi trên tên lớp. Bốn phương thức có trong đề AP là Math.abs, Math.pow, Math.sqrt và Math.random. Math.pow và Math.sqrt luôn trả về double, kể cả Math.pow(2, 3) — in ra là 8.0 chứ không phải 8.
Math.random() trả về một double trong khoảng từ 0 (lấy) tới 1 (không lấy). Khuôn lấy số nguyên ngẫu nhiên từ low tới high, hai đầu đều lấy:
(int) (Math.random() * (high - low + 1)) + low
Có chữ + 1 vì đếm số nguyên từ low tới high kể cả hai đầu thì được high - low + 1 giá trị.
Ép kiểu bằng (int) là cắt phần thập phân về phía số 0, không phải làm tròn: (int) 3.99 ra 3 và (int) (-3.99) ra −3. Ép kiểu bám rất chặt vào toán hạng ngay sau nó, nên (int) 7.9 / 2 nghĩa là 7 / 2, khác hẳn (int) (7.9 / 2). Chia hai số nguyên thì phần dư bị vứt, vì thế 1 / 2 * 3.0 ra 0.0 chứ không phải 1.5.
Lớp bao Integer và Double cho phép coi một giá trị nguyên thuỷ như một đối tượng, và mang theo hai hằng số Integer.MAX_VALUE (2147483647) cùng Integer.MIN_VALUE. Phép tính vượt qua hai mốc đó không ném ngoại lệ nào — nó lặng lẽ quay vòng sang đầu bên kia.
int có đúng 2^32 = 4294967296 giá trị xếp thành một vòng tròn. Cộng vượt qua đầu bên phải thì đi tiếp từ đầu bên trái, nên kết quả thật bằng kết quả toán học trừ đi 4294967296: 4000000000 - 4294967296 = -294967296. Điều nguy hiểm là không có ngoại lệ nào được ném ra — chương trình chạy tiếp với một con số âm vô lý. Muốn chặn thì phải ép sang long trước phép tính; ép sau khi tính xong là đã muộn.Xác định giá trị in ra của từng dòng và giải thích chỗ dễ trượt.
System.out.println(Math.pow(3, 2) + " " + (int) Math.pow(3, 2)); System.out.println((int) (3.7 + 3.7) + " " + ((int) 3.7 + (int) 3.7)); System.out.println(7 / 2 + " " + 7 / 2.0 + " " + (double) (7 / 2)); int n = Integer.MAX_VALUE; System.out.println(n + 1);
Dòng 1. Math.pow luôn trả double nên vế đầu in 9.0; ép (int) thì thành 9. Kết quả: 9.0 9.
Dòng 2. Vế đầu cộng trước rồi mới cắt: (int) 7.4 ra 7. Vế sau cắt từng số trước rồi mới cộng: 3 + 3 ra 6. Kết quả: 7 6. Hai vế lệch nhau 1 vì phần thập phân bị vứt hai lần.
Dòng 3. 7 / 2 là chia nguyên nên ra 3. 7 / 2.0 có một toán hạng thực nên ra 3.5. Còn (double) (7 / 2) chia nguyên xong mới nâng kiểu, nên ra 3.0 — nâng kiểu muộn không cứu lại được phần đã mất. Kết quả: 3 3.5 3.0.
Dòng 4. Vượt mốc nên quay vòng, in ra -2147483648. Không có ngoại lệ nào.
Bốn dòng này gói gọn ba lỗi hay mất điểm nhất: quên double của Math, cắt trước khi cộng, và nâng kiểu sau khi đã chia nguyên.
double tb = (double) (tong / soCau); trông rất hợp lý nhưng đã chia nguyên xong rồi mới nâng kiểu, nên trung bình 8,75 hiện ra thành 8,0. Phải viết (double) tong / soCau. Cùng một họ với nó là tràn số: int vượt 2147483647 thì quay vòng lặng lẽ, cách chặn duy nhất là ép (long) lên một toán hạng trước khi cộng hoặc nhân. Và nhớ rằng (int) cắt chứ không làm tròn — muốn tròn thì dùng Math.round hoặc cộng thêm 0,5 rồi mới cắt (với số dương).Math.pow và Math.sqrt trả double. (int) cắt về phía 0, không làm tròn, và bám chặt vào toán hạng ngay sau. Nâng kiểu phải làm trước phép chia. Tràn int quay vòng im lặng, chặn bằng (long) đặt trước phép tính.Đọc xong rồi — làm thử ngay
Bài tập của chương Unit 1 — Using Objects and Methods gồm 14 câu trắc nghiệm và 8 đề tự luận. Đáp án hiện ngay khi chọn, miễn phí.