Class, Object và Method trong Java
- 27-09-2023
- Toanngo92
- 0 Comments
Mục lục
Giới thiệu
Lớp là một cấu trúc logic xác định hình dạng và bản chất của một đối tượng. Vì nó là đơn vị thực thi chính cho lập trình hướng đối tượng trong Java, nên bất kỳ khái niệm nào trong chương trình Java phải được gói gọn trong lớp. Trong Java, lớp được định nghĩa là một kiểu dữ liệu mới. Kiểu dữ liệu này được sử dụng để tạo các đối tượng thuộc kiểu của nó. Mỗi đối tượng được tạo ra từ lớp chứa bản sao của các thuộc tính được định nghĩa trong lớp. Các thuộc tính cũng được gọi là các trường và biểu thị trạng thái của một đối tượng. Việc khởi tạo các đối tượng được thực hiện bằng cách sử dụng các hàm khởi tạo và hành vi của các đối tượng được xác định bằng các phương thức.
Khai báo lớp (Class)
Một khai báo lớp phải bắt đầu bằng từ khóa class theo sau là tên của lớp đang được khai báo. Bên cạnh đó, sau đây là một số quy ước cần tuân theo khi đặt tên cho một lớp:
- Tên lớp phải là một danh từ và có thể viết hoa chữ thường, với chữ cái đầu tiên của mỗi từ bên trong được viết hoa.
- Tên lớp nên đơn giản, mô tả và có ý nghĩa
- Tên lớp không được là từ khóa Java.
- Tên lớp không được bắt đầu bằng chữ số. Tuy nhiên, chúng có thể bắt đầu bằng ký hiệu đô la (s) hoặc ký tự gạch dưới.
Cú pháp:
class <class_name> {
// class body
}
Phần thân của lớp được bao bọc giữa vùng giữa các dấu ngoặc nhọn. Trong phần thân của lớp, bạn có thể khai báo các thành viên, chẳng hạn như các trường, phương thức và hàm khởi tạo.
// Student.java
// This is the class definition for the Student class
public class Student {
// Class variables or fields
private String name;
private int age;
private String studentId;
// Constructor: used to initialize the object with some initial values
public Student(String name, int age, String studentId) {
this.name = name;
this.age = age;
this.studentId = studentId;
}
// Getter methods to access the private fields
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getStudentId() {
return studentId;
}
// Setter methods to modify the private fields
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
// Other methods specific to the Student class can be defined here
// For example: displayStudentInfo(), calculateGPA(), etc.
}
Ví dụ khai báo một Class Customer
class Customer {
//body of class
}
Trong mã, một class được khai báo hoạt động như một kiểu dữ liệu mới. Tên của kiểu dữ liệu mới là Customer. Khai báo kiểu dữ liệu này chỉ là khuôn mẫu để tạo nhiều đối tượng có tính năng tương tự và không chiếm bộ nhớ.
Khởi tạo đối tượng
Các đối tượng là các thể hiện đại diện thực tế của lớp.
Khai báo và tạo đối tượng
Một trong những cách đơn giản và dễ dàng nhất để tạo đối tượng là tạo bằng toán tử new. Khi gặp toán tử mới, JVM cấp phát bộ nhớ cho đối tượng và trả về một tham chiếu hoặc địa chỉ bộ nhớ của đối tượng được cấp phát. Tham chiếu hoặc địa chỉ bộ nhớ sau đó được lưu trữ trong một biến. Biến này còn được gọi là biến tham chiếu.
Cú pháp:
class_name object_name = new <class_name>();
Trong đó:
new: Là một toán tử phân bổ bộ nhớ cho một đối tượng trong thời gian chạy.
cbject_name: Là biến lưu trữ tham chiếu của đối tượng.
Cú pháp:
Customer objCustomer = new Customer();
Biểu thức ở bên phải, new Customer() phân bổ bộ nhớ khi chạy. Sau khi bộ nhớ được phân bổ cho đối tượng, nó trả về tham chiếu hoặc địa chỉ của đối tượng được phân bổ, được lưu trữ trong biến objCustomer.
Tạo đối tượng: Quy trình hai giai đoạn
Ngoài ra, một đối tượng có thể được tạo bằng hai bước là khai báo biến tham chiếu và cấp phát bộ nhớ động cho đối tượng.
Sử dụng phương pháp này, một tham chiếu đối tượng được khai báo trước mà không cần sử dụng toán tử mới. Cú pháp khai báo tham chiếu đối tượng như sau:
<class_name> <object_name>;
Trong đó:
object_name: là một biến không trỏ tới bất kỳ vị trí vùng nhớ nào.
Theo mặc định, giá trị null được lưu trữ trong biến tham chiếu của đối tượng. Nói cách khác, nó có nghĩa là nó không trỏ đến một đối tượng thực tế. Đối tượng, cbjCustomer hoạt động như một tham chiếu đến một đối tượng thuộc loại Khách hàng. Nếu objCustomer được sử dụng tại thời điểm này mà không được khởi tạo, thì chương trình sẽ dẫn đến lỗi thời gian biên dịch (compile runtime error).
Do đó, trước khi sử dụng một đối tượng như vậy, đối tượng phải được khởi tạo bằng toán tử new. Toán tử mới sẽ tự động cấp phát bộ nhớ cho một đối tượng. Ví dụ: objCustomer = new Customer(); . Câu lệnh này sẽ cấp phát bộ nhớ cho đối tượng và địa chỉ bộ nhớ của đối tượng được cấp phát được lưu trong biến objCustomer.
Các thành viên (member) của một lớp (Class)
Các thành viên của một lớp là các trường và các phương thức. Các trường xác định trạng thái của một đối tượng được tạo từ lớp và được gọi là các biến đối tượng. Các phương thức được sử dụng để thực hiện hành vi của các đối tượng và được gọi là các phương thức thể hiện.
Biến thực thể (thuộc tính đối tượng)
Các trường hoặc biến được định nghĩa trong một lớp được gọi là biến thực thể. Các biến thực thể được sử dụng để lưu trữ dữ liệu trong đó. Chúng được gọi là các biến thực thể bởi vì mỗi thể hiện của lớp, nghĩa là các đối tượng của lớp đó sẽ có bản sao riêng của các biến thể hiện. Điều này có nghĩa là mỗi đối tượng của lớp sẽ chứa các biến thực thể trong quá trình tạo.
Hãy xem xét một kịch bản trong đó lớp Customer đại diện cho chi tiết về khách hàng có tài khoản tại ngân hàng. Trong trường hợp này, một câu hỏi điển hình có thể được đặt ra là Các dữ liệu khác nhau được yêu cầu để xác định một khách hàng trong miền ngân hàng và thể hiện nó dưới dạng một đối tượng là gì?
Như thể hiện trong hình trên, các yêu cầu dữ liệu được xác định đối với khách hàng của ngân hàng bao gồm: ID khách hàng, Tên, Địa chỉ và Tuổi. Để ánh xạ các yêu cầu dữ liệu này trong lớp Customer, các biến thực thể được khai báo. Mỗi phiên bản được tạo từ lớp Customer sẽ có bản sao riêng của các biến thực thể.
Như thể hiện trong hình trên, mỗi thực thể của lớp có các biến thực thể riêng của nó được khởi tạo với dữ liệu duy nhất. Bất kỳ thay đổi nào được thực hiện đối với các biến thực thể của một đối tượng sẽ không ảnh hưởng đến các biến thực thể của một đối tượng khác.
Cú pháp:
[access_modifier] data_type instanceVariableName;
Trong đó:
- access_modifier: Là một từ khóa tùy chọn chỉ định cấp độ truy cập của một biến thực thể. Nó có thể là private, protected, và public.
- data_type: Chỉ định kiểu dữ liệu của biến.
- instanceVariableName: chỉ định tên của biến.
public class Customer {
// Properties
private String firstName;
private String lastName;
private int age;
private String email;
private String phoneNumber;
// Constructors
public Customer() {
// Default constructor
}
}
Phương thức thực thể
Các phương thức là các hàm được khai báo trong một lớp và được sử dụng để thực hiện các thao tác trên các biến thực thể. Một phương thức thể hiện hành vi của một đối tượng. Nó có thể được truy cập bằng cách khởi tạo một đối tượng của lớp mà nó được định nghĩa và sau đó gọi phương thức. Ví dụ: lớp Car có thể có phương thức Brake() đại diện cho hành động ‘Áp dụng phanh’. Để thực hiện hành động, phương thức Brake() sẽ phải được gọi bởi một đối tượng của lớp Car.
Các quy ước sau phải được tuân theo khi đặt tên cho một phương thức:
- không thể là một từ khóa Java.
- không thể chứa khoảng trắng.
- Không thể bắt đầu bằng một chữ số.
- Có thể bắt đầu bằng một chữ cái, dấu gạch dưới hoặc ký hiệu ‘$’.
- Nên là một động từ trong chữ thường.
- Nên được mô tả và có ngữ nghĩa.
- Nếu là tên nhiều từ bắt đầu bằng động từ viết thường, theo sau là tính từ, danh từ, v.v. nên viết hoa chữ cái đầu
Cú pháp:
[access_modifier] <return_type> <method_name>([list of parameters]){
//body of method
}
Trong đó:
- access_modifier: Là một từ khóa tùy chọn chỉ định cấp độ truy cập của một phương thức thể hiện.
- Nó có thể là riêng tư, được bảo vệ và công khai.
- return_type: Chỉ định kiểu dữ liệu của giá trị được phương thức trả về.
- method_name: Là tên phương thức.
Mỗi thực thể của lớp có các biến thực thể riêng của nó, nhưng các phương thức thực thể được chia sẻ bởi tất cả các thực thể của lớp trong quá trình thực thi.
Ví dụ:
public class Customer {
// Properties
private String firstName;
private String lastName;
private int age;
private String email;
private String phoneNumber;
// Constructors
public Customer() {
// Default constructor
}
public Customer(String firstName, String lastName, int age, String email, String phoneNumber) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.email = email;
this.phoneNumber = phoneNumber;
}
// Getters and Setters
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
// Override toString() method for easy printing
@Override
public String toString() {
return "Customer{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", age=" + age +
", email='" + email + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
'}';
}
}
Lưu ý rằng khi lớp Customer này được biên dịch, trình biên dịch sẽ đặt nó trong file Customer.class. Lớp này không thể được thực thi vì không có phương thức main().
Gọi phương thức
Bạn có thể truy cập một phương thức của một lớp bằng cách tạo một đối tượng của lớp. Để gọi một phương thức, tên đối tượng được theo sau bởi toán tử dấu chấm (.) và tên phương thức.
Trong Java, một phương thức luôn được gọi từ một phương thức khác. Phương thức gọi một phương thức được gọi là phương thức gọi. Phương thức được gọi được gọi là phương thức được gọi. Sau khi thực hiện tất cả các câu lệnh trong khối mã của phương thức được gọi, điều khiển sẽ quay trở lại phương thức gọi. Hầu hết các phương thức được gọi từ phương thức main() của lớp, là điểm bắt đầu thực hiện chương trình.
Ví dụ:
public class Customer {
// Properties
private String firstName;
private String lastName;
private int age;
private String email;
private String phoneNumber;
// Constructors
public Customer() {
// Default constructor
}
public Customer(String firstName, String lastName, int age, String email, String phoneNumber) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.email = email;
this.phoneNumber = phoneNumber;
}
// Getters and Setters
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
// Override toString() method for easy printing
@Override
public String toString() {
return "Customer{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", age=" + age +
", email='" + email + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
'}';
}
// Main method to demonstrate the class
public static void main(String[] args) {
// Create a new Customer object
Customer customer = new Customer("John", "Doe", 30, "john.doe@example.com", "123-456-7890");
// Print customer information to the terminal
System.out.println("Customer Information:");
System.out.println("First Name: " + customer.getFirstName());
System.out.println("Last Name: " + customer.getLastName());
System.out.println("Age: " + customer.getAge());
System.out.println("Email: " + customer.getEmail());
System.out.println("Phone Number: " + customer.getPhoneNumber());
}
}
Hàm khởi tạo (Constructor)
Một lớp có thể chứa nhiều biến mà việc khai báo và khởi tạo trở nên khó theo dõi, chúng được thực hiện trong các khối khác nhau. Tương tự như vậy, có thể có các thao tác khởi động khác được yêu cầu thực hiện trong một ứng dụng như mở tệp, v.v. Ngôn ngữ lập trình Java cho phép các đối tượng khởi tạo các nghiệp vụ ngay khi tạo các đối tượng đó. Hành vi này đạt được bằng cách định nghĩa các hàm khởi tạo trong lớp.
Hàm khởi tạo là một phương thức có cùng tên với tên của lớp. Constructor khởi tạo các biến của một lớp hoặc thực hiện các thao tác khởi động chỉ một lần khi đối tượng của lớp được khởi tạo. Chúng được thực thi tự động bất cứ khi nào một thể hiện của một lớp được tạo, trước khi toán tử mới hoàn thành.
Ngoài ra, các phương thức khởi tạo không có kiểu trả về, nhưng chấp nhận các tham số.
Cú pháp:
<classname>() {
// initialization code
}
Ví dụ:
public class Car {
// Properties
private String make;
private String model;
private int year;
// Constructor
public Car() {
this.make = "abc";
this.model = model;
this.year = year;
}
// Getters and Setters (omitted for brevity)
// ...
// Other methods (omitted for brevity)
// ...
}
Gọi hàm khởi tạo
Các hàm khởi tạo được gọi ngay lập tức trong quá trình tạo đối tượng. Điều này có nghĩa là khi gặp toán tử mới, bộ nhớ sẽ được phân bổ cho đối tượng. Phương thức khởi tạo, nếu được cung cấp trong lớp là
được gọi bởi JVM để khởi tạo đối tượng.
Cú pháp:
<class_name> <object_name> = new <class_name>();
Hàm khởi tạo mặc định (Default Constructor)
Hãy xem xét một tình huống, trong đó phương thức khởi tạo không được định nghĩa cho một lớp. Trong trường hợp như vậy, một hàm khởi tạo ẩn được gọi bởi JVM để khởi tạo các đối tượng. Hàm khởi tạo ngầm định này còn được gọi là hàm khởi tạo mặc định và được tạo cho các lớp mà các hàm khởi tạo rõ ràng không được định nghĩa.
Nói cách khác, một hàm khởi tạo không đối số mặc định được trình biên dịch cung cấp cho bất kỳ lớp nào không có hàm khởi tạo rõ ràng. Hàm khởi tạo mặc định khởi tạo các biến thực thể của đối tượng mới được tạo thành các giá trị mặc định của chúng.
Danh sách giá trị mặc định được gán cho biến thực thể của lớp dựa vào kiểu dữ liệu của chúng:
Data Type | Default Value |
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0f |
double | 0.0 |
char | ‘\u0000’ |
boolean | False |
String (any object) | Null |
Hàm khởi tạo có tham số (Parameterized Constructor)
Hàm khởi tạo được có tham số chứa danh sách các tham số khởi tạo các biến đối tượng của một đối tượng. Giá trị cho các tham số được truyền trong quá trình tạo đối tượng. Điều này có nghĩa là mỗi đối tượng sẽ được khởi tạo với các bộ giá trị khác nhau.
Ví dụ:
public class MyClass {
// Instance variables
private String name;
private int age;
// Parameterized constructor
public MyClass(String name, int age) {
this.name = name;
this.age = age;
}
// Other methods and code can go here
}
MyClass obj1 = new MyClass("John", 30);
MyClass obj2 = new MyClass("Alice", 25);
Quản lý bộ nhớ trong Java
Bộ nhớ bao gồm hai thành phần là stack và heap. Ngăn xếp là một vùng trong bộ nhớ Lưu trữ các tham chiếu đối tượng và thông tin phương thức bao gồm: các tham số của một phương thức và các biến cục bộ của nó. Vùng heap của bộ nhớ xử lý cấp phát bộ nhớ động. Điều này có nghĩa là, các đối tượng Java được phân bổ không gian bộ nhớ vật lý trên heap khi chạy. Việc cấp phát bộ nhớ được thực hiện bất cứ khi nào JVM thực thi toán tử mới.
Bộ nhớ heap phát triển khi và khi phân bổ vật lý được thực hiện cho các đối tượng. Do đó, JVM cung cấp một thói quen thu gom rác giúp giải phóng bộ nhớ bằng cách hủy các đối tượng không còn cần thiết trong chương trình Java. Bằng cách này, bộ nhớ trong heap được sử dụng lại để cấp phát đối tượng.
Gán tham chiếu đối tượng (Assigning Object References)
Khi làm việc với các kiểu dữ liệu nguyên thủy, giá trị của một biến có thể được gán cho một biến khác bằng cách sử dụng toán tử gán.
Ví dụ:
int a = 5;
int b = a;
Câu lệnh b = a; sao chép giá trị từ biến a và lưu nó vào biến b
Vì các giá trị giữa các kiểu dữ liệu nguyên thủy có thể được sao chép, tương tự như vậy, giá trị được lưu trữ trong một biến tham chiếu đối tượng có thể được sao chép vào một biến tham chiếu khác. Vì Java là ngôn ngữ chặt chẽ nên cả hai biến tham chiếu phải cùng kiểu dữ liệu.
Nói cách khác, cả hai tham chiếu phải thuộc cùng một lớp.
Ví dụ tham chiếu một lớp vào một lớp khác:
public class TestObjectReferences {
public static void main(String[] args) {
// Instantiates an object of type Rectangle and stores its reference in the object reference variable, objRecl
Rectangle objRec1 = new Rectangle(10, 20);
// Declares a reference variable of type Rectangle
Rectangle objRec2;
// Assigns the value of objRecl to objRec2
objRec2 = objRec1;
System.out.println("\nRectangle1 Details");
// Invokes the method that displays values of the instance variables for object, objRecl
objRec1.displayDimensions();
System.out.println("\nRectangle2 Details");
// Invokes the method that displays values of the instance variables for object, objRec2
objRec2.displayDimensions();
}
}
Đoạn mã trên tạo hai biến tham chiếu đối tượng, objRec1 và objRec2. objRec1 trỏ đến đối tượng đã được cấp phát bộ nhớ và khởi tạo giá trị là 10 và 20, trong khi objRec2 không trỏ đến bất kỳ đối tượng nào. Đối với câu lệnh, objRec2 = objRec1; tham chiếu được lưu trữ trong objRec1 được sao chép vào objRec2, nghĩa là địa chỉ trong objRec1 được sao chép vào objRee2. Do đó, các tham chiếu được sao chép giữa các biến được tạo trên stack mà không ảnh hưởng đến các đối tượng thực tế được tạo trên vùng nhớ heap.
Tính đóng gói (Encapsulation)
Hãy xem xét một kịch bản
Khi bạn muốn học lái xe ô tô. Là người mới học, bạn không cần thiết phải hiểu chi tiết về cách hoạt động của động cơ, cấu tạo bình xăng, bánh xe thực hiện chức năng rẽ phải hay rẽ trái, v.v. Là người lái xe, bạn quan tâm và quan tâm đến cách khởi động và tắt động cơ cũng như cách cung cấp nhiên liệu cho xe. Ngoài ra, bạn có thể muốn biết cách sử dụng số và phanh để hiểu biết đầy đủ về ô tô. Như vậy, để lái 2 ô tô, bạn quan tâm đến giao diện, tính năng bên ngoài của ô tô hơn là chức năng của từng bộ phận.
Tương tự, trong lập trình hướng đối tượng, khái niệm ẩn chi tiết triển khai của một đối tượng đạt được bằng cách áp dụng khái niệm đóng gói.
Các chi tiết triển khai về nội dung của một lớp không bắt buộc phải hiển thị đối với các lớp và đối tượng khác sử dụng nó. Thay vào đó, chỉ những thông tin cụ thể mới có thể được hiển thị cho các thành phần khác của ứng dụng và phần còn lại có thể bị ẩn. Điều này đạt được thông qua việc đóng gói, còn gọi là ẩn dữ liệu. Nói cách khác, có thể nói rằng trong các ngôn ngữ OOP, việc đóng gói bao trùm các hoạt động bên trong của một đối tượng java. Mục đích chính của việc ẩn dữ liệu trong một lớp là giảm độ phức tạp trong quá trình phát triển phần mềm. Bằng cách ẩn chi tiết triển khai về những gì cần thiết để triển khai thao tác cụ thể trong lớp, việc sử dụng thao tác trở nên đơn giản. Trong Java, việc ẩn dữ liệu được thực hiện bằng cách sử dụng các bổ từ truy cập (access modifier).
Đóng gói dữ liệu ẩn các biến thể hiện đại diện cho trạng thái của một đối tượng. Do đó, sự tương tác hoặc sửa đổi duy nhất được thực hiện thông qua các phương thức. Ví dụ: để sửa đổi tên chủ tài khoản, phương thức thích hợp như changeHolderName() có thể được cung cấp trong lớp Account. Bằng cách này, việc triển khai lớp này sẽ bị ẩn khỏi phần còn lại của ứng dụng.
Bổ từ truy cập (Access modifier)
Bổ từ truy cập xác định cách các thành viên của một lớp, chẳng hạn như biến thể hiện và các phương thức có thể truy cập được từ bên ngoài lớp. Nói cách khác, họ xác định khả năng hiển thị của các thành viên. Chúng được sử dụng làm tiền tố khi khai báo các thành viên của một lớp. Phạm vi hoặc khả năng hiển thị của các thành viên cũng liên quan chặt chẽ đến các gói (package). Thông thường, cácbổ từ truy cập được sử dụng bất cứ khi nào có thể để đảm bảo tính đóng gói và hạn chế quyền truy cập đối với các thành viên của lớp.
Lưu ý: Đôi khi, bổ từ truy cập cũng được gọi là công cụ sửa đổi truy cập. Mặc dù cả hai thuật ngữ đều có nghĩa giống nhau, nhưng chúng tôi khuyên bạn nên sử dụng từ bổ từ truy cập làm thuật ngữ chính thức.
Sử dụng bổ từ truy cập có các ưu điểm sau:
- Bổ từ truy cập giúp kiểm soát quyền truy cập của các lớp và thành viên lớp.
- Công cụ sửa đổi quyền truy cập giúp ngăn chặn việc lạm dụng chi tiết lớp cũng như ẩn chi tiết triển khai mà các lớp khác không yêu cầu.
- Bổ từ truy cập cũng xác định xem các lớp và thành viên của lớp có thể được gọi bởi các lớp hoặc interface khác hay không.
- Khả năng truy cập ảnh hưởng đến tính kế thừa và cách các thành viên được lớp con kế thừa.
- Theo mặc định, package luôn có thể truy cập được.
Java cung cấp bốn loại bổ từ truy cập như sau:
public
Bổ từ truy cập public là bổ từ truy cập ít hạn chế nhất. Một trường, phương thức hoặc lớp được khai báo công khai sẽ hiển thị với bất kỳ lớp nào trong ứng dụng Java trong cùng một gói hoặc trong một gói khác. Các thành viên được khai báo là public có thể được truy cập từ bất kỳ đâu trong lớp cũng như từ các lớp khác.
private
Bổ từ truy cập private là bổ từ truy cập hạn chế nhất trong tất cả các bổ từ truy cập. Bổ từ truy cập private không thể được sử dụng cho các lớp và interface cũng như các trường và phương thức của interface. Các trường và phương thức được khai báo là private không thể được truy cập từ bên ngoài lớp . Một quy ước tiêu chuẩn là khai báo tất cả các trường là private và cung cấp các phương thức setter hoặc getter public để truy cập chúng. Do đó, khi dữ liệu quan trọng, nhạy cảm và không thể chia sẻ với người khác, nó sẽ được khai báo là private.
protected
Bổ từ truy cập được protected được sử dụng với các lớp có chung mối quan hệ cha-con được gọi là sự kế thừa. Từ khóa protected không thể được sử dụng cho các lớp và interface cũng như các trường và phương thức của interface. Các trường và phương thức được khai báo là được protected trong lớp cha (parent class) hoặc lớp super (super class) chỉ có thể được truy cập bởi child class hoặc sub class của nó trong các package khác. Bổ từ truy cập được protected cho phép các thành viên của lớp có thể truy cập được từ bên trong lớp cũng như từ bên trong các lớp dẫn xuất. Các lớp dẫn xuất là các lớp con được tạo trên cơ sở các lớp hoặc interface hiện có. Tuy nhiên, các lớp trong cùng một package cũng có thể truy cập các trường và phương thức protected, ngay cả khi chúng không phải là lớp con của lớp thành viên protected.
package (mặc định)
Bổ từ truy cập package (gói) chỉ cho phép các thành viên công khai của một lớp có thể truy cập được đối với tất cả các lớp có trong cùng một package. Đây là cấp độ truy cập mặc định cho tất cả các thành viên của lớp. package hoặc bổ từ truy cập mặc định được sử dụng khi không có bổ từ truy cập. Bổ từ này được áp dụng cho bất kỳ lớp, trường hoặc phương thức nào mà không có bổ từ truy cập nào được đề cập. Với bổ từ này, lớp, trường hoặc phương thức chỉ có thể được truy cập bởi các lớp trong cùng một package. Bổ từ này không thể được sử dụng cho các trường và phương thức trong một interface.
Theo nguyên tắc chung trong Java, chi tiết và cách triển khai của một lớp sẽ bị ẩn khỏi các lớp khác hoặc các đối tượng bên ngoài trong ứng dụng. Điều này được thực hiện bằng cách đặt các biến thể hiện (instance variables) là phương thức riêng tư và phương thức thể hiện (instance methods) là công khai.
Access Modifiers | Class | Package | Subclass | Global |
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
No modifiers (default) | Y | Y | N | N |
private | Y | N | N | N |
Cột đầu tiên cho biết liệu lớp đó có quyền truy cập vào các thành viên dữ liệu của chính nó hay không. Có thể thấy, một lớp luôn có thể truy cập các thành viên của chính nó. Cột thứ hai cho biết liệu các lớp trong cùng package với lớp chủ sở hữu (bất kể nguồn gốc của chúng) có thể truy cập thành viên hay không. Có thể thấy, tất cả các thành viên đều có thể được truy cập ngoại trừ các thành viên private. Cột thứ ba cho biết liệu các lớp con của lớp được khai báo bên ngoài package này có thể truy cập thành viên hay không. Trong những trường hợp như vậy, các thành viên public và được protected có thể được truy cập. Cột thứ tư cho biết liệu tất cả các lớp có thể truy cập thành viên dữ liệu hay không.
Quy tắc kiểm soát truy cập
Java có các quy tắc và ràng buộc đối với việc sử dụng các bổ từ truy cập như sau:
- trong khi khai báo các thành viên, không thể sử dụng bổ từ truy cập private với abstract, nhưng nó có thể được sử dụng với final hoặc static.
- Không có bổ từ truy cập nào có thể được lặp lại hai lần trong một lần khai báo.
- Constructor khi được khai báo là private sẽ có thể truy cập được trong lớp nơi nó được tạo.
- Constructor khi được khai báo là protected sẽ có thể truy cập được trong lớp nơi nó được tạo và trong các lớp kế thừa.
- private không thể được sử dụng với các trường và phương thức của interface.
- Mức truy cập hạn chế nhất phải được sử dụng phù hợp với một thành viên cụ thể.
- Hầu hết, bổ từ truy cập private luôn được sử dụng trừ khi có lý do chính đáng để không sử dụng nó.
- Tránh sử dụng public cho các trường ngoại trừ các hằng số.
public class NewRectangle {
// Declare instance variables
private int width;
private int height;
// Declare a no-argument constructor
public NewRectangle() {
System.out.println("Constructor Invoked...");
}
// Declare a parameterized constructor with two parameters
public NewRectangle(int width, int height) {
this.width = width;
this.height = height;
System.out.println("Parameterized Constructor Invoked...");
}
// Display the dimensions of the NewRectangle object
public void displayDimensions() {
System.out.println("Width: " + width);
System.out.println("Height: " + height);
}
public static void main(String[] args) {
// Example usage of the NewRectangle class
NewRectangle rectangle = new NewRectangle(10, 20);
rectangle.displayDimensions();
}
}
Đoạn mã trên bổ từ truy cập của các biến width và height của lớp NewRectangle từ mặc định sang private. Điều này có nghĩa là các trường lớp không thể truy cập trực tiếp từ bên ngoài lớp. Điều này được thực hiện để hạn chế quyền truy cập vào các thành viên dữ liệu của lớp. Tương tự, các bổ từ truy cập cho các phương thức được thay đổi thành public. Do đó, người dùng có thể truy cập các thành viên của lớp thông qua các phương thức của nó mà không ảnh hưởng đến việc triển khai nội bộ của lớp.
Object Initializers (Công cụ khởi tạo đối tượng)
Trình khởi tạo đối tượng trong Java cung cấp cách tạo một đối tượng và khởi tạo các trường của nó. Trong cách tiếp cận thông thường, bạn gọi một hàm khởi tạo để khởi tạo các đối tượng, nhưng bằng cách sử dụng các bộ khởi tạo đối tượng, bạn có thể bổ sung cho việc sử dụng các nhà xây dựng.
Có hai cách tiếp cận để khởi tạo các trường hoặc biến thể hiện của các đối tượng mới được tạo.
Sử dụng Công cụ khởi tạo biến đối tượng (Instance Variable Initializers)
Trong phương pháp này, bạn chỉ định tên của các trường và/hoặc thuộc tính sẽ được khởi tạo và đưa ra giá trị ban đầu cho mỗi trường.
Ví dụ:
public class Person {
private String name = "John";
private int age = 12;
// Displays the details of a Person object
void displayDetails() {
System.out.println("Person Details");
System.out.println("Person Name: " + name);
System.out.println("Person Age: " + age);
}
public static void main(String[] args) {
// Example usage of the Person class
Person person = new Person();
person.displayDetails();
}
}
Trong đoạn mã, biến name và age thể hiện được khởi tạo thành giá trị ‘John’ và 12 tương ứng. Việc khởi tạo các biến trong khai báo lớp không yêu cầu chúng phải khởi tạo trong hàm khởi tạo.
Ví dụ dưới trình bày một chương trình Java khai báo một lớp, Người và khởi tạo các trường của nó:
public class TestPerson {
public static void main(String[] args) {
// Create a Person object
Person objPerson1 = new Person();
// Display the details of the Person object
objPerson1.displayDetails();
}
}
Sử dụng khối khởi tạo (Initialization Block)
Theo cách tiếp cận này, một khối khởi tạo được chỉ định trong lớp. Khối khởi tạo được thực thi trước khi thực thi các hàm khởi tạo trong quá trình khởi tạo đối tượng.
public class Account {
private int accountID;
private String holderName;
private String accountType;
// Initialization block
{
accountID = 100;
holderName = "John Anderson";
accountType = "Savings";
}
// Displays the details of the Account object
public void displayAccountDetails() {
System.out.println("Account Details");
System.out.println("Account ID: " + accountID);
System.out.println("Account Type: " + accountType);
System.out.println("Holder Name: " + holderName);
}
public static void main(String[] args) {
// Example usage of the Account class
Account account = new Account();
account.displayAccountDetails();
}
}
Trong mã, các khối khởi tạo sẽ khởi tạo các biến thể hiện (instance variables) hoặc các trường của lớp. Các khối khởi tạo về cơ bản được sử dụng để thực hiện các chuỗi khởi tạo phức tạp.
public class TestInitializationBlock {
public static void main(String[] args) {
// Create an Account object
Account objAccount = new Account();
// Display the account details
objAccount.displayAccountDetails();
}
}
Khái niệm Phương thức (method)
Hãy xem xét tình huống trong đó người dùng muốn thực hiện một số phép toán trên hai số. Các số có thể là số nguyên hoặc số thực. Một cách để làm điều này là khai báo một số biến khác nhau trong lớp và viết số lượng biểu thức tùy theo số lượng và loại phép toán được yêu cầu. Trong trường hợp này, tất cả các thao tác sẽ được thực hiện cùng một lúc trên các con số trong quá trình thực hiện chương trình.
Tuy nhiên, điều gì sẽ xảy ra nếu người dùng chỉ muốn thực hiện một thao tác duy nhất tại một thời điểm? Trong trường hợp như vậy, cần có một tính năng cho phép chỉ thực hiện các biểu thức được yêu cầu chứ không phải toàn bộ chương trình. Các phương thức trong Java là một tính năng cho phép nhóm các câu lệnh và thực thi một tập hợp các câu lệnh cụ thể thay vì thực thi toàn bộ chương trình. Một lần nữa, nếu người dùng muốn hạn chế quyền truy cập vào một số phương thức nhất định thì sao? Java cung cấp một tập hợp các bổ từ truy cập có thể giúp người dùng hoàn thành nhiệm vụ này.
Một phương thức Java có thể được định nghĩa là một tập hợp các câu lệnh được nhóm lại với nhau để thực hiện một tác vụ cụ thể. Ví dụ: một lệnh gọi phương thức main() là điểm bắt đầu của bất kỳ chương trình Java nào, sẽ thực thi tất cả các câu lệnh được viết trong phạm vi của phương thức main().
Cú pháp khai báo phương thức:
modifier return_type method_name(parameter_list) {
// Body of the method
}
Trong đó:
modifier: Xác định mức độ hiển thị của phương thức. Khả năng hiển thị cho biết đối tượng nào có thể truy cập phương thức. Các giá trị có thể là public, private hoặc protected.
return_type: Xác định kiểu dữ liệu của giá trị được trả về bởi phương thức.
method_name: Xác định tên phương thức
list_of_parameters: Chỉ định danh sách tham số được phân cách bằng dấu phẩy được truyền cho phương thức.
Nói chung, một khai báo phương thức có các thành phần theo thứ tự sau:
- Các bổ từ truy cập như public, private và protected
- Kiểu trả về (return type) cho biết kiểu dữ liệu của giá trị được trả về bởi phương thức. Kiểu trả về được đặt thành void nếu phương thức không trả về giá trị.
- Tên phương thức được chỉ định dựa trên các quy tắc nhất định. Một tên phương thức:
- không thể là từ khóa Java
- không được có dấu cách
- không thể bắt đầu bằng một chữ số
- không thể bắt đầu bằng bất kỳ ký hiệu nào ngoài $ hoặc _
- có thể là động từ viết thường
- có thể là tên nhiều từ bắt đầu bằng một động từ viết thường, theo sau là tính từ hoặc có thể là tên nhiều từ với chữ cái đầu tiên của từ thứ hai và mỗi từ theo sau được viết hoa.
- Phải có tính mô tả và có ý nghĩa
- Một số tên phương thức hợp lệ là add, _view, $cal, add_num, setFirstName, CompareTo, AsValid … Tuy nhiên mình hay sử dụng theo quy ước tenPhuongThuc
- Danh sách tham số trong ngoặc đơn được phân tách bằng dấu phẩy. Mỗi tham số được đặt trước bởi kiểu dữ liệu của nó. Nếu không có tham số, dấu ngoặc đơn trống sẽ được sử dụng.
- Danh sách ngoại lệ chỉ định tên của các ngoại lệ có thể được phương thức đưa ra. Ngoại lệ là một sự kiện xảy ra trong quá trình thực thi chương trình, làm gián đoạn quá trình thực hiện chương trình.
- Phần thân phương thức bao gồm một tập hợp các câu lệnh được đặt trong cặp dấu ngoặc nhọn {}. Phần thân phương thức có thể có các biến, lệnh gọi phương thức và thậm chí cả các lớp.
Có 2 thành phần của khai báo phương thức là tên phương thức và danh sách tham số, kết hợp lại thành khái niệm chữ ký phương thức (method signature)
Tạo và gọi phương thức
Như đã thảo luận trước đó, các phương thức giúp tách biệt các nhiệm vụ khác nhau để cung cấp tính mô đun cho chương trình. Một chương trình có tính mô-đun khi các nhiệm vụ khác nhau trong chương trình được nhóm lại với nhau thành các mô-đun hoặc phân vùng.
Ví dụ: để thực hiện các loại phép toán khác nhau như cộng, trừ, nhân, v.v., người dùng có thể tạo các phương thức riêng lẻ như trong hình
Ví dụ khai báo phương thức add:
public void add(int num1, int num2) {
int num3; // Declare a variable
num3 = num1 + num2; // Perform the addition of numbers
System.out.println("Addition is " + num3); // Print the result
}
Đoạn mã trên định nghĩa một phương thức có tên add() chấp nhận hai tham số num1 và num2, mỗi tham số có kiểu dữ liệu int. Ngoài ra, phương thức này đã được khai báo bằng bổ từ truy cập public, điều đó có nghĩa là tất cả các đối tượng đều có thể truy cập nó. Kiểu trả về được đặt thành void cho biết rằng phương thức này không trả về bất kỳ thứ gì.
Phần thân phương thức bao gồm ba câu lệnh. Câu lệnh đầu tiên ‘int num3;’ là khai báo một biến số nguyên có tên num3. Câu lệnh thứ hai “num3 = num1 + num2” là một phép toán cộng được thực hiện trên các tham số num và num2 bằng toán tử số học “+”. Kết quả được lưu trữ trong biến thứ ba num3 bằng cách sử dụng toán tử gán “=”
Câu lệnh cuối cùng System.out.println(“Addition is “+ num3);’được dùng để in giá trị của biến num3. Chữ ký phương thức là ‘add (int, int)’.
Trong khi tạo một phương thức, người dùng xác định cấu trúc và chức năng của phương thức đó. Nói cách khác, nhà phát triển chỉ định phương thức sẽ thực hiện.
Tuy nhiên, để sử dụng phương thức này, nó phải được gọi hoặc gọi ra. Khi một chương trình gọi một phương thức, điều khiển sẽ được chuyển sang phương thức được gọi. Phương thức được gọi sẽ thực thi và trả lại quyền điều khiển cho phương thức gọi. Phương thức trả về dẽ liệu sau khi câu lệnh return của một phương thức được thực thi hoặc khi đạt đến dấu ngoặc đóng.
Một phương thức có thể được gọi theo một trong các cách sau:
Nếu phương thức trả về một giá trị thì lệnh gọi phương thức đó sẽ trả về một giá trị nào đó từ phương thức đó cho người gọi. Ví dụ:
int result = obj.add(20, 30);
Giả sử rằng phương thức add() trả về một giá trị Integer, lệnh gọi phương thức được đặt ở bên phải của toán tử gán. Giá trị trả về sẽ được lưu trữ trong một biến có tên là result được đặt ở bên trái toán tử gán. Kiểu trả về của một phương thức chỉ định loại giá trị mà phương thức trả về. Vì vậy, kiểu dữ liệu của biến result phải giống với kiểu trả về của phương thức add().
Nếu kiểu trả về của phương thức được đặt thành void thì lệnh gọi phương thức sẽ dẫn đến việc thực thi các câu lệnh trong phương thức mà không trả về bất kỳ giá trị nào cho phương thức gọi. Ví dụ: phương thức add() trong Đoạn mã ví dụ trước đó trả về void và in kết quả đầu ra bằng câu lệnh System.out.println(“Addition is “+ num3);
Do đó, lệnh gọi phương thức sẽ là obj.add (23, 30) mà không trả lại bất cứ thứ gì cho phương thức gọi. Trong những trường hợp như vậy, lệnh gọi phương thức sẽ là một câu lệnh.
Dự án bao gồm một gói có tên calculate với lớp Calculator có phương thức main(). Một số phương thức thực hiện các phép toán có thể được thêm vào lớp.
package calculate;
public class Calculator {
public static void main(String[] args) {
// Your main method logic here
System.out.println("Calculator App");
// Example usage of mathematical operation methods
double result1 = add(5, 3);
double result2 = subtract(10, 7);
double result3 = multiply(4, 6);
double result4 = divide(15, 3);
System.out.println("Addition: " + result1);
System.out.println("Subtraction: " + result2);
System.out.println("Multiplication: " + result3);
System.out.println("Division: " + result4);
}
// Mathematical operation methods
public static double add(double a, double b) {
return a + b;
}
public static double subtract(double a, double b) {
return a - b;
}
public static double multiply(double a, double b) {
return a * b;
}
public static double divide(double a, double b) {
if (b == 0) {
throw new ArithmeticException("Cannot divide by zero");
}
return a / b;
}
}
Lưu ý – Câu lệnh package calculate; chỉ ra rằng lớp thuộc về một gói có tên calculate. Một gói là một thư mục được sử dụng để nhóm các lớp liên quan. Ngoài ra, văn bản bên trong các ký hiệu /**… */ nếu có là các comment javadoc mặc định được bộ thực thi tạo ra để mô tả mã.
Tất cả các phương thức được định nghĩa trong lớp Máy tính đều là các phương thức thể hiện/thực thể. Các phương thức thực thể là những phương thức được gọi bằng cách sử dụng một đối tượng của lớp. Ngoài ra, tất cả các phương thức đều công khai, điều đó có nghĩa là chúng có thể truy cập được đối với tất cả các đối tượng trên các gói.
Truyền vào và trả về giá trị từ các phương thức
Tham số (parameters) là danh sách các biến được chỉ định trong khai báo phương thức, trong khi đối số (arguments) là giá trị thực được truyền cho phương thức khi nó được gọi. Khi một phương thức được gọi, loại và thứ tự của các đối số được truyền phải khớp với loại và thứ tự của các tham số được khai báo trong phương thức.
Một phương thức có thể chấp nhận giá trị của bất kỳ loại dữ liệu nào làm tham số. Một phương thức có thể chấp nhận các kiểu dữ liệu nguyên thủy như int, float, double, v.v. cũng như các kiểu dữ liệu tham chiếu như mảng và đối tượng làm tham số. Nói cách khác, các đối số có thể được truyền theo giá trị hoặc theo tham chiếu như sau:
Truyền đối số theo giá trị (pass by value)
Khi các đối số được truyền theo giá trị, nó được gọi là call-by-value và điều đó có nghĩa là:
- Một bản sao của đối số được truyền từ phương thức gọi sang phương thức được gọi.
- Những thay đổi được thực hiện đối với đối số được truyền trong phương thức được gọi sẽ không sửa đổi giá trị trong phương thức gọi.
- Các biến kiểu dữ liệu nguyên thủy như int, float được truyền theo giá trị.
Ví dụ:
public class PassByValueExample {
public static void main(String[] args) {
int number = 5;
System.out.println("Original number: " + number);
modifyNumber(number);
System.out.println("After method call, number remains unchanged: " + number);
}
public static void modifyNumber(int num) {
num = num * 2;
System.out.println("Inside modifyNumber method: " + num);
}
}
Truyền đối số bằng tham chiếu (pass by reference)
Khi các đối số được truyền bằng tham chiếu, điều đó có nghĩa là:
- Vị trí vùng nhớ thực của đối số được truyền cho phương thức được gọi và không truyền bản sao của đối tượng vào phương thức được gọi.
- Phương thức được gọi có thể thay đổi giá trị của đối số được truyền cho nó.
- Các biến có kiểu tham chiếu như đối tượng được truyền cho các phương thức bằng tham chiếu.
Ví dụ:
class Student {
String name;
Student(String name) {
this.name = name;
}
}
public class PassByReferenceExample {
public static void main(String[] args) {
Student student = new Student("Alice");
System.out.println("Original name: " + student.name);
modifyStudentName(student);
System.out.println("After method call, name is modified: " + student.name);
}
public static void modifyStudentName(Student s) {
s.name = "Bob";
System.out.println("Inside modifyStudentName method: " + s.name);
}
}
Trả về giá trị từ phương thức
Một phương thức sẽ chỉ trả về một giá trị cho phương thức gọi khi tất cả các câu lệnh trong phương thức gọi đó đã hoàn tất hoặc khi nó gặp một câu lệnh return hoặc khi một ngoại lệ được ném ra. Câu lệnh return được viết trong phần thân của phương thức để trả về một giá trị.
Một phương thức void sẽ không có kiểu trả về được chỉ định trong phần thân phương thức của nó. Lỗi trình biên dịch được tạo ra khi phương thức void trả về một giá trị.
Ví dụ khai báo lớp với phương thức có giá trị trả về:
public class Circle {
// Property PI
private double PI;
// Constructor to initialize PI
public Circle(double PI) {
this.PI = PI;
}
// Getter method for PI
public double getPI() {
return PI;
}
// Setter method for PI
public void setPI(double PI) {
this.PI = PI;
}
// Method to calculate the area of a circle
public double calculateArea(double radius) {
return PI * radius * radius;
}
// Method to calculate the circumference of a circle
public double calculateCircumference(double radius) {
return 2 * PI * radius;
}
public static void main(String[] args) {
Circle circle = new Circle(3.14159); // Initialize PI to a default value
System.out.println("PI: " + circle.getPI());
circle.setPI(3.14159265359); // Set a new value for PI
System.out.println("Updated PI: " + circle.getPI());
double radius = 5.0;
double area = circle.calculateArea(radius);
double circumference = circle.calculateCircumference(radius);
System.out.println("Area of the circle with radius " + radius + ": " + area);
System.out.println("Circumference of the circle with radius " + radius + ": " + circumference);
}
}
Biến PI được khai báo là private để hạn chế bất kỳ đối tượng nào truy cập trực tiếp vào nó. Thay vào đó, phương thức getPI() được sử dụng để truy cập nó. Do đó, phương thức getPI() trở thành phương thức truy cập cho PI. Tương tự, người ta có thể tạo một phương thức biến đổi như setPI() để sửa đổi giá trị của PI.
Phương pháp Khai báo các biến đối số (Argument Methods)
Java cung cấp một tính năng gọi là varargs để truyền số lượng đối số thay đổi cho một phương thức. varargs được sử dụng khi số lượng của một loại đối số cụ thể sẽ được truyền cho một phương thức không được biết cho đến khi chạy. Nó phục vụ như một lối tắt để tạo một mảng theo cách thủ công.
Để sử dụng các biến thể, loại tham số cuối cùng được theo sau bởi dấu ba chấm (. . .), sau đó là khoảng trắng, theo sau là tên của tham số. Phương thức này có thể được gọi với bất kỳ số lượng giá trị nào cho tham số đó, bao gồm cả không có giá trị.
Cú pháp biến đối số:
<method_name> (type ... variableName) { //method body }
Trong đó
… chỉ ra rằng có nhiều tham số và không xác định trước số lượng tham số
Ví dụ
public class VarargsExample {
// A method that accepts a variable number of integers
public static int sum(int... numbers) {
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}
public static void main(String[] args) {
int result1 = sum(1, 2, 3); // Calling with three arguments
int result2 = sum(10, 20, 30, 40, 50); // Calling with five arguments
System.out.println("Sum 1: " + result1);
System.out.println("Sum 2: " + result2);
}
}
Sử dụng Javadoc để tra cứu các phương thức của một lớp
Java cung cấp một công cụ JDK có tên Javadoc được sử dụng để tạo tài liệu API dưới dạng trang HTML từ các comment khai báo và comment tài liệu. Những nhận xét này là mô tả về mã được viết trong một chương trình. Các thuật ngữ khác nhau được sử dụng khi tạo javadoc như sau:
Java cung cấp một công cụ JDK có tên Javadoc được sử dụng để tạo tài liệu API dưới dạng trang HTML từ các nhận xét khai báo và tài liệu. Những nhận xét này là mô tả về mã được viết trong một chương trình. Các thuật ngữ khác nhau được sử dụng khi tạo javadoc như sau:
API documentation hoặc API docs (tài liệu API): Đây là những mô tả trực tuyến hoặc bản cứng của API chủ yếu dành cho các lập trình viên. Đặc tả API bao gồm tất cả các xác nhận để triển khai Nền tảng Java đúng cách nhằm đảm bảo rằng tính năng ‘viết một lần, chạy mọi nơi của Java được giữ lại.
Documentation comments hoặc doc comments: Đây là những comment đặc biệt trong mã nguồn Java. Chúng được viết trong dấu phân cách /** … */. Những comment này được xử lý bằng công cụ Javadoc để tạo tài liệu API.
Bốn loại tệp nguồn mà công cụ Javadoc có thể tạo đầu ra như sau:
- Tệp mã nguồn Java (.java) bao gồm các chú thích trường, lớp, hàm khởi tạo, phương thức và giao diện.
- Tệp chú thích gói bao gồm các chú thích gói,
- Tổng quan file nhận xét chứa các nhận xét về bộ gói.
- Các tệp khác chưa được xử lý như hình ảnh, tệp lớp, mã nguồn mẫu, tệp HTML, applet và bất kỳ tệp nào khác được tham chiếu từ các tệp trước đó.
Doc comment phải đứng trước khai báo trường, lớp, phương thức hoặc hàm khởi tạo. Doc comment bao gồm hai phần là phần mô tả và thẻ khối.
Ví dụ:
/**
* This is an example class that demonstrates JavaDoc comments.
* It provides information about the class and its purpose.
*/
public class JavaDocExample {
/**
* This is a constructor for the JavaDocExample class.
* It initializes a new instance of the class.
*/
public JavaDocExample() {
// Constructor logic here
}
/**
* This method adds two numbers and returns the result.
*
* @param a The first number to be added.
* @param b The second number to be added.
* @return The sum of the two numbers.
*/
public int add(int a, int b) {
return a + b;
}
/**
* Main method to start the program.
* It prints a welcome message to the console.
*
* @param args The command-line arguments (not used in this example).
*/
public static void main(String[] args) {
System.out.println("Welcome to the JavaDoc Example!");
}
}
Cách xem nhanh javadoc trên netbean IDE:
Cách tạo Javadoc bằng netbean IDE:
Lưu ý rằng vị trí của mô tả, tham số và kiểu dữ liệu trả về đã được công cụ Javadoc tự động điều chỉnh. Như vậy, có thể kết luận rằng javadoc tạo ra một tệp HTML chứa thông tin về từng lớp. Nó sẽ xuất ra một chỉ mục và một cây phân cấp.
Tương tự, công cụ Javadoc có thể được sử dụng để tra cứu các phương thức tích hợp khác cũng như tạo javadoc cho các phương thức do người dùng xác định.
Sử dụng bổ từ truy cập với biến và phương thức
Bổ từ truy cập có thể được sử dụng với các biến và phương thức của một lớp để hạn chế quyền truy cập từ các lớp khác. Đoạn mã dưới trình bày một ví dụ về cách sử dụng bổ từ truy cập với các biến và phương thức.
package session4;
public class Employee {
// Variables with default access
int empID; // Variable to store employee ID
String empName; // Variable to store employee name
// Variables with private and protected access
private String SSN; // Variable to store social security number
protected String empDesig; // Variable to store designation
/**
* Parameterized constructor
*
* @param ID an integer variable storing the employee ID
* @param name a String variable storing the employee name
*/
public Employee(int ID, String name) {
empID = ID;
empName = name;
}
/**
* Returns the value of SSN
*
* @return String
*/
public String getSsn() { // Accessor for SSN
return SSN;
}
/**
* Sets the value of SSN
*
* @param ssn a String variable storing the social security number
*/
public void setSsn(String ssn) { // Mutator for SSN
SSN = ssn;
}
/**
* Sets the value of Designation
*
* @param desig a String variable storing the employee designation
*/
public void setDesignation(String desig) { // Public method
empDesig = desig;
}
/**
* Displays employee details
*/
public void display() { // Public method
System.out.println("Employee ID is " + empID);
System.out.println("Employee name is " + empName);
System.out.println("Designation is " + empDesig);
System.out.println("SSN is " + SSN);
}
public static void main(String[] args) {
// Instantiate the Employee class
Employee objEmp1 = new Employee(1200, "Roger Stevens");
// Assign values to public variables
objEmp1.empDesig = "Manager";
objEmp1.setSsn("281-72-3873");
// Invoke the public method
objEmp1.display();
}
}
Trong Đoạn trên, lớp Nhân viên đã được khai báo public. Lớp này có hai thành viên với công cụ sửa đổi truy cập mặc định là empID và empName, 2 thành viên lớp được bảo vệ có tên empDesig và một thành viên lớp riêng có tên SSN. Lớp này có một hàm khởi tạo được tham số hóa được sử dụng để đặt giá trị của các biến thành viên empID và empName. Lớp cũng có một trình truy cập và một trình biến đổi
phương thức cho biến SSW và hai phương thức công khai khác.
Phương thức main() tạo một đối tượng của lớp Nhân viên với các giá trị empID và empName. Tiếp theo, các giá trị của empDesig và SSW được chỉ định bằng cách truy cập trực tiếp vào các biến có đối tượng objEmp1 mặc dù empDesig được bảo vệ và SSW là riêng tư. Điều này là do objEmp1 là một đối tượng có trong cùng một lớp. Vì vậy, objEmp1 có quyền truy cập vào các thành viên dữ liệu bằng bất kỳ công cụ sửa đổi quyền truy cập nào. Phương thức display() được sử dụng để hiển thị tất cả các giá trị như được hiển thị ở đầu ra.
package session4;
public class EmployeeDetails {
/**
* Main method that takes command line arguments
*/
public static void main(String[] args) {
// Instantiate the Employee class within EmployeeDetails class
Employee objEmp = new Employee(1300, "Clara Smith");
// Assign a value to the protected variable
objEmp.empDesig = "Receptionist";
// Use the mutator method to set the value of the private variable
objEmp.setSSN("282-72-3873");
// Invoke the public method
objEmp.display();
}
}
Trong phương thức main() của lớp EmployeeDetails, một đối tượng Nhân viên được tạo để đặt các giá trị của empID và empNane. Tiếp theo, giá trị của empDesigiis được chỉ định bằng cách truy cập trực tiếp vào biến được bảo vệ empDesig. Điều này là do, một biến được protected có thể được truy cập bởi một lớp khác của cùng một gói ngay cả khi đó không phải là lớp con của nhân viên.
Tuy nhiên, để đặt giá trị SSN, phương thức setSSN() được sử dụng. Điều này là do SSN là biến thành viên private trong lớp Employee và do đó, các lớp khác không thể truy cập trực tiếp. Cuối cùng, phương thức display() được sử dụng để in tất cả các chi tiết như hiển thị ở đầu ra.
Hãy xem xét lớp Máy tính được tạo trước đó. Lớp có các phương thức khác nhau cho các hoạt động khác nhau. Tuy nhiên, tất cả các phương pháp chỉ thêm số nguyên. Điều gì sẽ xảy ra nếu người dùng muốn cộng các số có kiểu khác nhau như hai số dấu phẩy động hoặc một số nguyên và số dấu phẩy động khác? Một giải pháp là tạo một phương thức có tên khác nhau như addFloat() hoặc addIntFloat() cho mục đích này. Tuy nhiên, điều này sẽ rất tẻ nhạt và không cần thiết vì chức năng của hai phương thức vẫn là phép cộng.
Nạp chồng phương thức (Method Overloading)
Xét tình huống đang trong một lớp Calculator có 2 phưng thức addFloat() và addIntFloat() sử dụng để thực hiện các phép cộng. Điều gì sẽ xảy ra nếu người dùng muốn cộng các số có kiểu khác nhau như hai số thực hoặc một số nguyên và số dấu thực khác? Một giải pháp là tạo một phương thức có tên khác nhau như addFloat() hoặc addIntFloat() cho mục đích này. Tuy nhiên, điều này sẽ rất tẻ nhạt và không cần thiết vì chức năng của hai phương thức vẫn là phép cộng.
Sẽ thuận tiện hơn nếu có cách tạo các biến thể khác nhau của cùng một phương thức add() để thêm các kiểu giá trị khác nhau. Ngôn ngữ lập trình Java cung cấp tính năng nạp chồng phương thức (method overloading) để phân biệt giữa các phương thức có chữ ký phương thức khác nhau. Sử dụng nạp chồng phương thức, nhiều phương thức của một lớp có thể có cùng tên nhưng có danh sách tham số khác nhau.
Nạp chồng phương thức có thể được thực hiện theo ba cách:
- Thay đổi số lượng tham số
- Thay đổi thứ tự các tham số
- Thay đổi kiểu dữ liệu cho tham số
Hình ảnh minh họa các overload method của một phương thức add()
Nạp chồng với danh sách tham số khác nhau
Các phương thức có thể bị nạp chồng bằng cách thay đổi số lượng hoặc chuỗi tham số của phương thức.
Ví dụ:
void add(int a, int b)
void add(int a, int b, int c)
Ví dụ cho thấy 2 phương thức add(). Từ ảnh minh họa Cả hai phương thức đều chấp nhận số nguyên làm tham số. Tuy nhiên, chúng khác nhau về số lượng tham số truyền vào.
Ví dụ 2:
int add(float a, int b)
void add(int a, float c)
Ở ví dụ 2, phương thức add() chấp nhận 2 biến làm tham số với kiểu dữ liệu int và float. Tuy nhiên, chúng khác nhau ở trình tự chấp nhận int và float.
Nạp chồng với các kiểu dữ liệu khác nhau
Các phương thức có thể nạp chồng bằng cách thay đổi kiểu dữ liệu của các tham số của phương thức. Ví dụ:
void add(int a, int b)
int add(float a, int b)
void add(int a, float c)
void add(int a, double b)
Lưu ý rằng phương thức add() được đánh số 6 trên ví dụ không được phép sử dụng vì khi so sánh kiểu dữ liệu, thứ tự sắp xếp tham số, kiểu dữ liệu trả ra của phương thức thì đều tương tự như phương thức được đánh số 5. Cả hai phương thức trước tiên đều chấp nhận một đối số nguyên và sau đó, một đối số float làm tham số. Tuy nhiên, tên tham số là khác nhau. Điều này là không đủ để làm cho một phương thức bị nạp chồng. Các phương thức phải khác nhau về kiểu dữ liệu và số lượng đối số chứ không chỉ đơn giản là tên của các đối số.
Tương tự, phương thức add() được đánh số 7 có chữ ký tương tự như phương thức được đánh số 1. Cả hai phương thức đều chấp nhận hai số nguyên a và b làm tham số. Tuy nhiên, kiểu trả về của phương thức được đánh số 7 là int trong khi kiểu trả về của phương thức được đánh số 1 là void. Một lần nữa, điều này không làm cho phương thức bị nạp chồng vì nó không khác nhau về kiểu dữ liệu và số lượng đối số.
Vì vậy, các phương thức add() được đánh số 6 và 7 không phải là các phương thức nạp chồng và sẽ dẫn đến lỗi biên dịch. Chỉ thay đổi tên của tham số hoặc kiểu trả về của phương thức không làm cho nó bị nạp chồng. Ngoài ra, một phương thức không thể được coi là nạp chồng nếu chỉ có các bổ từ truy cập khác nhau.
package session;
public class MathClass {
// Method to add two integers
// @param num1 an integer variable storing the value of the first number
// @param num2 an integer variable storing the value of the second number
// @return void
public void add(int num1, int num2) {
System.out.println("Result after addition is " + (num1 + num2));
}
// Overloaded method to add three integers
// @param num1 an integer variable storing the value of the first number
// @param num2 an integer variable storing the value of the second number
// @param num3 an integer variable storing the value of the third number
// @return void
public void add(int num1, int num2, int num3) {
System.out.println("Result after addition is " + (num1 + num2 + num3));
}
// Overloaded method to add a float and an integer
// @param num1 a float variable storing the value of the first number
// @param num2 an integer variable storing the value of the second number
// @return void
public void add(float num1, int num2) {
System.out.println("Result after addition is " + (num1 + num2));
}
// Overloaded method to add two floating-point numbers
// @param num1 a float variable storing the value of the first number
// @param num2 a float variable storing the value of the second number
// @return void
public void add(float num1, float num2) {
System.out.println("Result after addition is " + (num1 + num2));
}
// Main method
public static void main(String[] args) {
// Instantiate the MathClass class
MathClass objMath = new MathClass();
// Invoke the overloaded methods with relevant arguments
objMath.add(3.4F, 2);
objMath.add(4, 5);
objMath.add(6, 7, 8);
}
}
Output:
Ví dụ cho thấy một lớp có tên MathClass bao gồm các phương thức add() được nạp chồng. Phương thức main() tạo một đối tượng của MathClass và gọi số lượng đối số của phương thức add(). Trình biên dịch thực thi phương thức add() thích hợp dựa trên loại và số lượng đối số được người dùng truyền vào. Đầu ra hiển thị kết quả của phép cộng các giá trị khác nhau.
Nạp chồng hàm khởi tạo (Constructor Overloading)
Hàm khởi tạo (constructor) một phương thức đặc biệt của một lớp có cùng tên với tên lớp. Hàm khởi tạo được sử dụng để khởi tạo các biến của một lớp. Tương tự như một phương thức, một hàm khởi tạo cũng có thể được nạp chồng để khởi tạo các loại và số lượng tham số khác nhau. Khi lớp được khởi tạo, trình biên dịch sẽ gọi hàm khởi tạo dựa trên số lượng, kiểu và trình tự các đối số được truyền cho nó.
Ví dụ
public class Student {
private String name;
private int age;
// Default constructor (no parameters)
public Student() {
name = "Unknown";
age = 0;
}
// Constructor with name parameter
public Student(String name) {
this.name = name;
age = 0; // Default age
}
// Constructor with name and age parameters
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// Getter methods
public String getName() {
return name;
}
public int getAge() {
return age;
}
public static void main(String[] args) {
// Create Student objects using different constructors
Student student1 = new Student(); // Default constructor
Student student2 = new Student("Alice"); // Constructor with name parameter
Student student3 = new Student("Bob", 20); // Constructor with name and age parameters
// Display student information
System.out.println("Student 1: Name - " + student1.getName() + ", Age - " + student1.getAge());
System.out.println("Student 2: Name - " + student2.getName() + ", Age - " + student2.getAge());
System.out.println("Student 3: Name - " + student3.getName() + ", Age - " + student3.getAge());
}
}
Trong ví dụ này:
- Lớp Student có ba hàm khởi tạo:
- Hàm khởi tạo mặc định (public Student()) khởi tạo name thành “Chưa xác định” và age thành 0.
- Hàm khởi tạo thứ hai (public Student(String name)) nhận tham số name và khởi tạo name với giá trị được cung cấp và đặt age thành giá trị mặc định (0).
- Hàm khởi tạo thứ ba (public Student(String name, int age)) nhận cả hai tham số name và age và khởi tạo cả hai biến thể.
- Trong phương thức main, chúng ta tạo ba đối tượng Student bằng các hàm khởi tạo khác nhau, minh họa việc nạp chồng hàm khởi tạo.
Constructor overloading cho phép bạn tạo các đối tượng với các tập hợp giá trị khởi đầu khác nhau dựa trên các tham số bạn cung cấp khi tạo một thể hiện của lớp.
Sử dụng từ khóa this
Java cung cấp từ khóa this có thể được sử dụng trong một phương thức của lớp hoặc một hàm khởi tạo để tham chiếu đến đối tượng hiện tại, nghĩa là đối tượng có phương thức hoặc hàm khởi tạo đang được gọi. Bất kỳ thành viên nào của đối tượng hiện tại đều có thể được tham chiếu từ bên trong một phương thức phương thức hoặc một hàm khởi tạo bằng cách sử dụng từ khóa ‘this’. Từ khóa this không được sử dụng rõ ràng trong các phương thức khi đề cập đến các biến và phương thức của một lớp.
Ví dụ:
public class Person {
// Instance variables
private String name;
private int age;
// Constructor
public Person(String name, int age) {
// Using "this" to refer to instance variables
this.name = name;
this.age = age;
}
// Method to display person's information
public void displayInfo() {
// Using "this" to access instance variables and methods
System.out.println("Name: " + this.name);
System.out.println("Age: " + this.age);
}
// Method to compare two Person objects
public boolean isSamePerson(Person otherPerson) {
// Using "this" to access instance variables and compare with another object's variables
if (this.name.equals(otherPerson.name) && this.age == otherPerson.age) {
return true;
}
return false;
}
public static void main(String[] args) {
// Create two Person objects
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Bob", 30);
// Display information for person1
System.out.println("Person 1's Information:");
person1.displayInfo();
// Compare person1 with person2
if (person1.isSamePerson(person2)) {
System.out.println("Person 1 and Person 2 are the same.");
} else {
System.out.println("Person 1 and Person 2 are different.");
}
}
}
Trong ví dụ này:
- Chúng ta có một lớp Person với hai biến thành viên (name và age) và một số phương thức:
- Constructor (public Person(String name, int age)) sử dụng “this” để gán giá trị của tham số cho biến thành viên.
- Phương thức displayInfo() sử dụng “this” để truy cập và hiển thị thông tin của thể hiện hiện tại.
- Phương thức isSamePerson(Person otherPerson) sử dụng “this” để so sánh thông tin của thể hiện hiện tại với thông tin của đối tượng khác.
- Trong phương thức main, chúng ta tạo hai đối tượng Person và sử dụng từ khóa “this” để truy cập và hiển thị thông tin của đối tượng và so sánh hai đối tượng.
Từ khóa “this” trong Java giúp xác định rõ ràng thể hiện hiện tại của lớp và truy cập các thành phần của nó một cách dễ dàng.
Hướng dẫn
1. **Lớp và Đối Tượng Người (Person)**:
- Tạo một lớp `Person` có các trường như `name`, `age`, và `gender`.
- Định nghĩa phương thức `getInfo()` để in ra thông tin của một đối tượng `Person`.
- Tạo và sử dụng một số đối tượng `Person` khác nhau và gọi phương thức `getInfo()` để in thông tin của họ.
2. **Đối Tượng Sinh Viên (Student)**:
- Tạo một lớp `Student` với các trường như `name`, `age`, `studentId`, và `grade`. Lớp phải có thể khởi tạo theo 4 cách: 0 tham số, 2 tham số sutudentId và name , 3 tham số sutudentId và name, age và 4 tham số (tất cả thuộc tính)
- Viết phương thức tính điểm trung bình (`calculateGPA()`) dựa trên các điểm số đã cho (có thể sử dụng mảng).
3. **Ngân Hàng Đơn Giản (Simple Bank)**:
- Tạo một lớp `BankAccount` để biểu diễn tài khoản ngân hàng với trường `balance`.
- Thêm phương thức `deposit()` và `withdraw()` để thay đổi số dư tài khoản.
4. **Lớp Đối Tượng Máy Tính (Computer)**:
- Tạo một lớp `Computer` với các trường như `brand`, `model`, và `price`.
- Định nghĩa phương thức `displayInfo()` để hiển thị thông tin máy tính.
5. **Lớp Đối Tượng Hình Tròn (Circle)**:
- Tạo một lớp `Circle` với trường `radius`.
- Viết phương thức tính diện tích (`calculateArea()`) và chu vi (`calculatePerimeter()`) của hình tròn.
6. **Lớp Đối Tượng Động Vật (Animal)**:
- Tạo một lớp `Animal` với trường `name` và `sound`.
- Định nghĩa phương thức `makeSound()` để in ra tiếng của động vật.
7. **Lớp Đối Tượng Sản Phẩm (Product)**:
- Tạo một lớp `Product` để biểu diễn thông tin về sản phẩm với các trường như `name`, `price` Integer, và `quantity` - Float.
- Thêm phương thức tính tổng giá trị (`calculateTotalPrice()`) dựa trên giá và số lượng sản phẩm.
8. **Lớp Đối Tượng Xe Hơi (Car)**:
- Tạo một lớp `Car` với các trường như `brand`, `model`, `year`, và `price`.
- Viết phương thức `calculateDepreciation()` để tính sự giảm giá của xe theo thời gian. Cho công thức gỉam giá là year = 1 tương đương price giảm 5%
9. **Lớp Đối Tượng Nhân Viên (Employee)**:
- Tạo một lớp `Employee` với các trường như `name`, `employeeId`, `position`, và `salary`.
- Viết phương thức `calculateAnnualSalary()` để tính lương hàng năm dựa trên lương hàng tháng (field salary).
10. Khởi tạo dự án với package kèm file Main.java, trong file main có hàm main() để chạy ứng dụng, Trong ứng dụng vẽ ra các menu như sau:
Vui long chon tinh nang:
1. Tao doi tuong Person va hien thi thong tin
2. Tao doi tuong 4 Sinhvien va hien thi diem trung binh tung sinh vien (4 construct khac nhau)
3. Tao tai khoan ngan hang va nap rut
4. Tao doi tuong may tinh va hien thi thong tin
5. Tao hinh tron va hien thi tinh chu vi dien tich
6. Tao doi tuong dong vat va in ra tieng keo
7. Tao doi tuong xe hoi va in ra giam gia
8. Tao doi tuong nhan vien va in ra luong hang nam
Với mỗi tính năng sẽ đều y/c người dùng nhập lệu và đối tượng được tạo dụa trê n dữ liệu đầu vào của người dùng
11 (nâng cao) **Quản Lý Danh Sách (List Management)**:
- Tạo một lớp `Person` với các trường như `name`, `age`, và `gender`.
- Tạo một lớp `PersonList` để quản lý danh sách các đối tượng `Person`.
- Thêm phương thức để thêm, xóa và tìm kiếm đối tượng trong danh sách.
=> Nếu làm được bài này, bổ sung vào menu tính năng 9