Java Bài 28: Từ Khóa static

Posted by

Chào mừng các bạn đã đến với bài học Java số 28: Từ khóa static. Đây là bài học trong chuỗi bài về lập trình ngôn ngữ Java của Yellow Code Books.

Có thể nói, cho đến bài học OOP hôm nay, đã có ngày càng nhiều từ khóa được lần lượt giới thiệu đến các bạn. Để mình giúp các bạn ôn lại một chút các từ khóa và tên gọi quan trọng mà chúng ta đã xem qua.

– Từ khóa extends – Bạn bắt đầu làm quen với từ khóa này khi thể hiện sự kế thừa.
– Từ khóa this – Giúp tham chiếu đến các giá trị bên trong lớp.
– Từ khóa super – Giúp tham chiếu đến các giá trị của lớp cha gần nhất.
– Annotation @Overriding – Thể hiện phương thức đang định nghĩa là phương thức phủ quyết, hay ghi đè, hay overriding.
– Từ khóa Object – Dùng để chỉ lớp Object, lớp cha nhất của tất cả các lớp trong Java.
– Các từ khóa private/protected/public – Dùng khi định nghĩa khả năng truy cập vào các giá trị lớp.
– Từ khóa final – Giúp định nghĩa hằng số, hoặc ngăn chặn sự overriding bên trong OOP.
– Tên gọi getter và setter – Nhằm ám chỉ đến sự gói ghém dữ liệu thông qua các phương thức get/set cho thuộc tính private.

Vậy thì hôm nay, chúng ta lại tiếp tục tìm hiểu thêm một từ khóa mới, từ khóa static, xem từ khóa này là gì, và nó có công dụng gì nữa nhé.

Khái Niệm static

Có thể nói, một trong những nghĩa tiếng Việt trong ngôn ngữ lập trình gây khó hiểu nhất là đây. Static – Dịch ra là Tĩnh. Không hiểu gì cả.

Thực ra thì từ khóa static sẽ được áp dụng khi bạn khai báo các thành phần của lớp như bên dưới mình sẽ trình bày rõ. Nó mang tác dụng chính đối với sự quản lý về mặt bộ nhớ. Cụ thể là, các thành viên static sẽ thuộc về quản lý bộ nhớ của lớp, chứ không thuộc về quản lý của thể hiện lớp (hay có thể hiểu là đối tượng).

Vẫn chưa hiểu.

Mình mời bạn nhìn vào ví dụ sau, có thể qua ví dụ bạn vẫn chưa nắm rõ công dụng mà từ khóa static mang lại, nhưng hãy xem nó tác động như thế nào đến việc quản lý các giá trị bên trong lớp mà mình có nói trên đây. Bạn hãy chú ý các thành phần statickhông static trong lớp ToaDo.

public class ToaDo {

	public static String thongTin;
	public int x;
	public int y;
	
}

Vậy như mình có nói. Thuộc tính thongTin là static, và nó sẽ thuộc quyền quản lý của lớp ToaDo. Các thuộc tính x, y là không static, sẽ thuộc quyền quản lý của các thể hiện, hay các đối tượng được khai báo từ ToaDo.

Vấn đề sẽ rõ ràng hơn khi bạn dùng đến các giá trị static này của ToaDo ở một lớp khác.

public class MainClass {

	public static void main(String[] args) {
		// Các thuộc tính x, y này chỉ được truy xuất đến thông qua thể hiện toaDo1 của lớp ToaDo
		ToaDo toaDo1 = new ToaDo();
		toaDo1.x = 10;
		toaDo1.y = 20;
		
		// Các thuộc tính x, y này chỉ được truy xuất đến thông qua thể hiện toaDo2 của lớp ToaDo
		ToaDo toaDo2 = new ToaDo();
		toaDo2.x = 5;
		toaDo2.y = 6;
		
		// Thuộc tính thongTin lại được truy xuất đến thông qua lớp ToaDo
		ToaDo.thongTin = "Lưu tọa độ các hình học";
	}
}

Bạn có thấy rằng, các thuộc tính x, y, cũng như bao giá trị không static khác mà bạn từng biết, đều phải được gọi thông qua một thể hiện của lớp, như toaDo1, toaDo2 ở ví dụ. Còn thuộc tính thongTin lại được truy cập trực tiếp thông qua lớp ToaDo, mà không cần bất cứ một thể hiện nào cả.

Đến đây thì bạn đã hiểu được giá trị static là gì đúng không nào. Chắc bạn cũng “hườm hườm” ngẫm được tại sao nó lại có nghĩa là Tĩnh.

Tiếp theo chúng ta cùng tìm hiểu, từ khóa static sinh ra để làm gì.

Công Dụng Của Từ Khóa static

Như một số thông tin mà mình đã trình bày trên đây, bạn có thể thấy rằng, với việc gọi đến giá trị của lớp thông qua tên lớp, mà không cần phải khởi tạo đối tượng của lớp đó, giúp cho các giá trị được khai báo static sẽ dễ dàng truy xuất hơn.

Đặc biệt hơn cả là, các giá trị được khai báo static này có thể được dùng chung bởi các lớp khác trong ứng dụng. Nó mang ý nghĩa toàn cục trong cả ứng dụng. Bạn cứ thử nhìn vào ví dụ trên, bạn đã thử gán giá trị static thongTin trong ToaDo bằng dòng lệnh ToaDo.thongTin = “Lưu tọa độ các hình học”;, thì ở một chỗ khác, trong lớp khác chẳng hạn, bạn có thể lấy giá trị ToaDo.thongTin này ra dùng, hoặc gán lại cho nó một giá trị mới. Như vậy giá trị static sẽ được chia sẻ, được dùng chung, bởi bất kỳ chỗ nào, trong lớp đó hoặc bên ngoài lớp (miễn là giá trị đó đừng khai báo là private).

Nhưng việc các giá trị của lớp được truy cập và sửa đổi thoải mái như thế này lại là con dao hai lưỡi. Nếu bạn lạm dụng từ khóa static, bạn sẽ vô tình làm phá vỡ nguyên tắc của OOP. Vốn dĩ OOP ra đời là để phân chia đặc điểm và trách nhiệm cho từng đối tượng, để dễ quản lý. Thì từ khóa static lại mang các tính chất vốn dĩ thuộc trách nhiệm của đối tượng nào đó, ra dùng chung một cách đại trà. Do đó, mình khuyên là bạn nên thật cân nhắc khi khai báo static cho bất cứ giá trị nào của lớp.

Vậy từ khóa static thực chất được dùng ở đâu? Mình mời các bạn đọc qua phần dưới đây. Ở từng công dụng cụ thể của từ khóa static, mình sẽ tìm những ví dụ thực tế nhất mà từ khóa static mang lại trong quá trình xây dựng ứng dụng của mình.

Khai Báo Thuộc Tính static

Có lẽ không cần phải nói nhiều nữa. Thuộc tính static là thuộc tính có khai báo từ khóa static. Chúng được sử dụng như thế nào thì chúng ta cùng xem qua các ví dụ dưới đây, bạn sẽ hiểu rõ thôi.

Ví Dụ Đếm Số Lượng Hình Học Được Khai Báo Trong Một Project

Chúng ta cùng quay lại ví dụ về các thể loại hình học. Ví dụ như chúng ta có HinhHoc là lớp cha của hai lớp HinhTronHinhChuNhat. Và yêu cầu của ví dụ hôm nay là, mỗi khi chúng ta tạo ra một đối tượng hình học, thì có một biến đếm tự động tăng lên cho biết số lượng hình được tạo ra trong một chương trình.

Nào cùng xem qua lớp HinhHoc, giả sử chúng ta không quan tâm đến các dòng code tính toán khác, chỉ chú tâm vào biếm dem được set static bên trong lớp này.

public class HinhHoc {
	
	public static int dem = 0;
	
	public HinhHoc() {
		dem++;
	}
    
	// Các dòng code khác
	// ...
}

Sau đó, cứ mỗi constructor của lớp con, chúng ta đều gọi đến lớp cha gần nhất của nó thông qua phương thức super(). Việc gọi này sẽ làm tăng biến dem này lên ở constructor của lớp cha..

public class HinhTron extends HinhHoc {
	
	// Constructor
    public HinhTron() {
        super();
    }
    
    // Các code tính toán chu vi, diện tích
    // ...
 
}
public class HinhChuNhat extends HinhHoc {
 
    // Constructor
    public HinhChuNhat() {
        super();
    }
 
    // Các code tính toán chu vi, diện tích
    // ...
 
}

Và rồi ở hàm main(), bạn hãy thử khai báo các đối tượng của HinhTronHinhChuNhat thoải mái, rồi hãy in biến dem ra console xem sao nhé.

public class MainClass {
 
    public static void main(String[] args) {
        // Tạo các thể hiện của các lớp
    	HinhHoc hinhHoc = new HinhHoc();
        HinhTron hinhTron1 = new HinhTron();
        HinhTron hinhTron2 = new HinhTron();
        HinhChuNhat hinhChuNhat = new HinhChuNhat();
        
        System.out.println("Có tất cả " + HinhHoc.dem + " hình trong ứng dụng.");
    }
 
}

Kết quả in ra có 4 hình trong ứng dụng. Bạn thấy biến dem được khai báo static sẽ được dùng chung như thế nào rồi đúng không nào.

Ví Dụ Khai Báo Các Giá Trị Cấu Hình Tĩnh Cho Ứng Dụng

Có khi nào bạn từng thắc mắc rằng, sẽ làm sao nếu như mong muốn ứng dụng có các giá trị cố định dùng chung, nó mang ý nghĩa là các giá trị cấu hình cho ứng dụng. Chẳng hạn như khai báo đường dẫn tĩnh để lưu file này, hay khai báo đường dẫn đến một trang web này, hay đường dẫn API này,…. Thì tất cả các nhu cầu về cấu hình tĩnh đó, bạn có thể gom chung chúng vào một file và khai báo các thuộc tính static kết hợp với final cho nó.

Tiếp tục với ví dụ về việc đếm các hình học trên đây. Giả sử yêu cầu của bài toán lúc này đặt ra là bạn không được khai báo quá 5 hình. Nếu ứng dụng có quá 5 hình, thay vì câu lệnh in ra số lượng hình học ở trên đây, bạn có thể in ra câu thông báo nào đó. Trong trường hợp này, số lượng tối đa 5 hình trong ràng buộc được xem như là một cấu hình tĩnh của ứng dụng.

Đầu tiên, chúng ta sẽ cần phải tạo một lớp chỉ chuyên cho việc cấu hình thôi. Mình đặt tên lớp này là Configs.

public class Configs {

	// Cấu hình số lượng hình học
	public static final int SO_LUONG_HINH_TOI_THIEU = 0;
	public static final int SO_LUONG_HINH_TOI_DA = 5;
	
	// Các cấu hình khác nếu có
	// public static final xxx xxx
	// public static final xxx xxx
	// ...
}

Quay lại hàm main(), bạn hãy thử khai báo nhiều nhiều các đối tượng hình học xem nào. Sau đây là các kiểm tra ràng buộc dựa trên các giá trị cấu hình tĩnh lấy từ lớp Configs.

public class MainClass {
 
    public static void main(String[] args) {
        // Tạo các thể hiện của các lớp
    	HinhHoc hinhHoc1 = new HinhHoc();
    	HinhHoc hinhHoc2 = new HinhHoc();
        HinhTron hinhTron1 = new HinhTron();
        HinhTron hinhTron2 = new HinhTron();
        HinhChuNhat hinhChuNhat1 = new HinhChuNhat();
        HinhChuNhat hinhChuNhat2 = new HinhChuNhat();
        
        if (HinhHoc.dem > Configs.SO_LUONG_HINH_TOI_DA) {
        	System.out.println("Bạn đã khai báo vượt số lượng hình học cho phép!");
        	System.out.println("Số lượng hình học tối thiểu là: " + Configs.SO_LUONG_HINH_TOI_THIEU);
        	System.out.println("Số lượng hình học tối đa là: " + Configs.SO_LUONG_HINH_TOI_DA);
        } else {
        	System.out.println("Có tất cả " + HinhHoc.dem + " hình trong ứng dụng.");
        }
    }
 
}

Giờ thì bạn có thể chạy thử ứng dụng lên kiểm chứng nhé.

Khai Báo Phương Thức static

Phương thức static được sử dụng với đặc điểm và mục đích tương tự như thuộc tính static trên kia. Đó là.

– Các phương thức static vẫn thuộc quản lý của lớp chứ không phải của thể hiện lớp.
– Một phương thức được set là static khi chúng được dùng chung bởi tất cả các đối tượng bên trong ứng dụng. Như các phương thức về kiểm tra tính đúng đắn của giá trị nào đó, phương thức về chuyển đổi tiền tệ, chuyển đổi đơn vị, phương thức về lưu trữ giữ liệu xuống bộ nhớ, phương thức kết nối với server,…

Tuy nhiên có một lưu ý hết sức đặc biệt trong Java là, tất cả các phương thức static chỉ có thể gọi đến các thuộc tính được khai báo là static mà thôi. Điều này được dễ dàng kiểm chứng khi rất lâu rồi, ở bài 8, mình buộc phải khai báo biến static name khi biến này để ở ngoài hàm main(), và vì hàm main() được khai báo là static (hàm main() bắt buộc phải khai báo là hàm static nhé, để hệ thống có thể truy xuất bất cứ lúc nào thông qua lớp, chứ không cần khai báo thể hiện của lớp có chứa hàm main()), nên nếu main() muốn dùng đến name, name cũng phải static.

Ví Dụ Về Chuyển Đổi Đơn Vị Trong Hệ Thống Hình Học

Với ví dụ này, giả sử ứng dụng hình học của chúng ta đòi hỏi khắt khe hơn về mặt nhập liệu và tính toán đơn vị. Và giả sử chúng ta chấp nhận hai đơn vị nhập vào là inchcentimet.

Trước mắt, lớp Configs sẽ phải xây dựng thêm một số giá trị cấu hình tĩnh, và phải thêm các phương thức tĩnh chuyển đổi đơn vị nữa.

public class Configs {

	// Cấu hình số lượng hình học
	public static final int SO_LUONG_HINH_TOI_THIEU = 0;
	public static final int SO_LUONG_HINH_TOI_DA = 5;
	
	// Các cấu hình khác
	public static final float PI = 3.14f;
	public static final float INCH_CM = 2.54f; // 1 inch = 2.54 cm
	public static final int DON_VI_CM = 1; // Đánh dấu ứng dụng đang dùng đơn vị centimet
	public static final int DON_VI_INC = 2; // Đánh dấu ứng dụng đang dùng đơn vị inch
	public static int donVi = DON_VI_CM; // Cờ Cho biết đang dùng đơn vị gì
	
	// Phương thức static giúp chuyển đổi centimet sang inch
	public static float ChuyenCentimetSangInch(float cm) {
		float inch = cm / INCH_CM;
		return inch;
	}
	
	// Phương thức static giúp chuyển đổi inch sang centimet
	public static float ChuyenInchSangCentimet(float inch) {
		float cm = inch * INCH_CM; 
		return cm;
	}
}

Sau đó, ở lớp hình học nào cũng vậy, ví dụ dưới đây mình xây dựng cho lớp HinhTron, lớp này sẽ hỏi người dùng đang dùng đơn vị hình học nào, và sẽ xuất thông tin tương ứng kèm với thông tin chuyển đổi sang đơn vị còn lại dựa vào các cấu hình tĩnh và các phương thức tĩnh trong lớp Configs.

public class HinhTron extends HinhHoc {
	
	protected float banKinh;
	private Scanner scanner;
	
	// Constructor
    public HinhTron() {
        super();
        scanner = new Scanner(System.in);
    }
    
    public void nhapBanKinh() {
    	// Nhập đơn vị tính là centimet hay inch
    	System.out.println("Bạn dùng đơn vị tính nào: ");
    	System.out.println("\t1 - Centimet");
    	System.out.println("\t2 - inch");
    	Configs.donVi = scanner.nextInt();
    	
    	// Sau đó nhập bán kính
    	System.out.println("Hãy nhập vào Bán kính Hình tròn: ");
        banKinh = scanner.nextFloat();
    }
    
    public void inThongTin() {
    	if (Configs.donVi == Configs.DON_VI_CM) {
    		System.out.println("Hình tròn có bán kính " + banKinh + " cm");
    		System.out.println("Tương đương " + Configs.ChuyenCentimetSangInch(banKinh) + " inch");
    	} else {
    		System.out.println("Hình tròn có bán kính " + banKinh + " inch");
    		System.out.println("Tương đương " + Configs.ChuyenInchSangCentimet(banKinh) + " cm");
    	}
    }
 
}

Bạn cứ thử xây dựng lời gọi đến các phương thức này của HinhHoc để kiểm chứng nhé.

Vậy là chúng ta vừa đi qua khái niệm và cách sử dụng từ khóa static trong Java. Bạn có thể thấy rằng các giá trị static thực sự rất dễ dùng đúng không nào. Và lời khuyên của mình vẫn luôn là, bạn hãy cân nhắc, đừng lạm dụng các giá trị static này quá. Nếu không vì mục đích dùng chung như các ví dụ trên đây của mình, thì bạn đừng nên sử dụng đến từ khóa static nhé.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy đánh giá 5 sao nếu thấy thích bài viết, hãy comment bên dưới nếu có thắc mắc, hãy để lại địa chỉ email của bạn để nhận được thông báo mới nhất khi có bài viết mới, và nhớ chia sẻ các bài viết của Yellow Code Books đến nhiều người khác nữa nhé.

Bài Kế Tiếp

Chúng ta sẽ nói qua một chút về Nạp chồng phương thức trong OOP, hay còn gọi với cái tên tiếng Anh là Method Overloading.

Advertisements
Rating: 5.0/5. From 13 votes.
Please wait...

4 comments

    1. Ô kê bạn, mình sẽ cố viết nhanh tí. Hi vọng nếu đợi có lâu tí thì mọi người đừng nản nhé 😀

  1. public class HinhTron extends HinhHoc {

    protected float banKinh;
    private Scanner scanner;

    // Constructor
    public HinhTron() {
    super();
    scanner = new Scanner(System.in);
    Ad ơi,cho em hỏi cái phần Scanner ấy; em không rõ lắm anh có thể giải thích giúp em được không
    Em cảm ơn

Gửi phản hồi