Giải pháp chuyển đổi số trọn gói cho doanh nghiệp Việt

Khả Năng Truy Cập (Access Modifier) - Lập trình Java

Lập trình Java

← Toàn bộ tài liệu


Bài 25: Khả Năng Truy Cập (Access Modifier)

Bài 25/56 Xem dạng sách

Bài hôm nay chúng ta sẽ nói đến một vấn đề đã được hứa khá lâu, từ khi các bạn mới vừa làm quen với các thuộc tính và phương thức của lớp, đó là kiến thức về khả năng truy cập vào các thành phần của lớp. Vậy thì cái khả năng truy cập này là gì và chúng quan trọng như thế nào, mời các bạn cùng theo dõi bài học nhé.

Khả Năng Truy Cập (Access Modifier) Là Gì?

Khả năng truy cập, hay còn gọi là access modifier, là các từ khóa được Java cung cấp sẵn. Và bạn, hay các lập trình viên khác sẽ sử dụng các định nghĩa của các từ khóa này, để thể hiện các quyền được truy cập vào các thành phần của lớp. Các thành phần của lớp ở đây mình muốn nhắc đến bao gồm các thuộc tính, các phương thức, và cả các constructor của một lớp.

Các từ khóa liên quan đến access modifier trong Java chính là: private, protected, public và default.

Định Nghĩa Khả Năng Truy Cập Như Thế Nào?

Chúng ta khoan hãy nói đến từng khả năng truy cập đã nêu trên, mà hãy điểm lại xem làm cách nào để khai báo một khả năng truy cập nhé.

Bạn hãy nhớ lại đi, trong các cú pháp liên quan đến việc khai báo các thành phần của lớp, mình đều có nêu cách khai báo khả năng truy cập này rồi đấy.

Chẳng hạn như khi khai báo thuộc tính của lớp này.

[khả_năng_truy_cập] kiểu_thuộc_tính tên_thuộc_tính [= giá_trị_ban_đầu];

Hay khi khai báo phương thức của lớp này.

[kả_năng_truy_cập] kiểu_trả_về tên_phương_thức  () {
     // Các dòng code
}

Hay khi khai báo một constructor này.

[khả_năng_truy_cập]  tên_phương_thức  () {
     // Các dòng code
}

Vậy bạn có thể hiểu cách để định nghĩa một khả năng truy cập vào các thành phần lớp rồi đúng không nào. Dưới đây là một ví dụ đầy đủ cho việc khai báo các khả năng truy cập này ở lớp HinhTron.

public class HinhTron {
protected final float PI = 3.14f;
private float banKinh;
 
// Constructor
public HinhTron(float banKinh) {
this.banKinh = banKinh;
}
 
protected float tinhChuVi() {
return 2 * PI * banKinh;
}
 
protected float tinhDienTich() {
return PI * banKinh * banKinh;
}
 
public void xuatThongTin() {
System.out.println("Đây là Hình tròn");
}
}

Ý Nghĩa Của Các Khả Năng Truy Cập

Nào, giờ chúng ta sẽ đi qua từng khả năng truy cập xem chúng có ý nghĩa và lợi ích gì nhé.

Trước hết, mình xin tổng hợp các khả năng truy cập dựa trên một bảng dưới đây, để cho các bạn dễ nhớ. Bảng này chỉ đơn giản mang các giá trị “Có” nếu như định nghĩa này cho phép truy cập ở một phạm vi nào đó, và mang giá trị “Không” cho ý nghĩa ngược lại.

Phạm viprivatedefaultprotectedpublic
Bên trong lớp Có Có Có Có
Ở lớp khác, nhưng trong cùng package Không Có Có Có
Ở lớp con, trong cùng package Không Có Có Có
Ở lớp con, khác package Không Không Có Có
Ở bất cứ lớp nào khác (không phải lớp con), khác package Không Không Không Có
Bảng ý nghĩa các khả năng truy cập

Giờ mình sẽ nói rõ hơn từng khả năng truy cập.

Khả Năng Truy Cập private

Nếu bạn chỉ định một thành phần nào đó (thuộc tính, phương thức hay constructor) của lớp là private, thì như bảng trên, bạn chỉ có thể truy xuất đến thành phần này bên trong lớp đó mà thôi.

Khả năng private này mang ý nghĩa bảo vệ các thành phần bên trong lớp, không cho các lớp bên ngoài có thể “nhìn thấy” (đọc và chỉnh sửa) được.

  • Nếu private được chỉ định cho một thuộc tính, thuộc tính đó sẽ không được phép truy cập, hay chỉnh sửa từ các lớp khác, trừ khi bạn xây dựng các phương thức getter và setter cho thuộc tính đó mà mình sẽ nói sau.
  • Còn nếu private được chỉ định cho một phương thức, phương thức đó sẽ không được truy cập hay kế thừa từ lớp khác.
  • Và nếu private được chỉ định cho constructor, constructor này sẽ không dùng được để khởi tạo đối tượng cho lớp đó.

Bạn nên tập một thói quen, đó là luôn khai báo private cho các thuộc tính bên trong lớp. Với phương thức thì nếu bạn không chắc nó có được dùng ở đâu đó bên ngoài lớp này hay không, thì cứ chỉ định private luôn cho chắc cú.

Bài Thực Hành Số 1

Bài thực hành này mình tạo lớp HinhTron có khai báo các thành phần là private. Bạn có thể thấy các thành phần này khi dùng bên trong lớp HinhTron thì rất tốt, không bị báo lỗi từ hệ thống.

public class HinhTron {
private final float PI = 3.14f;
private float banKinh;
 
// Constructor
private HinhTron() {
}
 
private float tinhChuVi() {
return 2 * PI * banKinh;
}
 
private float tinhDienTich() {
return PI * banKinh * banKinh;
}
}

Vậy bạn có thể tự trắc nghiệm xem, với việc gọi đến các thành phần này của HinhTron từ main(), thì dòng code nào sau đây sẽ bị hệ thống báo lỗi nhé.

public class MainClass {
public static void main(String[] args) {
HinhTron hinhTron = new HinhTron();
hinhTron.banKinh = 20;
hinhTron.tinhDienTich();
}
}

Kết quả dòng bị báo lỗi ở dưới đây.

Dòng 3 báo lỗi
Dòng 4 báo lỗi
Dòng 5 báo lỗi

Khả Năng Truy Cập default

Thực ra không có một kiểu định nghĩa khả năng default nào cả. default ở đây có nghĩa là bạn không định nghĩa khả năng truy cập gì hết, nó giống như các bài học mở đầu mà chúng ta đã làm quen, khi đó chúng ta tạm thời để trống các khai báo khả năng truy cập này.

Và với việc để trống khả năng truy cập cho các thành phần của lớp, thì bạn có thể thấy rằng, nếu bên trong lớp đó hoặc các lớp khác trong cùng package, sẽ có thể nhìn thấy và truy xuất đến các thành phần đó. Khả năng nhìn thấy và truy xuất chỉ bị chặn khi bạn gọi đến các thành phần này từ một lớp nằm ngoài package mà thôi.

Khả năng này nên ít được dùng hơn, vì bạn nên tập thói quen chỉ định khả năng truy cập một cách tường minh từ bây giờ.

Bài Thực Hành Số 2

Ở đây mình bỏ hết các khai báo khả năng truy cập cho các thành phần của lớp HinhTron.

package shapes;
 
public class HinhTron {
final float PI = 3.14f;
float banKinh;
 
// Constructor
HinhTron() {
}
 
float tinhChuVi() {
return 2 * PI * banKinh;
}
 
float tinhDienTich() {
return PI * banKinh * banKinh;
}
}

Và tương tự, với các dòng code khai báo ở main() như sau, bạn đoán xem dòng nào sẽ bị lỗi nhé.

package main;
 
import shapes.HinhTron;
 
public class MainClass {
public static void main(String[] args) {
HinhTron hinhTron = new HinhTron();
hinhTron.banKinh = 20;
hinhTron.tinhDienTich();
}
}

Kết quả dòng bị lỗi như sau.

Dòng 7 báo lỗi
Dòng 8 báo lỗi
Dòng 9 báo lỗi

Sở dĩ các dòng này báo lỗi, là vì chúng ở package main khác với package shapes của HinhTron. Bạn thử đưa chúng về cùng một package rồi kiểm chứng lại nhé.

Khả Năng Truy Cập protected

Khả năng này mang ý nghĩa bảo vệ các thành phần của lớp cho các lớp con của nó dùng lại hoặc ghi đè. Do đó có thể hiểu, khả năng protected sẽ trao quyền sử dụng hoàn toàn tự do cho các lớp con, dù có ở cùng hay khác package. Khả năng này chỉ giới hạn với các lớp không phải lớp con của nó và nằm ngoài package mà thôi.

Bài Thực Hành Số 3

Lần này bạn hãy xem các khai báo protected cho các thành phần bên trong HinhTron như sau.

package shapes;
 
public class HinhTron {
protected final float PI = 3.14f;
protected float banKinh;
 
// Constructor
public HinhTron(float banKinh) {
this.banKinh = banKinh;
}
 
protected float tinhChuVi() {
return 2 * PI * banKinh;
}
 
protected float tinhDienTich() {
return PI * banKinh * banKinh;
}
}

Và với code ở main(), bạn lại thử đoán xem dòng code nào bị báo lỗi.

package main;
 
import shapes.HinhTron;
 
public class MainClass {
public static void main(String[] args) {
HinhTron hinhTron = new HinhTron(10);
hinhTron.tinhDienTich();
}
}

Kết quả dòng bị lỗi như sau.

Dòng 8 báo lỗi

Phương thức tinhDienTich() rơi vào trường hợp “Không” duy nhất của protected. Bởi nó vừa gọi từ một lớp không có quan hệ kế thừa với HinhTron, và cũng nằm ngoài package so với HinhTron nữa.

Bài Thực Hành Số 4

Vẫn với code của Bài thực hành số 3, lần này mình khai báo thêm HinhTru kế thừa từ HinhTron, và main() như sau. Bạn xem có dòng nào báo lỗi không nhé.

package shapes;
 
public class HinhTru extends HinhTron {
public float chieuCao;
 
// Constructor
public HinhTru(float banKinh, float chieuCao) {
super(banKinh);
this.chieuCao = chieuCao;
}
 
public float tinhTheTich() {
return tinhDienTich() * chieuCao;
}
}
package main;
 
import shapes.HinhTru;
 
public class MainClass {
public static void main(String[] args) {
HinhTru hinhTru = new HinhTru(10, 20);
hinhTru.tinhTheTich();
}
}

Đây là kết quả các dòng báo lỗi.

Không dòng nào báo lỗi hết.

Vì HinhTru kế thừa các phương thức từ cha và set lại khả năng truy cập là public.

Khả Năng Truy Cập public

Có lẽ không cần tốn nhiều giấy mực để nói về khả năng này. Nếu bạn chỉ định một thành phần trong lớp là public, thì coi như tất cả các lớp nào khác đều có thể truy xuất đến các thành phần này.

Sơ đồ quan hệ các bảng

Table Name: {{table.name}}
Num Name Type
{{field_index+1}}
{{field.name}} {{field.type}}

Hỏi đáp

Gửi với tên {{comment_full_name}}

Chưa có câu hỏi nào cho bài này.

Zalo 0936.006.058 Hotline 0936.006.058