1. Trang chủ
  2. » Thể loại khác

Lập trình JAVA hiệu quả

58 165 0
Tài liệu được quét OCR, nội dung có thể không chính xác

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 58
Dung lượng 11,16 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Ví dụ: “Cách bình thường: Map m=new HashMap; *Với hàm khởi tạo: Map 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

Trang 1

Tac gia: Joshua Bloch Trình bày: Lê Quốc Anh

30-05-2015

Trang 2

VN-Có nhận xét gì về đoạn code này ?

List<Integer> lst = new ArrayList<Integer>() ;

Iterator<Integer> iter = lst.iterator();

Trang 3

Có 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 ();

iter.remove () ; iter remove () ;

Trang 4

So sánh thời gian thực hiện remove

size=1000: time =166 ms size=1000: time =57 ms

size=10000: time =8167 ms size=10000: time =60 ms

Trang 6

LinkedList<E> ArrayList<E>

Get (idx) Add(e)

‘Add(idx, e)

Remove(idx)

Trang 8

Effective Java

Second Edition

Trang 9

PHAN 1: TAO VA HUY ĐỐI

TUONG

Trang 10

Quy 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 abstract class Books() { public static Book createBook(){ return new MyBook(); }

private class MyBook implements Book{ public void cover(){} } }

10

Trang 11

Quy 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

factory methods và các static factory khác.

Trang 12

Quy 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 13

Quy 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; }

public NutritionFacts build() {return new NutritionFacts(this); }

Trang 14

Quy 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() { }

* - Câu hỏi: Khi lớp Singleton thực thi Serializable?

Trang 15

Quy 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

// 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

* Các thuộc tính (eg., version) nên được khai báo với từ khóa transient nếu giá trị của chúng không đổi trước và sau khi serialization hoặc chúng có kiểu khác primitives.

Trang 16

Quy 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() { }

}

* Enum mac dinh đã thu’ thi serializable

Trang 17

Quy 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

constructor

Trang 18

Quy 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

— Sử dụng kiểu biến primitives (eg., long, int,

double) thay vì boxed primitives (eg., Long,

Integer, Double)

Trang 19

Quy 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();

Object result = elements[ size];

return result;

Trang 20

Quy 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

dựng caches, listeners hoặc callbacks

Trang 21

Quy 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

kiểm chứng lại)

Trang 22

Quy 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

không kiểm soát được

Trang 23

Quy 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

CHUNG CHO CÁC ĐÔI TƯƠNG

Trang 25

Quy tắc 8: Điều kiện khi viết lại hàm

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

Cac lop private hay package-private thi tuyet doi ko can viet lai ham equals vi chung ko the duoc truy cap toi :)

Trang 26

Quy 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

5 Loi goi x.equals(null) luon tra lai false.

Trang 27

Quy 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

— 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

— Sau khi viet xong ham equals, kiem tra lai xem no co tuan thu 5 quy dinh khong

— Luon viet lai ham hashCode mot khi da viet lai ham equals (quy tac 9)

Trang 28

Quy 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

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

4 Nên biết rằng khi hàm hashCode khác nhau trên mỗi đối

tượng khác nhau thì mới tăng hiệu suất của hệ thống.

Trang 29

Quy 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;

PhoneNumber pn = (PhoneNumber)o;

}

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,

5309)) sé tra lai gia tri "Jenny", nhuing no tra lai null => Ly do là vì hàm hashCode cho 2 đối tượng không bằng nhau

Trang 30

Quy 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?

Trang 31

Quy 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 => Dieu nay kho, phai lam vai cai luan van TS

Trang 32

Quy 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;

}

return result; }

Trang 33

Quy 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 -

8738242"

Ngày đăng: 12/06/2017, 19:54

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w