LẬP TRÌNH JAVA HIỆU QUA
Tac gia: Joshua Bloch Trình bày: Lê Quốc Anh
30-05-2015
Trang 2VN-Có nhận xét gì về đoạn code này ?
List<Integer> lst = new ArrayList<Integer>() ;
Trang 3Có gì khác nhau?
List<Integer> lst = new List<Integer> lst = new
ArrayList<Integer>() ; LinkedList<Integer>() ;
Iterator<Integer> iter = Iterator<Integer> iter =
1st.iterator(); Let., iterator ();
while (iter.hasNext()) { while (iter.hasNext()) {
AL aele TẤN
iter.remove () ; iter remove () ;
Trang 4So sánh thời gian thực hiện remove ArrayList LinkedList 4 Lst.Size() Lst.Size() Ạ | Ẻ > — ——>
Running time Running time
Trang 7)
Trang 8Effective Java
Trang 9
PHAN 1: TAO VA HUY ĐỐI
Trang 10Quy tắc 1: Dùng hàm gọi đối tượng
» _ Định nghĩa: Là hàm trả lại một đối tượng của lớp
public static Boolean valueOf(boolean b) {return b ? Boolean.TRUE : Boolean.FALSE;
* Loi ich:
— Không cần phải khởi tạo lớp mỗi lần gọi
— Gọi đến cùng một đối tượng (ví dụ Singleton ở phần sau)
— Dé doc (ham cé tén gọi, rút gọn tham số, etc ) Ví dụ:
“Cách bình thường: Map<String, List<String>> m=new HashMap<String, List<String>>();
*Với hàm khởi tạo: Map<String, List<String>> m=HashMap.newlnstance();
* Quy udc:
— lớp chứa hàm kiểu này không nên được khởi tạo trực tiếp nữa, vì đã có hàm khởi tạo va tra
lại đối tượng của lớp rồi Do vậy lớp hoặc constructor của lớp được khai báo private
— _ Nếu đối tượng trả về có tên gọi là Type thì lớp chứa hàm factory sé có tên la Types Ví dụ
public interface book { public void cover(); }
public abstract class Books() { public static Book createBook(){ return new MyBook(); } private class MyBook implements Book{ public void cover(){} } }
Trang 11Quy tắc 1: Dùng hàm gọi đối tượng
° Điểm hạn chế:
— Lớp chứa các static factory methods không cho phép
kế thừa Ví dụ không thể kế thừa lớp
java.util.Collections (theo quy ước lớp này có giao
diện tên là java.util.Collection không có s) Nguyên nhân là để các lập trình viên nên dùng composition thay vì inheritance mà ta sẽ xem trong Quy tắc 16
— Không có sự khác biệt rõ ràng giữa các hàm static
Trang 12Quy tắc 2: Dùng builder cho lớp có
nhiều tham số
* _ Xem xét lỚp:
public class NutritionFacts (
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional private final int carbohydrate; // (g) optional
}
NutritionFacts cocaCola = new NutritionFacts(size, serving, calos, fat, sodium, carbo);
* Hạn chế: Nhiều tham số tùy chọn nhưng vẫn phải khai báo hết khi khởi tạo
* Giải pháp 1: Khởi tạo đối tượng và dùng hàm setter
— Vidu public void setFat(int val) { fat = val; }
— _ Không khả thi cho lớp bất biến
* _ Giải pháp 2: Dùng lớp trung gian builder để tùy chọn khai báo tham số
public class NutritionFacts { public static class Builder {
Trang 13Quy tắc 2: Dùng builder cho lớp có
nhiều tham số
public class NutritionFacts {
public static class Builder {
}
private final int servingSize; // bat budc
private int calories = 0; // tùy chọn, khởi tạo với giá trị mặc định private int fat = 0; // tùy chọn, khởi tạo với giá trị mặc định
public Builder(int servingSize) {this.servingSize = servingSize; } public Builder calories(int val) { calories = val; return this; } public Builder fat(int val) { fat = val; return this; }
Trang 14Quy tắc 3:Xây dựng lớp Singleton với kiểu Enum
* - Câu hỏi: Singleton khác với lớp bất biến???
*- Định nghĩa: Lớp chỉ được khởi tạo 1 lần duy nhất Ví dụ:
// Singleton with static factory public class Elvis {
private static final Elvis INSTANCE = new Elvis(); private Elvis() { }
public static Elvis getInstance() { return INSTANCE; } public void leaveTheBuilding() { }
Trang 15Quy tắc 3:Xây dựng lớp Singleton với kiểu Enum
// Singleton with static factory
public class Elvis implements Serializable {
private transient String[] songs= {« a», « b »};
private static final Elvis INSTANCE = new Elvis(); private Elvis() { }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() { }
// readResolve method to preserve singleton property
private Object readResolve() {
// Return the one true Elvis and let the garbage collector // take care of the Elvis impersonator
return INSTANCE;
}
*Deserialization sẽ gọi hàm constructor để tạo ra đối tượng mới ngay cả khi nó được khai báo private
*readResolve() bình thường sẽ trả lại đối tượng mới được tạo mới sau khi deserialized => viết lại
Trang 16Quy tắc 3:Xây dựng lớp Singleton với kiểu Enum
+ Giải pháp ngắn gọn và an toàn hơn là dùng Enum
public enum Elvis {
INSTANCE;
private transient String[] songs= {« a », « b »};
public void leaveTheBuilding() { }
}
Trang 17Quy tắc 4: Cấm khởi tạo lớp với
private constructor
*_ Đôi khi ta muốn xây dựng lớp chỉ chứa các
static methods hay static fields
* Giải pháp khai báo lớp kiểu trừu tượng
Abstract không hiệu quả vì lớp con của nó
vẫn có thể được khởi tạo
* Giải pháp triệt để là khai báo lớp với private
Trang 18Quy tắc 5: Tránh tạo đối tượng thừa
* Vi du: String s = new String("stringette"); — Có đến 2 đối tượng được tạo ra
— Tương đương: String s = "stringette”;
° Giải pháp chung:
— Str dung static factory methods (Quy tac 1) — Sử dụng phương phap Jazy initialization
Trang 19Quy tắc 6: Loại bỏ các tham chiếu
đến đối tượng không dùng nữa
° Xét vi du sau:
public class Stack {
private Object[] elements; private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public Object pop() {
if (size == 0) throw new EmptyStackException();
Trang 20Quy tắc 6: Loại bỏ các tham chiếu
đến đối tượng không dùng nữa
° Xét ví dụ sau:
public Object pop() {
if (size == 0) throw new EmptyStackException();
Object result = elements|[ size];
elements[size] = null; // Eliminate obsolete reference return result;
}
* Giải pháp: chú ý giải pháp bộ nhớ khi xây
Trang 21Quy tắc 7: Tránh sử dụng finalizers
° Định nghĩa: Garbage collector (GC) trong Java phụ trách giải phóng các đối tượng không còn có thể được sử dụng (unreacheable) Trước khi giải phóng đối tượng thì hàm finalize() của đối tượng đó sẽ được gọi
»- Sử dụng finalizers nguy hiểm? Vì sao?
— Không dự đoán được kết quả: thời gian từ lúc một đối tượng thành unreachable cho tới lúc được GC xử lý là không xác định — Ngay cả ép chạy GC bằng lệnh System.gc vì nó cũng không
đảm bảo để finalize() của đối tượng được thực thi tức thời
— Sử dụng finalize() làm tăng thời gian thực hiện lên 430 lần (cần
Trang 22Quy tac 7: Tránh sử dụng
finalizers
* Giải pháp
— Thay thế bằng try finally
— Hoặc gọi lệnh System.runFinalizersOnexit hoặc
Runtime.runFinalizersOnExit Tuy nhiên cả 2 hàm
này không còn được sử dụng (deprecated) bởi lý
do nó có thể được gọi ngay cả với đối tượng reacheable => gây ra các hành vi bất thường
Trang 23Quy tắc 7: Tránh sử dụng
finalizers
*_ Giải pháp
E Nếu buộc phải sử dụng, phải đảm bảo finalize() luôn được gọi
cả ở lớp cha (superclass) và lớp con (subclass) Ví dụ
@Override protected void finalize() throws Throwable { try{
// Finalize subclass state } finally {
super finalize(); } }
— Hoặc sử dụng phương pháp finalizer guardian để đảm bảo finalize() luôn được gọi Ví dụ:
public class Foo {
Foo object private final Object finalizerGuardian = new
Object() {
@Override protected void finalize() throws Throwable { }
Trang 24
PHẦN 2: PHƯƠNG THỨC
Trang 25Quy tắc 8: Điều kiện khi viết lại hàm
equals()
° Trước hết trả lời câu hỏi liệu việc viết lại hàm equals có cần thiết không???
Co hai loai lop (value classes vs activity classes) Doi voi mot activity class nhu lop Thread thi khong can phai viet lai
Khong can quan tam lieu ham equals hien co co hop ly khong Vi du nhu viet lai ham equals cho lop Random de bat buoc 2 the hien cua lop Random duoc cho la bang nhau neu va chi neu
chung san xuat mot chuoi cac gia tri giong nhau Cai nay phai xem lai nhu cau co can thiet ko
Cac lop cua Set: HashSet, TreeSet, hay cac lop cua List:
ArrayList, LinkedList, da viet de ham equals thich hop cho chung roi thi cung ko nen viet lai
Trang 26Quy tắc 8: Điều kiện khi viết lại hàm
equals()
+ Nếu cần viết lại hàm equals thì phải tuân thủ 5 quy định sau:
1 Phan chieu: No phai bang no, x.equals(x) luon dung
2 Doi xung: x.equals(y) dung khi va chi khi y.equals(x) cung dung 3 Bac cau: Neu x.equals(y) va y.equals(z) dung thi x.equals(z) cung
dung
4 Vung chac: Neu x va y la cac gia tri khong null thi x.equals(y) se phai luon tra lai gia tri nhu nhau moi khi duoc goi den cho du cac gia tri khong thuoc equals cua lop x va y co the bi thay doi Loi khuyen la khong neu dung cac resources khong tin cay de xay dung ham equals
Trang 27Quy tắc 8: Điều kiện khi viết lại hàm
equals()
* - Cách xây dựng hàm equals hiệu quả:
— Dau tien kiem tra bang toan tu == xem lieu chung co reference
den cung doi tuong khong (vd: if (o == this) return true;)
— Kiem tra xem chung co cung kieu khong bang toan tu
instanceof (vd: if (!(o instanceof PhoneNumber)) return false;) — Ep kieu de chung ve cung kieu (vd: PhoneNumber pn =
(PhoneNumber)o;)
— So sanh tung doi so (argument) xem chung co tuong ung voi
nhau khong (vd: return pn.lineNumber == lineNumber &&
pn.prefix == prefix && pn.areaCode == areaCode;)
— Sau khi viet xong ham equals, kiem tra lai xem no co tuan thu 5 quy dinh khong
Trang 28Quy tắc 9: Luôn viết lại hashCode()
sau khi thay đổi hàm equals()
1 Trong một phiên làm việc nếu hàm hashCode được gọi lại nhiều lần thì kết quả trả lại đều phải như nhau Tuy nhién, néu đó là 2 phiên làm việc khác nhau thì có thể cho kết quả hashCode khác nhau
2 Nếu hai đối tượng x, y được tính là bằng nhau theo hàm
X equals(y)==true, thì x hashCode() == y.hashCode()
3 Ngược lại, nếu x equals(y) == false, thi ham hashCode() cUa x va y không nhất thiễt phải khác nhau
Trang 29Quy tắc 9: Luôn viết lại hashCode()
sau khi thay đổi hàm equals()
Điều 2 « đối tượng bằng nhau theo equals thì hashCode phải bằng nhau» hay bị vi phạm khi ta viết lại hàm equals Ví dụ:
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof PhoneNumber)) return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNumber ==lineNumber && pn.prefix
== prefix && pn.areaCode == areaCode;
}
Map<PhoneNumber, String> m =new
HashMap<PhoneNumber, String>(); m.put(new PhoneNumber(707, 867, 5309), "Jenny");
Chúng ta hi vọng lời gọi m.get(new PhoneNumber(707 , 867,
Trang 30Quy tắc 9: Luôn viết lại hashCode()
sau khi thay đổi hàm equals()
* Cach giai quyet don gian nhat la viet lai ham hashCode nhu sau:
@Override public int hashCode() {
return 42;
}
Trang 31Quy tắc 9: Luôn viết lại hashCode()
sau khi thay đổi hàm equals()
Cach giai quyet don gian nhat la viet lai ham hashCode nhu
sau:
@Override public int hashCode() { return 42; }
Cach lam nhu tren co on thoa khong?
Khong, vi nhu vay tat ca cac doi tuong se co cung gia tri
hashCode Khi do bang bam se phai su dung danh sach lien
ket (linkedList) de tim kiem doi tuong va do do lam giam hieu
suat tim kiem
Giai phap hay nhat la xay dung hashCode sao cho hai doi tuong khong bang nhau se ko co chung gia tri hashCode =>
Trang 32Quy tắc 9: Luôn viết lại hashCode()
sau khi thay đổi hàm equals()
* Nguyen tac xay dung an toan ham hashCode:
— la chi su dung cac gia tri dung trong ham equals
— Can doi giua running time tong the he thong so voi running time cua ham hashCode Loi khuyen la khong nen ngai su dung tỉnh toan phục tap hashCode
— Neu viec tinh toan hashCode phuc tap, khong nen tinh lai no nhieu lan cho mot doi tuong, cung nhu chi tinh toan no khi doi tuong duoc tao ra Vi du:
private volatile int hashCode; // (Xem quy tac 71)
@Override public int hashCode() {
int result = hashCode;
if (result == 0) { result = 17; result = 31 * result + areaCode;
result = 31 * result + prefix; result = 31 * result + lineNumber;
}
Trang 33Quy tắc 10: Nên viết lại toString()
* ham toString nen tra lai toan bo thong tin ve doi tuong * viet tai lieu ro rang ve dinh dang du lieu tra ve cua
toString- cung cap ham truy cap den tat ca cac thong tin
tra ve boi toString
— LTV khong can phai xu ly chuoi String ket xuat thong tin nao do — Kho kiem soat khi dinh dang du lieu tra ve cua toString thay
doi
— Vi du, binh thuong lop PhoneNumber.toString se tra lai
"PhoneNumber@14845" tuong ung "ten lop@gia tri hashCode viet duoi dang unsigned hexadecimal" Chung ta co the viet lai ham toString ro rang hon de tra lai gia tri vi du "(0084) 043 -
Trang 34Quy tắc 11: Cẩn thận khi viết lại clone
* Định nghĩa: tao ra mot doi tuong moi voi tat ca thuoc tinh giong y chang mot doi tuong khac ma khong can goi den ham khoi tao
* Nguyen tac viet ham clone:
— x.clone() != x: Day phai hoan toan la 2 doi tuong doc lap nhau Chu y: cac doi tuong mutables co cau truc "deep structure" can duoc sao chep theo phuong
phap "deep copy"
— x.clone().getClass() == x.getClass(): chu y super.clone phai duoc khai bao trong ham clone
Trang 35Quy tắc 12: Thực thi Comparable
* Dinh nghia: Lop a thuc thi Comparable va viet lai ham compareTo() Khi do Array.sort(dsa) se sap xep danh sach cac doi tuong cua lop a theo tieu chi cua ham
compareTo()
* Nguyen tac xay dung ham compareTo()
— Doi xung: sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) dung cho moi x, y — Bac cau: (x.compareTo(y)>0 && y.compareTo(z)>0) keo theo x.compareTo(z)>0 — Vung chac: Neu (x.compareTo(y)==0) thi (x.compareTo(z) == y.compareTo(z))
Trang 37Quy tắc 13: Hạn chế truy cập lớp và
các thành viên bên trong lớp
Mot modul duoc cho la thiet ke tot neu no ẩn duoc cac du lieu va cac ham xu ly du lieu cua no khoi su truy cap boi cac module khac (ie., tinh dong goi) => de phat trien song song, cai tien doc lap, de
phat hien loi, de bao tri,
Uu tien neu co the khai bao lop hoac giao dien la package-private
class/interface Boi vi neu khai bao public, ban bat buoc phai bao
tri tinh tuong thich cua no mai mai do no co the duoc su dung o dau do ben ngoai package
ngay ca cach viet hang so cho du lieu mang (ie public static final array) cung khong dam bao noi dung khong bi sua doi: Nen dung ham tra ve doi tuong Vi du dung:
private static final Thing[] PRIVATE_VALUES = { }; public static final List<Thing> VALUES =
Trang 38Quy tắc 14: Dùng øccessor methods
để truy cập các biến bên trong lớp
* Tat ca cac truong (fields) cua public classes
phai duoc truy cap thong qua ham tuong ung *- Nhu giai thich trong quy tac 13, de no dam
bao tỉnh bao dong
Trang 39Quy tắc 15: immutable vs mutable
Định nghĩa?
* Bat dau qua vi du sau
Trang 40Quy tắc 15: Nhận xét gì về đoạn code
này?
String string = "";
for (int i= 0; i < times; itt) {
string/+ string: concati("stsa)y
}
times
Running time