├── .gitignore ├── .projects └── TicTacToe.java ├── 1. The Java Language ├── ArgumentPassing.java ├── Arrays.java ├── Boxing.java ├── DataTypes.java ├── Enumerations.java ├── HelloJava.java ├── HelloWorld.java ├── InitializerBlock.java ├── MethodOverloading.java ├── ObjectCreation.java ├── ObjectDestruction.java ├── ObjectsInJava.java ├── ThisReference.java └── Wrappers.java ├── 2. Classes in Java ├── Abstract.java ├── AdapterClass.java ├── Annotations.java ├── AnonymousInnerClass.java ├── Callback.java ├── Casting.java ├── Clone.java ├── Equals.java ├── EventHandling.java ├── Hashcode.java ├── InnerClass.java ├── Interface.java ├── InterfaceVariables.java ├── NestedClass.java ├── Reflection.java ├── StaticInnerClass.java ├── SubInterface.java ├── Subclass.java ├── SuperConstructor.java ├── TheClassClass.java ├── TheObjectClass.java └── WithinMethods.java ├── 3. Core Ultilities ├── .gitkeep ├── Collections.java ├── Iteration.java ├── LoggingAPI.java ├── Math.java ├── Properties.java ├── Random.java ├── StringTips.java └── Time.java ├── 4. IO Facilities └── .gitkeep ├── 5. Data Structures and Algorithms ├── MaxSubarray.java ├── SumOfArray.java └── SumTwoInts.java ├── 6. Network Programming └── .gitkeep ├── ArgumentPassing.md ├── README.md ├── documents ├── Effective Java.pdf ├── Learning Java ~ An Introduction to Real-World Programming with Java.pdf ├── Learning Java.pdf └── Object-oriented programming and Java.pdf └── images ├── 01.png ├── 02.png ├── 03.png ├── 04.png ├── 05.png └── logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .vscode/ 3 | .vscode/settings.json 4 | 5 | .bin/ -------------------------------------------------------------------------------- /.projects/TicTacToe.java: -------------------------------------------------------------------------------- 1 | import javax.swing.*; 2 | import java.awt.*; 3 | import java.awt.event.ActionEvent; 4 | import java.awt.event.ActionListener; 5 | import java.util.LinkedList; 6 | import java.util.Queue; 7 | 8 | public class TicTacToe extends JFrame implements ActionListener { 9 | private JButton[][] buttons = new JButton[3][3]; 10 | private JLabel statusLabel = new JLabel("X's turn", SwingConstants.CENTER); 11 | private JLabel scoreLabel = new JLabel("Player 1: 0 | Player 2: 0", SwingConstants.CENTER); 12 | private char currentPlayerMark = 'X'; 13 | private int player1Score = 0; 14 | private int player2Score = 0; 15 | private boolean isPlayer1X = true; // Track who is playing 'X' 16 | private Queue xMoves = new LinkedList<>(); 17 | private Queue oMoves = new LinkedList<>(); 18 | 19 | public TicTacToe() { 20 | super("Tic Tac Toe"); 21 | setSize(400, 400); 22 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 23 | setLayout(new BorderLayout()); 24 | 25 | JPanel boardPanel = new JPanel(new GridLayout(3, 3)); 26 | initializeButtons(boardPanel); 27 | add(boardPanel, BorderLayout.CENTER); 28 | 29 | JPanel statusPanel = new JPanel(new GridLayout(2, 1)); 30 | statusLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20)); 31 | scoreLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16)); 32 | statusPanel.add(statusLabel); 33 | statusPanel.add(scoreLabel); 34 | add(statusPanel, BorderLayout.NORTH); 35 | } 36 | 37 | private void initializeButtons(JPanel boardPanel) { 38 | for (int i = 0; i < 3; i++) { 39 | for (int j = 0; j < 3; j++) { 40 | buttons[i][j] = new JButton(); 41 | buttons[i][j].setFont(new Font(Font.SANS_SERIF, Font.BOLD, 100)); 42 | buttons[i][j].setFocusPainted(false); 43 | buttons[i][j].setBackground(Color.WHITE); // Set background color 44 | buttons[i][j].setOpaque(true); 45 | buttons[i][j].addActionListener(this); 46 | boardPanel.add(buttons[i][j]); 47 | } 48 | } 49 | } 50 | 51 | public void actionPerformed(ActionEvent e) { 52 | JButton buttonClicked = (JButton) e.getSource(); 53 | if (buttonClicked.getText().equals("")) { 54 | buttonClicked.setText(String.valueOf(currentPlayerMark)); 55 | buttonClicked.setForeground(currentPlayerMark == 'X' ? Color.RED : Color.BLUE); 56 | Point move = findPosition(buttonClicked); 57 | trackMove(currentPlayerMark, move); 58 | manageFourMarks(currentPlayerMark); 59 | 60 | if (checkForWin()) { 61 | JOptionPane.showMessageDialog(null, "Player " + currentPlayerMark + " wins!"); 62 | updateScore(currentPlayerMark); 63 | resetButtons(); 64 | switchPlayers(); 65 | } else if (isBoardFull()) { 66 | JOptionPane.showMessageDialog(null, "Draw!"); 67 | resetButtons(); 68 | switchPlayers(); 69 | } else { 70 | currentPlayerMark = (currentPlayerMark == 'X') ? 'O' : 'X'; 71 | statusLabel.setText(currentPlayerMark + "'s turn"); 72 | } 73 | } 74 | } 75 | 76 | private Point findPosition(JButton button) { 77 | for (int i = 0; i < 3; i++) { 78 | for (int j = 0; j < 3; j++) { 79 | if (buttons[i][j] == button) { 80 | return new Point(i, j); 81 | } 82 | } 83 | } 84 | return null; // Should never happen 85 | } 86 | 87 | private void trackMove(char player, Point move) { 88 | if (player == 'X') { 89 | xMoves.add(move); 90 | } else { 91 | oMoves.add(move); 92 | } 93 | } 94 | 95 | private void manageFourMarks(char player) { 96 | Queue moves = (player == 'X') ? xMoves : oMoves; 97 | if (moves.size() > 3) { 98 | Point oldMove = moves.poll(); 99 | buttons[oldMove.x][oldMove.y].setText(""); 100 | } 101 | } 102 | 103 | private boolean isBoardFull() { 104 | for (int i = 0; i < 3; i++) { 105 | for (int j = 0; j < 3; j++) { 106 | if (buttons[i][j].getText().equals("")) { 107 | return false; 108 | } 109 | } 110 | } 111 | return true; 112 | } 113 | 114 | private void switchPlayers() { 115 | isPlayer1X = !isPlayer1X; 116 | } 117 | 118 | private void updateScore(char winner) { 119 | if ((winner == 'X' && isPlayer1X) || (winner == 'O' && !isPlayer1X)) { 120 | player1Score++; 121 | } else { 122 | player2Score++; 123 | } 124 | scoreLabel.setText("Player 1: " + player1Score + " | Player 2: " + player2Score); 125 | } 126 | 127 | private boolean checkForWin() { 128 | return checkRowsForWin() || checkColumnsForWin() || checkDiagonalsForWin(); 129 | } 130 | 131 | private boolean check(JButton b1, JButton b2, JButton b3) { 132 | return !b1.getText().equals("") && b1.getText().equals(b2.getText()) && b2.getText().equals(b3.getText()); 133 | } 134 | 135 | private boolean checkRowsForWin() { 136 | for (int i = 0; i < 3; i++) { 137 | if (check(buttons[i][0], buttons[i][1], buttons[i][2])) { 138 | return true; 139 | } 140 | } 141 | return false; 142 | } 143 | 144 | private boolean checkColumnsForWin() { 145 | for (int i = 0; i < 3; i++) { 146 | if (check(buttons[0][i], buttons[1][i], buttons[2][i])) { 147 | return true; 148 | } 149 | } 150 | return false; 151 | } 152 | 153 | private boolean checkDiagonalsForWin() { 154 | return check(buttons[0][0], buttons[1][1], buttons[2][2]) || check(buttons[0][2], buttons[1][1], buttons[2][0]); 155 | } 156 | 157 | private void resetButtons() { 158 | for (int i = 0; i < 3; i++) { 159 | for (int j = 0; j < 3; j++) { 160 | buttons[i][j].setText(""); 161 | } 162 | } 163 | currentPlayerMark = 'X'; // 'X' luôn là người chơi đầu tiên 164 | statusLabel.setText(currentPlayerMark + "'s turn"); 165 | xMoves.clear(); 166 | oMoves.clear(); 167 | } 168 | 169 | public static void main(String[] args) { 170 | TicTacToe tictactoe = new TicTacToe(); 171 | tictactoe.setVisible(true); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /1. The Java Language/ArgumentPassing.java: -------------------------------------------------------------------------------- 1 | // Khái niệm truyền tham trị và truyền tham chiếu có thể gây nhầm lẫn. 2 | // Nhưng quan trọng là hiểu rằng, Java chỉ sử dụng truyền tham trị. 3 | /* 4 | * - Truyền tham trị (pass by value): Trong Java, khi bạn truyền một biến vào một phương thức, 5 | * giá trị của biến đó được sao chép vào một biến mới trong phương thức được gọi. Điều này có 6 | * nghĩa là bất kỳ sự thay đổi nào đối với biến trong phương thức đó sẽ không ảnh hưởng đến biến ban đầu. 7 | * - Truyền tham chiếu (pass by reference): Đây là một khái niệm mà nhiều ngôn ngữ khác sử dụng, trong đó 8 | * tham chiếu đến một đối tượng hoặc vùng nhớ được truyền vào phương thức, cho phép phương thức thay đổi 9 | * trực tiếp đối tượng hoặc giá trị tại vùng nhớ đó. 10 | */ 11 | 12 | // Cách hoạt động của Java: 13 | /* 14 | * - Với kiểu dữ liệu nguyên thủy: Các kiểu như int, double, char, v.v., khi được truyền vào một phương thức, 15 | * giá trị của chúng được sao chép. Bất kỳ sự thay đổi nào trong phương thức đối với biến này không ảnh hưởng đến biến ban đầu. 16 | * - Với đối tượng: Khi truyền một đối tượng vào phương thức, địa chỉ (tham chiếu) của đối tượng đó được sao chép vào biến mới. 17 | * Tuy nhiên, đây là một bản sao của tham chiếu, không phải của chính đối tượng. Do đó, bạn có thể thay đổi đối tượng qua tham 18 | * chiếu này (ví dụ, thay đổi thuộc tính, gọi phương thức thay đổi trạng thái), và các thay đổi đó sẽ phản ánh trên đối tượng ban đầu. Tuy nhiên, nếu bạn cố gắng gán lại tham chiếu này (ví dụ, gán nó vào một đối tượng mới), tham chiếu ban đầu không thay đổi. 19 | */ 20 | 21 | public class ArgumentPassing { 22 | int x; 23 | 24 | // Truyền tham trị (pass by value) 25 | static void changeValue(int x) { 26 | x = 10; 27 | } 28 | 29 | // Truyền một đối tượng vào phương thức 30 | static void changeValue(ArgumentPassing obj) { 31 | obj.x = 10; 32 | } 33 | 34 | void changeValue() { 35 | this.x = 20; 36 | } 37 | 38 | public static void main(String[] args) { 39 | ArgumentPassing obj = new ArgumentPassing(); 40 | obj.x = 5; 41 | 42 | // Truyền biến x vào phương thức changeValue (truyền tham trị) 43 | changeValue(obj.x); 44 | System.out.println(obj.x); // -> 5 45 | // Biến x (thuộc tính x của đối tượng obj) không thay đổi giá trị sau khi truyền 46 | // bởi vì giá trị của biến x được sao chép vào tham số x của phương thức 47 | // changeValue. 48 | 49 | // Truyền đối tượng obj vào phương thức changeValue 50 | changeValue(obj); 51 | System.out.println(obj.x); // -> 10 52 | // Đối tượng obj bị thay đổi giá trị thuộc tính x sau khi truyền vào phương thức 53 | // changeValue. 54 | 55 | obj.changeValue(); 56 | System.out.println(obj.x); // -> 20 57 | } 58 | } 59 | 60 | // Đọc thêm 61 | /* 62 | * 1. Kiểu nguyên thủy 63 | * Kiểu nguyên thủy bao gồm int, double, float, boolean, char, byte, short, và 64 | * long. Các kiểu này lưu trữ dữ liệu trực tiếp mà không lưu trữ tham chiếu đến 65 | * đối tượng. 66 | * 67 | * -> Khi bạn truyền một biến kiểu nguyên thủy vào hàm, Java sẽ 68 | * tạo một bản sao của giá trị đó và lưu trữ trong stack của thread hiện hành. 69 | * Stack là một vùng nhớ được dùng để lưu trữ các thông tin cục bộ của mỗi 70 | * phương thức như tham số và biến cục bộ. Việc này đảm bảo rằng thay đổi giá 71 | * trị trong hàm không ảnh hưởng đến biến ban đầu. 72 | * 73 | * 2. Kiểu đối tượng 74 | * Kiểu đối tượng bao gồm các lớp và mảng. Thay vì lưu giá trị trực tiếp, biến 75 | * của kiểu đối tượng lưu trữ tham chiếu đến một đối tượng trong heap. 76 | * 77 | * -> Đối tượng thực sự được lưu trong vùng nhớ heap. Heap là 78 | * một vùng nhớ được quản lý bởi bộ thu gom rác của Java, dùng để lưu trữ dữ 79 | * liệu động tạo ra trong quá trình chạy chương trình. 80 | * -> Khi bạn truyền một đối tượng vào hàm, thực chất là 81 | * bạn truyền bản sao của tham chiếu đến đối tượng đó. Tham chiếu này được lưu 82 | * trên stack, nhưng nó trỏ đến cùng một đối tượng trên heap. Do đó, bất kỳ thay 83 | * đổi nào đối với thuộc tính của đối tượng qua tham chiếu này sẽ thay đổi trực 84 | * tiếp đối tượng trên heap. 85 | */ -------------------------------------------------------------------------------- /1. The Java Language/Arrays.java: -------------------------------------------------------------------------------- 1 | @SuppressWarnings("unused") 2 | public class Arrays { 3 | public static int sumOfArray(int[] arr) { 4 | int sum = 0; 5 | for (int i = 0; i < arr.length; i++) { 6 | sum += arr[i]; 7 | } 8 | return sum; 9 | } 10 | 11 | public static void main(String[] args) { 12 | // Mảng là một kiểu dữ liệu dùng để lưu trữ nhiều giá trị cùng kiểu dữ liệu. 13 | // Mảng trong Java có độ dài cố định và không thể thay đổi sau khi đã khởi tạo. 14 | 15 | // Khai báo (declaration) mảng các số nguyên 16 | int[] arrayOfInts; // Cách 1 17 | float arrayOfFloats[]; // Cách 2 (giống C) 18 | 19 | // Khởi tạo (initialization) mảng 20 | /* 21 | * Toán tử new được sử dụng để tạo một mảng mới. 22 | * Cú pháp: new kiểu_dữ_liệu[số_phần_tử] 23 | */ 24 | arrayOfInts = new int[5]; // Mảng arrayOfInts có 5 phần tử kiểu int 25 | 26 | // Kích thước của mảng (số phần tử) được lưu trong thuộc tính length 27 | System.out.println(arrayOfInts.length); // In ra 5 28 | 29 | // Phương thức sao chép mảng 30 | int[] sourceArray = { 1, 2, 3, 4, 5 }; 31 | int[] destArray = new int[5]; 32 | 33 | System.arraycopy(sourceArray, 0, destArray, 0, 5); 34 | 35 | for (int i = 0; i < destArray.length; i++) { 36 | System.out.println(destArray[i]); 37 | } 38 | 39 | // Mảng ẩn danh (anonymous array) là một mảng không cần tên 40 | // Ví dụ sử dụng phương thức sumOfArray để tính tổng mảng ẩn danh 41 | int sum = sumOfArray(new int[] { 1, 2, 3, 4, 5 }); 42 | System.out.println(sum); // In ra 15 43 | 44 | // Mảng đa chiều (multidimensional array) 45 | int[][] matrix = new int[2][3]; // Mảng 2 chiều 2x3 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /1. The Java Language/Boxing.java: -------------------------------------------------------------------------------- 1 | import java.lang.Double; 2 | 3 | public class Boxing { 4 | // Tự đóng gói (autoboxing) và mở gói (unboxing) trong Java: 5 | /* 6 | * - Tự đóng gói (autoboxing): là quá trình chuyển đổi một kiểu dữ liệu nguyên 7 | * thủy (primitive) sang một đối tượng của lớp Wrapper tương ứng. 8 | * - Mở gói (unboxing): là quá trình chuyển đổi một đối tượng của lớp Wrapper 9 | * sang một kiểu dữ liệu nguyên thủy tương ứng. 10 | */ 11 | 12 | public static Double multiply(Double a, Double b) { 13 | return a.doubleValue() * b.doubleValue(); 14 | } 15 | 16 | public static void main(String[] args) { 17 | // Tự đóng gói (autoboxing) 18 | // Cú pháp khởi tạo một đối tượng Wrapper: 19 | // obj = 20 | Integer i = 10; 21 | 22 | // Mở gói (unboxing) 23 | // Chuyển đối tượng Integer sang kiểu dữ liệu nguyên thủy int 24 | int value = i; 25 | 26 | System.out.println("Value: " + value); // -> 10 27 | 28 | double d = multiply(5.0, 5.0); 29 | System.out.println("5.0 x 5.0 = " + d); // -> 25.0 30 | // Trường hợp sử dụng hàm multiply này xảy ra 3 trường hợp boxing: 31 | /* 32 | * - Đầu tiên: hai giá trị nguyên thủy kiểu double 5.0 được đóng gói thành hai 33 | * đối tượng Double. 34 | * - Tiếp theo: kết quả của phép nhân 5.0 * 5.0 là 25.0 là một giá trị nguyên 35 | * thủy kiểu double, nó được đóng gói thành một đối tượng Double. 36 | * - Cuối cùng: giá trị trả này được mở gói thành một giá trị nguyên thủy kiểu 37 | * double để gán cho d. 38 | */ 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /1. The Java Language/DataTypes.java: -------------------------------------------------------------------------------- 1 | @SuppressWarnings("unused") 2 | public class DataTypes { 3 | /* 4 | * Các kiểu dữ liệu trong Java chia thành 2 loại: 5 | * 1. Kiểu dữ liệu nguyên thủy (Primitive Data Types) 6 | * - Kiểu số nguyên: byte, short, int, long 7 | * - Kiểu số thực: float, double 8 | * - Kiểu ký tự: char 9 | * - Kiểu boolean: boolean 10 | * 2. Kiểu dữ liệu tham chiếu (Reference Data Types) 11 | * - Trong ngôn ngữ hướng đối tượng, chúng ta có thể tạo ra các kiểu dữ liệu 12 | * mới, phức tạp hơn từ các kiểu dữ liệu cơ bản bằng cách sử dụng class. 13 | * - Mỗi class được coi như một kiểu dữ liệu mới. 14 | */ 15 | 16 | public char grade = 'A'; // Kiểu ký tự 17 | private String name = "Java"; // Kiểu chuỗi 18 | 19 | public static void main(String[] args) { 20 | // Khai báo biến kiểu số nguyên 21 | byte b = 100; 22 | short s = 1000; 23 | int i = 100000; 24 | 25 | // Hằng số kiểu long phải kết thúc bằng "L" hoặc "l" 26 | long l = 1000000000L; 27 | 28 | // Hằng số thực kiểu float phải kết thúc bằng "F" hoặc "f" 29 | float f = 10.5F; 30 | 31 | // Một giá trị số thực không có hậu tố sẽ mặc định là kiểu double 32 | double d = 10.5; 33 | 34 | // Chuyển đổi kiểu dữ liệu (casting) 35 | /* 36 | * Java là ngôn ngữ định kiểu chặt. 37 | * JVM có thể tự động chuyển đổi kiểu dữ liệu nhỏ hơn sang kiểu dữ liệu lớn hơn. 38 | * Để chuyển đổi ngược lại, chúng ta cần sử dụng phép ép kiểu tường minh 39 | * (explicit casting). 40 | */ 41 | // Ví dụ: 42 | int x = 100; 43 | long y = x; // tự động chuyển đổi kiểu dữ liệu 44 | int z = (int) y; // ép kiểu tường minh, nếu viết int z = y; sẽ báo lỗi 45 | 46 | // Chuyển đổi kiểu dữ liệu được xảy ra tự động nếu không có mất mát dữ liệu 47 | // byte -> short -> int -> long -> float -> double 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /1. The Java Language/Enumerations.java: -------------------------------------------------------------------------------- 1 | public class Enumerations { 2 | // Enumerations (Enums) trong Java: 3 | /* 4 | * - Là một kiểu dữ liệu đặc biệt trong Java, được sử dụng để định nghĩa một tập 5 | * hợp các hằng số. 6 | * - Enumerations giúp tạo ra một tập hợp các hằng số có ý nghĩa, giúp mã nguồn 7 | * dễ đọc hơn. 8 | * - Enumerations được khai báo bằng từ khóa enum. 9 | */ 10 | 11 | enum Weekdays { 12 | MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY 13 | } 14 | 15 | // enum là kiểu public static final, nên có thể truy cập từ bất kỳ lớp nào 16 | 17 | // Có thể lấy danh sách theo thứ tự cho một biến thông qua phương thức values() 18 | Weekdays[] days = Weekdays.values(); 19 | 20 | // Các enumerations có thể có giá trị với các constructor, phương thức, trường 21 | // giống như các lớp khác. 22 | 23 | enum Directions { 24 | EAST(1), WEST(2), NORTH(3), SOUTH(4); 25 | 26 | private int value; 27 | 28 | Directions(int value) { 29 | this.value = value; 30 | } 31 | 32 | public int getValue() { 33 | return value; 34 | } 35 | } 36 | 37 | public static void main(String[] args) { 38 | // Truy cập các hằng số trong enum 39 | System.out.println(Weekdays.MONDAY); // -> MONDAY 40 | 41 | // Lấy danh sách các hằng số trong enum 42 | Enumerations obj = new Enumerations(); 43 | for (Weekdays day : obj.days) { 44 | System.out.println(day); 45 | } 46 | 47 | // Truy cập giá trị của enum 48 | System.out.println(Directions.EAST.getValue()); // -> 1 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /1. The Java Language/HelloJava.java: -------------------------------------------------------------------------------- 1 | import javax.swing.*; 2 | 3 | public class HelloJava { 4 | public static void main(String[] args) { 5 | // Tạo một cửa sổ (window) mới 6 | JFrame frame = new JFrame("Hello Java"); 7 | 8 | // Tạo một nhãn (label) mới với nội dung là "Hello World" 9 | JLabel label = new JLabel("Hello Java", JLabel.CENTER); 10 | 11 | // Thêm nhãn vào cửa sổ 12 | frame.add(label); 13 | 14 | // Đặt kích thước cửa sổ 15 | frame.setSize(300, 200); 16 | 17 | // Hiển thị cửa sổ 18 | frame.setVisible(true); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /1. The Java Language/HelloWorld.java: -------------------------------------------------------------------------------- 1 | public class HelloWorld { 2 | public static void main(String[] args) { 3 | System.out.println("Hello World"); // Chương trình in ra màn hình chuỗi "Hello World" 4 | } 5 | } 6 | 7 | // Trong lớp HelloWorld, chúng ta có một phương thức (method) main(). 8 | // Phương thức này in ra màn hình chuỗi "Hello World". 9 | // Đây là một chương trình dòng lệnh (command-line program), tức là nó chạy trên 10 | // shell hoặc cửa sổ DOS và in output ra đó. -------------------------------------------------------------------------------- /1. The Java Language/InitializerBlock.java: -------------------------------------------------------------------------------- 1 | public class InitializerBlock { 2 | // Có thể khai báo một khối khởi tạo (initializer block) trong một lớp. 3 | /* 4 | * - Khối khởi tạo được sử dụng để khởi tạo dữ liệu của đối tượng. 5 | * - Khối khởi tạo được chạy trước khi hàm tạo của lớp được chạy. 6 | * - Một lớp có thể có nhiều khối khởi tạo, nhưng chúng sẽ được thực thi theo 7 | * thứ tự xuất hiện trong lớp. 8 | * - Khối này không thuộc về bất kỳ phương thức nào và được đặt trong dấu ngoặc 9 | * nhọn. 10 | * - Nếu sử dụng từ khóa static trước khối khởi tạo, nó sẽ trở thành khối khởi 11 | * tạo static, dùng để khởi tạo biến static. 12 | */ 13 | 14 | // Ví dụ về khối khởi tạo 15 | int x; 16 | static int y; 17 | 18 | // Khối khởi tạo 19 | { 20 | x = 5; 21 | y = 5; 22 | } 23 | 24 | // Khối khởi tạo static 25 | // Khối static được thực thi trước bất kỳ khối non-static nào và bất kỳ hàm tạo 26 | // nào. 27 | static { 28 | y = 10; 29 | } 30 | 31 | public static void main(String[] args) { 32 | InitializerBlock obj = new InitializerBlock(); 33 | System.out.println("Value of x: " + obj.x); // -> 5 34 | System.out.println("Value of y: " + y); // -> 5 (khối static được thực thi trước khối non-static) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /1. The Java Language/MethodOverloading.java: -------------------------------------------------------------------------------- 1 | public class MethodOverloading { 2 | // Nạp chồng phương thức (Method Overloading) trong Java: 3 | /* 4 | * - Nạp chồng phương thức (Method Overloading) là cách tạo nhiều phương thức 5 | * cùng tên trong một lớp nhưng có số lượng tham số hoặc kiểu dữ liệu tham số 6 | * khác nhau. 7 | * - Khi gọi một phương thức, Java sẽ xác định phương thức cần gọi dựa trên 8 | * số lượng tham số hoặc kiểu dữ liệu của tham số. 9 | * - Nạp chồng phương thức giúp tăng cường tính linh hoạt cho lớp và giúp người 10 | * lập trình dễ dàng sử dụng phương thức mà không cần nhớ tên phương thức. 11 | * - Khi nạp chồng phương thức, các phương thức cùng tên phải khác nhau về 12 | * số lượng tham số hoặc kiểu dữ liệu của tham số. 13 | * - Các phương thức cùng tên có thể khác nhau về kiểu trả về. 14 | */ 15 | 16 | // Phương thức sum với 2 tham số kiểu int 17 | static int sum(int a, int b) { 18 | return a + b; 19 | } 20 | 21 | // Phương thức sum với 3 tham số kiểu int 22 | static int sum(int a, int b, int c) { 23 | return a + b + c; 24 | } 25 | 26 | // Phương thức sum với 2 tham số kiểu double 27 | static double sum(double a, double b) { 28 | return a + b; 29 | } 30 | 31 | public static void main(String[] args) { 32 | // Gọi phương thức sum với 2 tham số kiểu int 33 | System.out.println("Sum of 10 and 20: " + sum(10, 20)); // -> 30 34 | 35 | // Gọi phương thức sum với 3 tham số kiểu int 36 | System.out.println("Sum of 10, 20 and 30: " + sum(10, 20, 30)); // -> 60 37 | 38 | // Gọi phương thức sum với 2 tham số kiểu double 39 | System.out.println("Sum of 10.5 and 20.5: " + sum(10.5, 20.5)); // -> 31.0 40 | } 41 | } 42 | 43 | // Nạp chồng phương thức khác với ghi đè phương thức (Method Overriding) trong 44 | // Java: 45 | /* 46 | * - Nạp chồng phương thức (Method Overloading) là cách tạo nhiều phương thức 47 | * cùng tên trong một lớp nhưng có số lượng tham số hoặc kiểu dữ liệu tham số 48 | * khác nhau. 49 | * - Ghi đè phương thức (Method Overriding) là cách tạo một phương thức trong 50 | * lớp con có cùng tên, cùng tham số và cùng kiểu trả về với phương thức trong 51 | * lớp cha. 52 | */ 53 | -------------------------------------------------------------------------------- /1. The Java Language/ObjectCreation.java: -------------------------------------------------------------------------------- 1 | // Các đối tượng trong Java được lưu trữ trong vùng nhớ heap. 2 | /* 3 | * - Không như các ngôn ngữ khác, chúng ta không phải quản lý bộ nhớ thủ công. 4 | * - Java cấp phát bộ nhớ tự động và giải phóng bộ nhớ khi không cần thiết. 5 | */ 6 | 7 | public class ObjectCreation { 8 | // Hàm tạo (Constructor) trong Java: 9 | /* 10 | * - Hàm tạo (Constructor) là một phương thức đặc biệt trong Java, được sử dụng 11 | * để khởi tạo đối tượng của lớp. 12 | * - Hàm tạo có tên giống với tên lớp và không có kiểu trả về. 13 | * - Khi một đối tượng được khởi tạo, hàm tạo sẽ được gọi tự động. 14 | * - Nếu không có hàm tạo nào được định nghĩa trong lớp, Java sẽ tạo một hàm 15 | * tạo mặc định (default constructor) không có tham số và không thực hiện 16 | * công việc gì. 17 | * - Một lớp có thể có nhiều hàm tạo, nhưng phải khác nhau về số lượng tham số 18 | * hoặc kiểu dữ liệu của tham số. 19 | * - Hàm tạo có thể gọi hàm tạo khác của cùng lớp bằng từ khóa this. 20 | */ 21 | 22 | int x; 23 | 24 | // Hàm tạo không tham số 25 | ObjectCreation() { 26 | x = 5; 27 | } 28 | 29 | // Hàm tạo có tham số 30 | ObjectCreation(int y) { 31 | x = y; 32 | } 33 | 34 | public static void main(String[] args) { 35 | // Khởi tạo đối tượng obj1 với hàm tạo không tham số 36 | ObjectCreation obj1 = new ObjectCreation(); 37 | System.out.println("Value of x in obj1: " + obj1.x); // -> 5 38 | 39 | // Khởi tạo đối tượng obj2 với hàm tạo có tham số 40 | ObjectCreation obj2 = new ObjectCreation(10); 41 | System.out.println("Value of x in obj2: " + obj2.x); // -> 10 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /1. The Java Language/ObjectDestruction.java: -------------------------------------------------------------------------------- 1 | @SuppressWarnings("unused") 2 | public class ObjectDestruction { 3 | // Hủy đối tượng (Object Destruction) trong Java: 4 | /* 5 | * - Trong Java, việc hủy đối tượng được thực hiện tự động bởi garbage 6 | * collector. 7 | * - Garbage collector là một tiến trình chạy ngầm, quét các đối tượng không còn 8 | * sử dụng và giải phóng bộ nhớ của chúng. 9 | * - System.gc() là một phương thức được sử dụng để yêu cầu garbage collector 10 | * chạy. 11 | */ 12 | 13 | // Trước khi một đối tượng bị hủy, phương thức finalize() sẽ được gọi. 14 | /* 15 | * - Phương thức finalize() là một phương thức của lớp Object, được gọi trước 16 | * khi một đối tượng bị hủy. 17 | * - Phương thức finalize() được sử dụng để thực hiện các tác vụ dọn dẹp hoặc 18 | * giải phóng tài nguyên trước khi đối tượng bị hủy. 19 | * - Phương thức finalize() có thể được ghi đè trong lớp con để thực hiện các 20 | * tác vụ dọn dẹp cụ thể. 21 | */ 22 | 23 | public static void main(String[] args) { 24 | ObjectDestruction obj = new ObjectDestruction(); 25 | obj = null; 26 | System.gc(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1. The Java Language/ObjectsInJava.java: -------------------------------------------------------------------------------- 1 | // Lớp (class) là các khối xây dựng của một ứng dụng Java. 2 | /* 3 | * Một lớp có thể chứa: các phương thức (methods), các biến (variables), mã khởi tạo (initialization code) và các lớp khác. 4 | * Từ khóa public và private xác định phạm vi truy cập của lớp: 5 | * - public: lớp có thể được truy cập từ bên ngoài package. 6 | * - private: lớp chỉ có thể được truy cập từ bên trong lớp đó. 7 | */ 8 | 9 | @SuppressWarnings("static-access") 10 | public class ObjectsInJava { 11 | int var; 12 | double number; 13 | static int staticVar = 10; 14 | final int finalVar = 100; 15 | 16 | int getVar() { 17 | return var; 18 | } 19 | 20 | static int print10() { 21 | return 10; 22 | } 23 | 24 | // Từ khóa public và private xác định phạm vi truy cập của biến và phương thức: 25 | // - public: biến hoặc phương thức có thể được truy cập từ bên ngoài package. 26 | // - private: biến hoặc phương thức chỉ có thể được truy cập từ bên trong lớp 27 | // đó. 28 | // - protected: biến hoặc phương thức chỉ có thể được truy cập từ bên trong 29 | // package hoặc từ lớp con. 30 | 31 | public static void main(String[] args) { 32 | // Khi định nghĩa một lớp, ta có thể tạo ra đối tượng (object) từ lớp đó. 33 | // Đối tượng là một thể hiện (instance) của một lớp. 34 | // Để tạo một đối tượng, ta sử dụng từ khóa new. 35 | ObjectsInJava obj = new ObjectsInJava(); 36 | 37 | // Gán giá trị cho các biến của đối tượng 38 | obj.var = 10; 39 | 40 | // Gọi phương thức của đối tượng 41 | System.out.println("getVar method: " + obj.getVar()); // -> 10 42 | 43 | // Lớp DataTypes ở dưới đây là một lớp public, nên có thể được truy cập từ bên 44 | // ngoài package. 45 | DataTypes data = new DataTypes(); 46 | 47 | // Gọi đến biến grade (public) của đối tượng data 48 | System.out.println("grade variable: " + data.grade); // -> 'A 49 | 50 | // Gọi đến biến name (private) của đối tượng data 51 | // System.out.println(data.name); // Lỗi: name has private access in DataTypes 52 | 53 | // Từ khóa static: 54 | /* 55 | * - Biến static: biến static được chia sẻ giữa tất cả các đối tượng của lớp, 56 | * nếu thay đổi giá trị của biến static từ một đối tượng, giá trị của biến 57 | * static sẽ thay đổi cho tất cả các đối tượng khác. 58 | * - Phương thức static: phương thức static có thể được gọi mà không cần tạo 59 | * đối tượng từ lớp đó. 60 | */ 61 | 62 | // Gọi biến staticVar thông qua tên lớp 63 | System.out.println("staticVar: " + ObjectsInJava.staticVar); // -> 10 64 | 65 | // Trong cùng một lớp, ta có thể gọi biến static trực tiếp mà không cần tiền tố 66 | // tên lớp 67 | System.out.println("staticVar: " + staticVar); // -> 10 68 | 69 | // Gọi phương thức static print10 từ lớp ObjectsInJava 70 | System.out.println("print10 method: " + ObjectsInJava.print10()); // -> 10 71 | 72 | // Thay đổi giá trị của biến staticVar 73 | ObjectsInJava.staticVar = 20; 74 | 75 | // Gọi biến staticVar thông qua một đối tượng 76 | ObjectsInJava otherObject = new ObjectsInJava(); 77 | 78 | System.out.println("staticVar from otherObject: " + otherObject.staticVar); // -> 20 79 | 80 | // Gọi phương thức static sumOfArray từ lớp Arrays 81 | int[] arr = { 1, 2, 3, 4, 5 }; 82 | System.out.println("sumOfArray method: " + Arrays.sumOfArray(arr)); // -> 15 83 | 84 | // Từ khóa final: 85 | /* 86 | * - Biến final: biến final là hằng số, giá trị của biến final không thể thay 87 | * đổi sau khi đã gán giá trị. 88 | * - Phương thức final: phương thức final không thể bị ghi đè (override) trong 89 | * lớp con. 90 | */ 91 | 92 | // Gọi biến finalVar thông qua đối tượng obj 93 | System.out.println("finalVar: " + obj.finalVar); // -> 100 94 | // obj.finalVar = 200; // Lỗi: cannot assign a value to final variable finalVar 95 | } 96 | } 97 | 98 | /* 99 | * - Nếu một biến hoặc phương thức không được khai báo là public, private hoặc 100 | * protected, nó chỉ có thể được truy cập từ cùng một package. 101 | */ 102 | -------------------------------------------------------------------------------- /1. The Java Language/ThisReference.java: -------------------------------------------------------------------------------- 1 | class Display { 2 | public void displayObject(Object obj) { 3 | System.out.println(obj.toString()); 4 | } 5 | } 6 | 7 | public class ThisReference { 8 | // Sử dụng tham chiếu đặc biệt 'this' để truy cập các biến và phương thức của 9 | // lớp hiện tại. 10 | 11 | private int x; 12 | private int y; 13 | 14 | // 1. Phân biệt giữa biến instance và biến local 15 | public void setVar(int x) { 16 | // Biến x ở đây là biến local 17 | // this.x ở đây là biến instance 18 | this.x = x; 19 | } 20 | 21 | // 2. Gọi một constructor khác trong cùng một lớp 22 | public ThisReference() { 23 | // Gọi constructor có tham số 24 | this(0, 0); 25 | } 26 | 27 | public ThisReference(int x, int y) { 28 | this.x = x; 29 | this.y = y; 30 | } 31 | 32 | // 3. Trả về chính đối tượng hiện tại 33 | public ThisReference setX(int x) { 34 | this.x = x; 35 | return this; 36 | } 37 | 38 | public ThisReference setY(int y) { 39 | this.y = y; 40 | return this; 41 | } 42 | 43 | // 4. Truyền đối tượng hiện tại cho phương thức khác 44 | public void display() { 45 | Display display = new Display(); 46 | display.displayObject(this); 47 | } 48 | 49 | @Override // Override phương thức toString() để hiển thị thông tin của đối tượng 50 | public String toString() { 51 | return "ThisReference [x=" + x + ", y=" + y + "]"; 52 | } 53 | 54 | public static void main(String[] args) { 55 | ThisReference obj = new ThisReference(); 56 | System.out.println("x: " + obj.x); // -> 0 57 | System.out.println("y: " + obj.y); // -> 0 58 | 59 | obj.setX(30).setY(40); 60 | System.out.println("x: " + obj.x); // -> 30 61 | System.out.println("y: " + obj.y); // -> 40 62 | 63 | obj.display(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /1. The Java Language/Wrappers.java: -------------------------------------------------------------------------------- 1 | import java.lang.Float; 2 | 3 | public class Wrappers { 4 | // Trình bao bọc (Wrapper) trong Java là các lớp được sử dụng để bao bọc các 5 | // kiểu dữ liệu nguyên thủy (primitive data types). 6 | /* 7 | * - Lớp Wrapper cung cấp một cách để sử dụng các kiểu dữ liệu nguyên thủy như 8 | * một đối tượng. 9 | * - Các lớp Wrapper trong Java: 10 | * + Void -> java.lang.Void 11 | * + Boolean -> java.lang.Boolean 12 | * + Char -> java.lang.Character 13 | * + Byte -> java.lang.Byte 14 | * + Short -> java.lang.Short 15 | * + Integer -> java.lang.Integer 16 | * + Long -> java.lang.Long 17 | * + Float -> java.lang.Float 18 | * + Double -> java.lang.Double 19 | * - Các lớp Wrapper cung cấp các phương thức để chuyển đổi giữa kiểu dữ liệu 20 | * nguyên thủy và kiểu dữ liệu Wrapper. 21 | * - Các lớp Wrapper cung cấp các phương thức để thực hiện các phép toán số học, 22 | * so sánh, chuyển đổi, ... 23 | */ 24 | 25 | public static void main(String[] args) { 26 | // Cú pháp khởi tạo một đối tượng Wrapper: 27 | // x = .valueOf() 28 | 29 | // Khởi tạo một đối tượng Float 30 | Float f = Float.valueOf(10.5f); 31 | 32 | // Chuyển đổi giá trị của đối tượng Float sang kiểu dữ liệu nguyên thủy float 33 | float value = f.floatValue(); 34 | System.out.println("Value: " + value); // -> 10.5 35 | 36 | // Chuyển đổi giá trị của đối tượng Float sang kiểu dữ liệu nguyên thủy int 37 | int intValue = f.intValue(); 38 | System.out.println("Int value: " + intValue); // -> 10 39 | 40 | // Chuyển đổi giá trị của đối tượng Float sang kiểu dữ liệu nguyên thủy double 41 | double doubleValue = f.doubleValue(); 42 | System.out.println("Double value: " + doubleValue); // -> 10.5 43 | 44 | // So sánh hai đối tượng Float 45 | Float f1 = Float.valueOf(10.5f); 46 | Float f2 = Float.valueOf(10.5f); 47 | System.out.println("f1 == f2: " + (f1 == f2)); // -> false 48 | 49 | // So sánh giá trị của hai đối tượng Float 50 | System.out.println("f1.equals(f2): " + f1.equals(f2)); // -> true 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /2. Classes in Java/Abstract.java: -------------------------------------------------------------------------------- 1 | abstract class Shape { 2 | abstract double area(); // -> Diện tích của hình. 3 | // Phương thức trừu tượng, không có phần thân. Mọi lớp con phải định nghĩa 4 | // phương thức này. 5 | 6 | abstract double perimeter(); // Phương thức trừu tượng. 7 | 8 | // Phương thức không trừu tượng. 9 | void display() { 10 | System.out.println("This is a shape."); 11 | } 12 | } 13 | 14 | // Phương thức trừu tượng (abstract method) là một phương thức mà không có phần 15 | // thân (body). 16 | /* 17 | * - Một phương thức trừu tượng không thể được gọi trực tiếp. 18 | * - Để khai báo một phương thức trừu tượng, bạn cần sử dụng từ khóa abstract 19 | * trước kiểu trả về. 20 | * - Mọi lớp con không trừu tượng của lớp chứa phải định nghĩa tất cả các phương 21 | * thức trừu tượng của lớp cha (bằng cách ghép đè chúng). 22 | * - Phương thức trừu tượng được sử dụng để xác định một giao diện mà tất cả các 23 | * lớp con của lớp trừu tượng phải tuân theo. Nó buộc các lớp con phải cung cấp 24 | * triển khai cụ thể cho các phương thức trừu tượng đó. 25 | */ 26 | 27 | // Lớp trừu tượng (abstract class) là một lớp mà không thể tạo đối tượng từ nó. 28 | /* 29 | * - Một lớp được khai báo là trừu tượng có thể chứa một hoặc nhiều phương thức 30 | * trừu tượng. Lớp này cũng có thể chứa phương thức không trừu tượng (có thân 31 | * phương thức). 32 | * - Lớp trừu tượng có thể chứa các biến và phương thức thông thường, cung cấp 33 | * chức năng sẵn có cho các lớp con. 34 | * - Để khai báo một lớp trừu tượng, bạn cần sử dụng từ khóa abstract trước từ 35 | * khóa class. 36 | * - Lớp trừu tượng đóng vai trò như một khung sườn, xác định những tính năng cơ 37 | * bản mà lớp con có thể mở rộng hoặc tùy biến. Lớp này thường chứa một sự kết 38 | * hợp của phương thức trừu tượng (cần phải được triển khai bởi lớp con) và 39 | * phương thức không trừu tượng (đã được triển khai sẵn, có thể sử dụng ngay). 40 | */ 41 | 42 | class Circle extends Shape { 43 | double radius; 44 | 45 | // Hàm tạo của lớp Circle. 46 | Circle(double radius) { 47 | this.radius = radius; 48 | } 49 | 50 | // Định nghĩa phương thức area() của lớp cha. 51 | @Override 52 | double area() { 53 | return Math.PI * radius * radius; 54 | } 55 | 56 | @Override 57 | double perimeter() { 58 | return 2 * Math.PI * radius; 59 | } 60 | } 61 | 62 | class Rectangle extends Shape { 63 | double width, length; 64 | 65 | Rectangle(double width, double length) { 66 | this.width = width; 67 | this.length = length; 68 | } 69 | 70 | @Override 71 | double area() { 72 | return width * length; 73 | } 74 | 75 | @Override 76 | double perimeter() { 77 | return 2 * (width + length); 78 | } 79 | } 80 | 81 | @SuppressWarnings("unused") 82 | public class Abstract { 83 | public static void main(String[] args) { 84 | // Shape shape = new Shape() // -> Báo lỗi vì Shape là lớp trừu tượng. 85 | 86 | Shape point = new Shape() { 87 | @Override 88 | double area() { 89 | return 0; 90 | } 91 | 92 | @Override 93 | double perimeter() { 94 | return 0; 95 | } 96 | }; 97 | // -> Không lỗi vì tạo đối tượng từ lớp trừu tượng Shape thông qua lớp con ẩn 98 | // danh. 99 | 100 | Circle circle = new Circle(5); 101 | 102 | System.out.println("Circle area: " + circle.area()); 103 | System.out.println("Circle perimeter: " + circle.perimeter()); 104 | circle.display(); // Sử dụng phương thức không trừu tượng display() của lớp cha. 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /2. Classes in Java/AdapterClass.java: -------------------------------------------------------------------------------- 1 | // Xem xét ví dụ sau: 2 | /* 3 | * - Chúng ta muốn kết nối một ứng dụng với một hệ thống nhạc. 4 | * - Ứng dụng của chúng ta có một interface để điều kiển nhạc. 5 | * - Nhưng hệ thống nhạc sử dụng các API không tương thích với interface của chúng ta. 6 | */ 7 | 8 | // Interface để điều khiển nhạc. 9 | interface MusicPlayerControl { 10 | void play(); // Phát nhạc. 11 | 12 | void stop(); // Dừng phát nhạc. 13 | 14 | void pause(); // Tạm dừng phát nhạc. 15 | } 16 | 17 | // Hệ thống nhạc sử dụng các API không tương thích với interface của chúng ta. 18 | class AdvancedMusicPlayer { 19 | public void startPlayback() { 20 | System.out.println("Playback started."); 21 | } 22 | 23 | public void stopPlayback() { 24 | System.out.println("Playback stopped."); 25 | } 26 | 27 | public void pausePlayback() { 28 | System.out.println("Playback paused."); 29 | } 30 | } 31 | 32 | // ################################################################# 33 | // ################ Cách 1: Sử dụng Inner Class #################### 34 | // ################################################################# 35 | 36 | // Sử dụng nội lớp để kết nối ứng dụng với hệ thống nhạc. 37 | class MusicSystem { 38 | private AdvancedMusicPlayer player = new AdvancedMusicPlayer(); 39 | 40 | private class MusicPlayerAdapter implements MusicPlayerControl { 41 | public void play() { 42 | player.startPlayback(); 43 | } 44 | 45 | public void stop() { 46 | player.stopPlayback(); 47 | } 48 | 49 | public void pause() { 50 | player.pausePlayback(); 51 | } 52 | } 53 | 54 | public MusicPlayerControl getController() { 55 | return new MusicPlayerAdapter(); 56 | } 57 | } 58 | 59 | // ################################################################ 60 | // ################ Cách 2: Sử dụng Adapter Class ################# 61 | // ################################################################ 62 | class MusicPlayerAdapter implements MusicPlayerControl { 63 | private AdvancedMusicPlayer player; 64 | 65 | public MusicPlayerAdapter(AdvancedMusicPlayer player) { 66 | this.player = player; 67 | } 68 | 69 | public void play() { 70 | player.startPlayback(); 71 | } 72 | 73 | public void stop() { 74 | player.stopPlayback(); 75 | } 76 | 77 | public void pause() { 78 | player.pausePlayback(); 79 | } 80 | } 81 | 82 | // Nếu không sử dụng inner class, chúng ta phải tạo một lớp adapter riêng biệt. 83 | // Điều này có thể làm tăng độ phức tạp của cấu trúc dự án và làm cho các thành 84 | // phần phụ thuộc lẫn nhau một cách không cần thiết. 85 | 86 | // ##################### Lớp thực thi ######################### 87 | 88 | public class AdapterClass { 89 | public static void main(String[] args) { 90 | // Sử dụng Inner Class 91 | MusicSystem system = new MusicSystem(); 92 | MusicPlayerControl controller = system.getController(); 93 | 94 | controller.play(); 95 | controller.pause(); 96 | controller.stop(); 97 | 98 | // Sử dụng Adapter Class 99 | AdvancedMusicPlayer player = new AdvancedMusicPlayer(); 100 | MusicPlayerControl newController = new MusicPlayerAdapter(player); 101 | 102 | newController.play(); 103 | newController.pause(); 104 | newController.stop(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /2. Classes in Java/Annotations.java: -------------------------------------------------------------------------------- 1 | // Annotations (hay chú thích) là một cách để thêm metadata vào code của bạn. 2 | /* 3 | * - Metadata (siêu dữ liệu) là dữ liệu mô tả dữ liệu khác. 4 | * - Các chú thích này không ảnh hưởng trực tiếp đến hoạt động của chương trình, nhưng chúng có thể được sử dụng bởi các công cụ hoặc frameworks khác để thực hiện một số công việc. 5 | * - Chú thích cung cấp thông tin bổ sung mà không thay đổi chương trình của bạn. 6 | * - Các chú thích tích hợp sẵn trong Java: 7 | * + @Override: chỉ ra 1 phương thức sẽ ghi đè 1 phương thức trong lớp cha. 8 | * + @Deprecated: chỉ ra rằng phương thức hoặc lớp đã bị lỗi thời và không nên được sử dụng. 9 | * + @SuppressWarnings: nói cho bộ biên dịch Java bỏ qua cảnh báo cụ thể. 10 | * + @FunctionalInterface: chỉ ra rằng một interface được thiết kế để hỗ trợ các lambda expression (chỉ chứa một phương thức trừu tượng). 11 | * + ... 12 | * - Người dùng cũng có thể tạo chú thích của riêng mình bằng cách sử dụng @interface. 13 | */ 14 | 15 | // Ví dụ việc tạo chú thích: 16 | @interface MyAnnotation { 17 | String author(); 18 | 19 | String date(); 20 | 21 | int currentRevision() default 1; 22 | 23 | String lastModified() default "N/A"; 24 | 25 | String lastModifiedBy() default "N/A"; 26 | 27 | String[] reviewers(); 28 | } 29 | 30 | // Sử dụng chú thích: 31 | @MyAnnotation(author = "Nguyen Van A", date = "3/4/2021", currentRevision = 2, lastModified = "4/4/2021", lastModifiedBy = "Nguyen Van B", reviewers = { 32 | "Nguyen Van C", "Nguyen Van D" }) 33 | class MyClass { 34 | // code 35 | } 36 | 37 | public class Annotations { 38 | 39 | } 40 | -------------------------------------------------------------------------------- /2. Classes in Java/AnonymousInnerClass.java: -------------------------------------------------------------------------------- 1 | // Tiếp tục về nội lớp (inner class). 2 | // 4. Nội lớp vô danh (anonymous inner class): 3 | /* 4 | * - Là một loại nội lớp không có tên, được khai báo và khởi tạo ngay tại chỗ. 5 | * - Dùng khi bạn cần tạo một lớp mà chỉ sử dụng một lần duy nhất. 6 | */ 7 | 8 | // Xét ví dụ sau: 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Iterator; 13 | 14 | // Lớp Friend chứa thông tin về một người bạn, bao gồm tên và địa chỉ. 15 | class Friend { 16 | private String name; 17 | private String address; 18 | 19 | public Friend(String name, String address) { 20 | this.name = name; 21 | this.address = address; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "Friend [name=" + name + ", address=" + address + "]"; 27 | } 28 | } 29 | 30 | // Lớp FriendList chứa danh sách các người bạn. 31 | class FriendList { 32 | private List friends = new ArrayList<>(); 33 | 34 | public void addFriend(Friend friend) { 35 | friends.add(friend); 36 | } 37 | 38 | public int getSize() { 39 | return friends.size(); 40 | } 41 | 42 | // Nội lớp cung cấp iterator để duyệt qua danh sách các người bạn. 43 | public class FriendIterator implements Iterator { 44 | private int index = 0; 45 | 46 | @Override 47 | public boolean hasNext() { 48 | return index < friends.size(); 49 | } 50 | 51 | @Override 52 | public Friend next() { 53 | return friends.get(index++); 54 | } 55 | } 56 | 57 | public FriendIterator getIterator() { 58 | return new FriendIterator(); 59 | } 60 | 61 | // Sử dụng nội lớp vô danh để tạo một Iterator mới mà không cần phải tạo lớp 62 | // mới. 63 | public Iterator getReverseIterator() { 64 | return new Iterator() { 65 | private int index = friends.size() - 1; 66 | 67 | @Override 68 | public boolean hasNext() { 69 | return index >= 0; 70 | } 71 | 72 | @Override 73 | public Friend next() { 74 | return friends.get(index--); 75 | } 76 | }; 77 | // Đây là một nội lớp vô danh, không có tên, được khai báo và khởi tạo ngay 78 | // tại chỗ. 79 | } 80 | } 81 | 82 | public class AnonymousInnerClass { 83 | public static void main(String[] args) { 84 | FriendList myList = new FriendList(); 85 | 86 | // Thêm các người bạn vào danh sách. 87 | myList.addFriend(new Friend("Alice", "USA")); 88 | myList.addFriend(new Friend("Bob", "UK")); 89 | myList.addFriend(new Friend("Carol", "France")); 90 | 91 | System.out.println("--Book list:"); 92 | 93 | // Sử dụng Iterator để duyệt qua danh sách các cuốn sách. 94 | Iterator iterator = myList.getIterator(); 95 | while (iterator.hasNext()) { 96 | System.out.println(iterator.next()); 97 | } 98 | 99 | System.out.println("\n--Book list in reverse order:"); 100 | 101 | // Sử dụng Iterator để duyệt qua danh sách các cuốn sách theo thứ tự ngược lại. 102 | Iterator reverseIterator = myList.getReverseIterator(); 103 | while (reverseIterator.hasNext()) { 104 | System.out.println(reverseIterator.next()); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /2. Classes in Java/Callback.java: -------------------------------------------------------------------------------- 1 | // Callback (gọi lại) là một cơ chế trong lập trình cho phép một đối tượng truyền tham chiếu đến một 2 | // hoặc nhiều phương thức của nó cho một đối tượng khác, để sau này đối tượng được gọi có thể gọi lại các phương thức đó. 3 | // Hiểu đơn giản là, truyền một phương thức vào một phương thức khác để thực thi nó sau này. 4 | /* 5 | * - Đây là một phương pháp thường được sử dụng trong các ngôn ngữ như C hay C++ thông qua con trỏ hàm, 6 | * nhưng trong Java, cách tiếp cận này được thực hiện bằng cách sử dụng interfaces. 7 | * - Callback giúp chúng ta giảm sự phụ thuộc giữa các đối tượng với nhau, giúp chúng ta dễ dàng mở rộng 8 | * ứng dụng của mình hơn. 9 | */ 10 | 11 | // Ví dụ: 12 | 13 | // Định nghĩa interface TaskListener với phương thức onTaskCompleted() để xử lý kết quả trả về từ một Task nào đó. 14 | interface TaskListener { 15 | void onTaskCompleted(String result); 16 | } 17 | 18 | // Tạo class Task mà sẽ sử dụng interface TaskListener để thông báo cho đối 19 | // tượng khác khi nó hoàn thành công việc. 20 | class Task { 21 | private TaskListener listener; 22 | 23 | public void setTaskListener(TaskListener listener) { 24 | this.listener = listener; 25 | } 26 | 27 | public void completeTask() { 28 | // Do something 29 | // Sau khi hoàn thành công việc, gọi phương thức onTaskCompleted() của interface 30 | // TaskListener 31 | if (listener != null) { 32 | listener.onTaskCompleted("Task completed"); 33 | } 34 | } 35 | } 36 | 37 | // Triển khai class Application mà triển khai interface TaskListener và đăng ký 38 | // nhận sự kiện từ Task. 39 | class Application implements TaskListener { 40 | @Override 41 | public void onTaskCompleted(String result) { 42 | System.out.println("Notification from Task:: " + result); 43 | } 44 | 45 | public static void main(String[] args) { 46 | 47 | Task task = new Task(); 48 | Application app = new Application(); 49 | 50 | // Đăng ký nhận sự kiện từ Task 51 | task.setTaskListener(app); 52 | 53 | // Thực thi Task 54 | task.completeTask(); 55 | } 56 | } 57 | 58 | // Trong ví dụ này: 59 | /* 60 | * - TaskListener là interface chứa phương thức onTaskCompleted() để xử lý kết 61 | * quả trả về từ Task. 62 | * - Task là class chứa phương thức completeTask() để thực thi công việc và 63 | * thông báo kết quả trả về thông qua interface TaskListener. 64 | * - Application là class triển khai interface TaskListener để xử lý kết quả trả 65 | * về từ Task. 66 | */ 67 | 68 | // Như vậy, interface TaskListener có ý nghĩa: 69 | /* 70 | * - Task không cần biết đối tượng nào sẽ xử lý kết quả trả về của nó. 71 | * - Task chỉ cần gọi phương thức onTaskCompleted() của interface TaskListener 72 | * để thông báo kết quả trả về. 73 | * - Application sẽ triển khai interface TaskListener để xử lý kết quả trả về từ 74 | * Task. 75 | * -> Từ đó, chúng ta có thể dễ dàng thay đổi hoặc mở rộng ứng dụng mà không cần 76 | * sửa đổi Task. 77 | * -> Callback giúp chúng ta giảm sự phụ thuộc giữa các đối tượng với nhau, giúp 78 | * chúng ta dễ dàng mở rộng ứng dụng của mình hơn. 79 | */ 80 | 81 | public class Callback { 82 | public static void main(String[] args) { 83 | Application.main(args); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /2. Classes in Java/Casting.java: -------------------------------------------------------------------------------- 1 | class Animal { 2 | void eat() { 3 | System.out.println("eating..."); 4 | } 5 | } 6 | 7 | // Lớp Dog kế thừa từ lớp Animal. 8 | class Dog extends Animal { 9 | void bark() { 10 | System.out.println("barking..."); 11 | } 12 | } 13 | 14 | public class Casting { 15 | // Ép kiểu (Casting) trong Java là quá trình chuyển đổi giữa các kiểu dữ liệu. 16 | // Có hai loại ép kiểu: ép kiểu ngầm định (implicit casting) và ép kiểu tường 17 | // minh (explicit casting). 18 | /* 19 | * - Ép kiểu ngầm định (implicit casting): Java tự động chuyển đổi giữa các kiểu 20 | * dữ liệu. 21 | * - Ép kiểu tường minh (explicit casting): Chuyển đổi giữa các kiểu dữ liệu một 22 | * cách thủ công. 23 | * Để thực hiện ép kiểu tường minh, bạn cần sử dụng cú pháp: (type) expression. 24 | * Trong đó, type là kiểu dữ liệu mà bạn muốn chuyển đổi và expression là giá 25 | * trị cần chuyển đổi. 26 | */ 27 | 28 | // Trong kế thừa đối tượng, một đối tượng của lớp con có thể được gán cho một 29 | // đối tượng của lớp cha. 30 | // Điều này được gọi là ép kiểu ngầm định. 31 | // Tuy nhiên, một đối tượng của lớp cha không thể được gán cho một đối tượng của 32 | // lớp con mà không có ép kiểu tường minh. 33 | 34 | // Ví dụ: 35 | public static void main(String[] args) { 36 | Animal anAnimal = new Animal(); 37 | Dog aDog = new Dog(); 38 | 39 | anAnimal = aDog; // Ép kiểu ngầm định 40 | // aDog = anAnimal; 41 | // Lỗi: incompatible types: Animal cannot be converted to Dog 42 | aDog = (Dog) anAnimal; // Ép kiểu tường minh 43 | 44 | // lệnh instanceof kiểm tra xem một đối tượng có phải là một thể hiện của một 45 | // lớp cụ thể hay không. 46 | 47 | // Ví dụ: 48 | if (anAnimal instanceof Dog) { 49 | aDog = (Dog) anAnimal; 50 | aDog.bark(); 51 | } // -> Nếu anAnimal là một thể hiện của lớp Dog, thì gọi phương thức bark() 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /2. Classes in Java/Clone.java: -------------------------------------------------------------------------------- 1 | // Phương thức clone() được sử dụng để tạo một bản sao của đối tượng hiện tại. 2 | /* 3 | * - Bản sao đó có cùng giá trị với đối tượng gốc, nhưng nó không phải là cùng một đối tượng (không cùng một vùng nhớ). 4 | * - Để sử dụng phương thức clone(), lớp của đối tượng cần phải triển khai giao diện Cloneable. 5 | * - Phương thức clone() là protected, nghĩa là nó chỉ có thể được gọi từ bên trong lớp, lớp con hoặc các lớp cùng gói. 6 | * -> Do đó nếu muốn sử dụng phương thức clone() từ bên ngoài, bạn cần phải ghi đè phương thức clone() và đặt nó là public. 7 | * - Phương thức clone() trả về một đối tượng, do đó cần phải ép kiểu đối tượng trả về về kiểu của đối tượng gốc. 8 | */ 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | class Robot implements Cloneable { 14 | String model; 15 | int weight; 16 | List skills; 17 | 18 | public Robot(String model, int weight, List skills) { 19 | this.model = model; 20 | this.weight = weight; 21 | this.skills = skills; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "Robot [model=" + model + ", weight=" + weight + ", skills=" + skills + "]"; 27 | } 28 | 29 | @Override 30 | public Robot clone() { 31 | try { 32 | Robot robot = (Robot) super.clone(); 33 | return robot; 34 | } catch (CloneNotSupportedException e) { 35 | return null; 36 | } 37 | } 38 | } 39 | 40 | public class Clone { 41 | public static void main(String[] args) { 42 | System.out.println("--Shallow copy:"); 43 | Robot robot1 = new Robot("R2D2", 100, List.of("walk", "talk")); 44 | Robot robot2 = robot1.clone(); 45 | 46 | System.out.println(robot1.toString()); // Robot [model=R2D2, weight=100, skills=[walk, talk]] 47 | System.out.println(robot2.toString()); // Robot [model=R2D2, weight=100, skills=[walk, talk]] 48 | 49 | System.out.println(robot1 == robot2); // false 50 | 51 | // -> Như vậy, robot2 là một bản sao của robot1, chúng có cùng giá trị nhưng 52 | // không cùng một vùng nhớ. 53 | // Tuy nhiên, đây là bản sao nông (shallow copy), nghĩa là các tham chiếu bên 54 | // trong đối tượng vẫn trỏ đến cùng một vùng nhớ. 55 | // Khi sử dụng phương thức clone() để tạo bản sao, Java mặc định tạo một bản 56 | // sao nông. Do đó, nếu muốn tạo bản sao sâu (deep copy), bạn cần phải tự viết 57 | // mã để sao chép các trường tham chiếu bên trong đối tượng. 58 | 59 | System.out.println(robot1.skills == robot2.skills); // true 60 | 61 | System.out.println("\n--Deep copy:"); 62 | // Ví dụ về bản sao sâu (deep copy): 63 | Automaton automaton1 = new Automaton("T-800", 200, List.of("walk", "talk")); 64 | Automaton automaton2 = automaton1.clone(); 65 | 66 | System.out.println(automaton1.toString()); // Automaton [model=T-800, weight=200, skills=[walk, talk]] 67 | 68 | // Thay đổi skills của automaton2. 69 | automaton2.skills.add("shoot"); 70 | 71 | System.out.println(automaton2.toString()); // Automaton [model=T-800, weight=200, skills=[walk, talk, shoot]] 72 | } 73 | } 74 | 75 | // Sử dụng bản sao sâu (deep copy) để sao chép các trường tham chiếu bên trong 76 | // đối tượng. 77 | class Automaton implements Cloneable { 78 | String model; 79 | int weight; 80 | List skills; 81 | 82 | public Automaton(String model, int weight, List skills) { 83 | this.model = model; 84 | this.weight = weight; 85 | this.skills = skills; 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | return "Automaton [model=" + model + ", weight=" + weight + ", skills=" + skills + "]"; 91 | } 92 | 93 | @Override 94 | public Automaton clone() { 95 | try { 96 | Automaton automaton = (Automaton) super.clone(); 97 | // Tạo một bản sao sâu (deep copy) của skills. 98 | automaton.skills = new ArrayList<>(skills); 99 | return automaton; 100 | } catch (CloneNotSupportedException e) { 101 | return null; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /2. Classes in Java/Equals.java: -------------------------------------------------------------------------------- 1 | // Trong Java, equals() là một phương thức của lớp Object được sử dụng để so sánh hai đối tượng. 2 | /* 3 | * - Nó xem xét hai đối tượng là bằng nhau nếu chúng cùng tham chiếu đến cùng một vùng nhớ trong bộ nhớ. 4 | * - Phương thức equals() được ghi đè bởi các lớp con để so sánh dữ liệu của đối tượng. 5 | * - Nếu không ghi đè phương thức này, nó sẽ so sánh hai đối tượng bằng cách so sánh địa chỉ bộ nhớ của chúng. 6 | * -> Kết quả sẽ giống như sử dụng toán tử ==. 7 | */ 8 | 9 | // Ví dụ về equals() mặc định: 10 | @SuppressWarnings("unused") 11 | class Footballer { 12 | private String name; 13 | private int age; 14 | 15 | public Footballer(String name, int age) { 16 | this.name = name; 17 | this.age = age; 18 | } 19 | } 20 | 21 | // Nếu ghi đè phương thức equals() để so sánh dữ liệu của đối tượng: 22 | class Coach { 23 | private String name; 24 | private int age; 25 | 26 | public Coach(String name, int age) { 27 | this.name = name; 28 | this.age = age; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | // Kiểm tra xem obj có phải là một instance của lớp Coach hay không. 34 | if (obj instanceof Coach) { 35 | // Ép kiểu obj về lớp Coach. 36 | Coach coach = (Coach) obj; 37 | // So sánh dữ liệu của hai đối tượng. 38 | return (name.equals(coach.name) && age == coach.age); 39 | } 40 | return false; 41 | } 42 | } 43 | 44 | @SuppressWarnings("unlikely-arg-type") 45 | public class Equals { 46 | public static void main(String[] args) { 47 | Footballer footballer1 = new Footballer("Messi", 34); 48 | Footballer footballer2 = new Footballer("Messi", 34); 49 | 50 | // So sánh hai đối tượng sử dụng equals() mặc định. 51 | // Kết quả sẽ là false vì không ghi đè phương thức equals(). 52 | System.out.println(footballer1.equals(footballer2)); // false 53 | 54 | // So sánh hai đối tượng sử dụng equals() đã được ghi đè. 55 | // Kết quả sẽ là true vì dữ liệu của hai đối tượng giống nhau. 56 | Coach coach1 = new Coach("Klopp", 54); 57 | Coach coach2 = new Coach("Klopp", 54); 58 | System.out.println(coach1.equals(coach2)); // true 59 | 60 | // Nếu so sánh giữa hai đối tượng khác nhau. 61 | System.out.println(coach1.equals(footballer1)); // false 62 | // -> false vì student1 không phải là một instance của lớp Teacher. 63 | 64 | // So sánh hai đối tượng sử dụng toán tử ==. 65 | // Kết quả sẽ là false vì toán tử == so sánh địa chỉ bộ nhớ của hai đối tượng. 66 | System.out.println(footballer1 == footballer2); // false 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /2. Classes in Java/EventHandling.java: -------------------------------------------------------------------------------- 1 | // Một tác dụng khác thường thấy của anonymous inner class là xử lý sự kiện (event handling). 2 | /* 3 | * - Sự kiện (event) là một hành động xảy ra trong ứng dụng, ví dụ nhấn nút chuột, gõ phím, kéo thả, ... 4 | * - Event handling là quá trình xử lý các sự kiện này. 5 | * - Trong Java, event handling thường được thực hiện thông qua các listener (người nghe) và adapter (bộ chuyển đổi). 6 | * - Listener là một interface chứa các phương thức để xử lý sự kiện. 7 | * - Adapter là một lớp trung gian giữa listener và component (thành phần) để xử lý sự kiện. 8 | * - Anonymous inner class thường được sử dụng để triển khai listener và adapter một cách nhanh chóng và tiện lợi. 9 | */ 10 | 11 | // Xét ví dụ sau: 12 | /* 13 | * - Trong ví dụ này, chúng ta tạo một cửa sổ JFrame với một nút JButton. 14 | * - Khi người dùng nhấn nút, chúng ta muốn in ra màn hình dòng chữ "Button clicked!". 15 | * - Để xử lý sự kiện này, chúng ta sử dụng một anonymous inner class để triển khai ActionListener. 16 | * - ActionListener là một interface trong gói java.awt.event, nó chứa một phương thức actionPerformed() để xử lý sự kiện. 17 | * - Trong anonymous inner class, chúng ta triển khai phương thức actionPerformed() để in ra dòng chữ "Button clicked!". 18 | */ 19 | 20 | import java.awt.event.*; 21 | import javax.swing.*; 22 | 23 | // Sử dụng nội lớp vô danh để xử lý sự kiện khi người dùng nhấn nút. 24 | public class EventHandling { 25 | public static void main(String[] args) { 26 | // Tạo một cửa sổ JFrame 27 | JFrame frame = new JFrame("Event Handling Example"); 28 | 29 | // Tạo một nút JButton 30 | JButton button = new JButton("Click Me"); 31 | 32 | // Thêm nút vào cửa sổ 33 | frame.add(button); 34 | 35 | // Đặt kích thước cửa sổ 36 | frame.setSize(300, 200); 37 | 38 | // Đặt cửa sổ hiển thị ở giữa màn hình 39 | frame.setLocationRelativeTo(null); 40 | 41 | // Đặt cửa sổ hiển thị 42 | frame.setVisible(true); 43 | 44 | // Sử dụng nội lớp vô danh để xử lý sự kiện khi người dùng nhấn nút 45 | button.addActionListener(new ActionListener() { 46 | @Override 47 | public void actionPerformed(ActionEvent e) { 48 | System.out.println("Button clicked!"); 49 | } 50 | }); 51 | } 52 | } 53 | 54 | // Tác dụng của nội lớp vô danh trong ví dụ trên: 55 | /* 56 | * - Anonymous inner class này được truyền vào phương thức addActionListener() 57 | * của JButton để xử lý sự kiện khi người dùng nhấn nút. 58 | * - Anonymous inner class giúp viết mã ngắn gọn và dễ hiểu hơn so với việc tạo 59 | * một lớp mới và triển khai ActionListener. 60 | */ 61 | -------------------------------------------------------------------------------- /2. Classes in Java/Hashcode.java: -------------------------------------------------------------------------------- 1 | // Phương thức hashCode() trả về mã băm của đối tượng. 2 | /* 3 | * - Mã băm là một số nguyên duy nhất được tạo ra để đại diện cho một đối tượng. 4 | * - Mã băm được sử dụng trong các cấu trúc dữ liệu như bảng băm. 5 | * - Nếu hai đối tượng bằng nhau theo phương thức equals(Object), thì chúng phải có cùng một giá trị hash code. 6 | * - Nếu hai đối tượng có hash code khác nhau, chúng không được coi là bằng nhau. 7 | * - Nếu hai đối tượng có cùng hash code, chúng có thể bằng nhau hoặc không bằng nhau. 8 | */ 9 | 10 | // Ví dụ về phương thức hashCode(): 11 | class Laptop { 12 | private String brand; 13 | private String model; 14 | private int price; 15 | 16 | public Laptop(String brand, String model, int price) { 17 | this.brand = brand; 18 | this.model = model; 19 | this.price = price; 20 | } 21 | 22 | @Override 23 | public int hashCode() { 24 | return brand.hashCode() + model.hashCode() + price; 25 | } 26 | } 27 | 28 | public class Hashcode { 29 | public static void main(String[] args) { 30 | Laptop laptop1 = new Laptop("Dell", "Inspiron", 700); 31 | Laptop laptop2 = new Laptop("Dell", "Inspiron", 700); 32 | 33 | // So sánh hai đối tượng sử dụng hashCode(). 34 | // Kết quả sẽ là true vì hai đối tượng giống nhau. 35 | System.out.println(laptop1.hashCode() == laptop2.hashCode()); // true 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /2. Classes in Java/InnerClass.java: -------------------------------------------------------------------------------- 1 | // Tiếp tục về lớp chồng nhau (nested class, inner class). 2 | 3 | // 1. Inner Classes as Adapters 4 | /* 5 | * Một trong những ứng dụng phổ biến của inner class là sử dụng chúng như một adapter class (lớp tiếp hợp). 6 | * - Lớp tiếp hợp (adapter class) là một lớp giúp kết nối giữa hai lớp không liên quan với nhau. 7 | * - Việc sử dụng inner classes như là các adapter cho phép tách biệt rõ ràng giữa các chức năng 8 | * và giữ gìn tính đóng gói, đồng thời cung cấp một cách linh hoạt để mở rộng chức năng của một 9 | * lớp mà không làm phức tạp hoặc làm lộ dữ liệu nội bộ. 10 | */ 11 | 12 | // Ví dụ: 13 | 14 | import java.util.ArrayList; 15 | import java.util.Iterator; 16 | import java.util.List; 17 | 18 | // Lớp Book chứa thông tin về một cuốn sách (tên sách, tác giả) 19 | class Book { 20 | String title; 21 | String author; 22 | 23 | Book(String title, String author) { 24 | this.title = title; 25 | this.author = author; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return title + " by " + author; 31 | } 32 | } 33 | 34 | // Lớp BookList chứa một danh sách các cuốn sách. 35 | class BookList { 36 | private List books = new ArrayList<>(); 37 | 38 | public void addBook(Book book) { 39 | books.add(book); 40 | } 41 | 42 | public int getSize() { 43 | return books.size(); 44 | } 45 | 46 | // Nội lớp cung cấp iterator để duyệt qua danh sách các cuốn sách. 47 | public class BookIterator implements Iterator { 48 | private int index = 0; 49 | 50 | @Override 51 | public boolean hasNext() { 52 | return index < books.size(); 53 | } 54 | 55 | @Override 56 | public Book next() { 57 | return books.get(index++); 58 | } 59 | } 60 | 61 | public BookIterator getIterator() { 62 | return new BookIterator(); 63 | } 64 | } 65 | 66 | public class InnerClass { 67 | public static void main(String[] args) { 68 | BookList myList = new BookList(); 69 | 70 | myList.addBook(new Book("The Java Programming", "James Gosling")); 71 | myList.addBook(new Book("Learn Python Programming", "Guido van Rossum")); 72 | 73 | // Lấy ra một iterator để duyệt qua danh sách các cuốn sách. 74 | BookList.BookIterator iterator = myList.getIterator(); 75 | 76 | // Duyệt qua danh sách các cuốn sách và in ra màn hình. 77 | while (iterator.hasNext()) { 78 | System.out.println(iterator.next()); 79 | } 80 | } 81 | } 82 | 83 | // Mục đích của ví dụ: 84 | /* 85 | * Trong trường hợp này, BookList là một lớp quản lý một danh sách các sách. 86 | * Chúng ta muốn có thể duyệt qua các sách trong danh sách này một cách tuần tự, 87 | * nhưng BookList không tự nó cung cấp phương thức để làm điều đó. Để giải quyết 88 | * vấn đề này, chúng ta sử dụng một inner class (BookListIterator) để cung cấp 89 | * khả năng iterator mà không cần phải thay đổi cấu trúc hoặc công khai chi tiết 90 | * nội bộ của BookList. 91 | */ 92 | 93 | // Chú thích: Interator là một interface trong Java Collection Framework, nó 94 | // cung cấp 95 | // các phương thức để duyệt qua các phần tử của một Collection. 96 | // - boolean hasNext(): Kiểm tra xem có phần tử tiếp theo không. 97 | // - next(): Trả về phần tử tiếp theo trong Collection. 98 | // - void remove(): Xóa phần tử hiện tại khỏi Collection. 99 | 100 | /* 101 | * Cách Hoạt Động Của BookList và BookListIterator 102 | * i) Lớp BookList: 103 | * - Đây là lớp chính chứa danh sách các sách. 104 | * - Lớp này cung cấp phương thức addBook() để thêm sách vào danh sách và phương 105 | * thức getIterator() để lấy một iterator. 106 | * ii) Inner Class BookListIterator: 107 | * - Đây là một inner class được định nghĩa bên trong BookList. Nó cung cấp các 108 | * phương thức để duyệt qua danh sách sách. 109 | * - hasNext(): Kiểm tra xem có sách tiếp theo trong danh sách không. 110 | * - next(): Trả về sách tiếp theo và di chuyển chỉ mục tiếp theo. Nếu không còn 111 | * sách, phương thức này sẽ ném ra một NoSuchElementException. 112 | */ 113 | 114 | /* 115 | * Tại Sao BookListIterator Là Một Adapter? 116 | * 117 | * BookListIterator đóng vai trò như một adapter vì nó "chuyển đổi" lớp BookList 118 | * từ một lớp không có khả năng iterator sang một lớp có thể được duyệt qua một 119 | * cách tuần tự. Nó làm điều này mà không yêu cầu BookList phải thực hiện trực 120 | * tiếp giao diện Iterable hoặc thay đổi thiết kế của nó để hỗ trợ duyệt. Thay 121 | * vào đó, BookListIterator cung cấp một cách để truy cập tuần tự đến các phần 122 | * tử của BookList từ bên trong lớp. 123 | */ 124 | 125 | // -> Xem thêm ví dụ ở file AdapterClass.java 126 | -------------------------------------------------------------------------------- /2. Classes in Java/Interface.java: -------------------------------------------------------------------------------- 1 | // Giao diện (interface) trong Java là một nhóm các phương thức trừu tượng mà 2 | // không chứa triển khai (implementation) cụ thể nào cả. Interface chỉ định rõ 3 | // các phương thức mà một lớp phải triển khai nếu lớp đó tuyên bố thực thi 4 | // (implements) interface đó. 5 | 6 | /* 7 | * - Khác với lớp trừu tượng, một lớp khi thực thi một interface không cần phải kế 8 | * thừa từ một phần cụ thể nào của hệ thống kế thừa. Điều này giúp tăng tính 9 | * linh hoạt và khả năng sử dụng lại mã lệnh, do interface không yêu cầu một cấu 10 | * trúc kế thừa cụ thể nào. 11 | * - Để khai báo một interface, sử dụng từ khóa interface, sau đó là tên của interface 12 | * và một tập hợp các phương thức trừu tượng. Một lớp có thể thực thi nhiều interface 13 | * bằng cách sử dụng từ khóa implements. 14 | * - Một lớp thực thi một interface phải cung cấp triển khai cho tất cả các phương thức 15 | * trừu tượng trong interface đó. 16 | * - Interface không thể chứa các biến instance, nhưng có thể chứa các biến static 17 | * và final. 18 | */ 19 | 20 | // 21 | 22 | interface Movable { 23 | // Hàm move() để di chuyển đối tượng theo vector (x, y, z). 24 | // Không cần khai báo từ khóa abstract vì tất cả các phương thức trong interface 25 | // đều mặc định là trừu tượng và public. 26 | void move(double x, double y, double z); 27 | 28 | // Một interface có thể chứa các biến static và final. 29 | static final double MAX_DISTANCE = 100; 30 | } 31 | 32 | interface Trackable { 33 | // Hàm getTrackerID() trả về ID của tracker. 34 | String getTrackerID(); 35 | 36 | // Hàm getCurrentPosition() trả về mảng chứa tọa độ hiện tại (x, y, z). 37 | double[] getCurrentPosition(); 38 | 39 | // Hàm isClose() kiểm tra xem đối tượng có ở gần không. 40 | boolean isClose(double x, double y, double z); 41 | } 42 | 43 | // Lớp Drone thực thi cả hai interface Movable và Trackable. 44 | class Drone implements Movable, Trackable { 45 | private String trackerID; 46 | private double x, y, z; 47 | 48 | public Drone(String trackerID) { 49 | this.trackerID = trackerID; 50 | x = 0.0; 51 | y = 0.0; 52 | z = 0.0; 53 | } 54 | 55 | @Override 56 | public void move(double x, double y, double z) { 57 | this.x += x; 58 | this.y += y; 59 | this.z += z; 60 | } 61 | 62 | @Override 63 | public String getTrackerID() { 64 | return trackerID; 65 | } 66 | 67 | @Override 68 | public double[] getCurrentPosition() { 69 | return new double[] { x, y, z }; 70 | } 71 | 72 | @Override 73 | public boolean isClose(double x, double y, double z) { 74 | return Math.sqrt(Math.pow(this.x - x, 2) + Math.pow(this.y - y, 2) + Math.pow(this.z - z, 2)) < MAX_DISTANCE; 75 | } 76 | 77 | public void printPosition() { 78 | System.out.println("Car " + trackerID + " is at position (" + x + ", " + y + ", " + z + ")"); 79 | } 80 | } 81 | 82 | public class Interface { 83 | public static void main(String[] args) { 84 | Drone drone = new Drone("DR001"); 85 | 86 | drone.move(10, 20, 30); 87 | drone.printPosition(); 88 | 89 | System.out.println("Tracker ID: " + drone.getTrackerID()); // -> DR001 90 | System.out.println( 91 | "Current position: " + drone.getCurrentPosition()[0] + ", " + drone.getCurrentPosition()[1] + ", " 92 | + drone.getCurrentPosition()[2]); // -> 10.0, 20.0, 30.0 93 | 94 | System.out.println("Is close: " + drone.isClose(10, 20, 30)); // -> true 95 | 96 | // Có thể khai báo một biến kiểu interface và gán cho nó một đối tượng của lớp 97 | // thực thi interface đó. 98 | Movable movable; 99 | 100 | movable = drone; // -> Đúng vì Drone thực thi cả hai interface Movable và Trackable 101 | movable.move(5, 5, 5); // -> Di chuyển drone 5 đơn vị theo vector (5, 5, 5) 102 | 103 | // movable.printPosition(); 104 | // -> Lỗi vì phương thức printPosition() không thuộc interface Movable 105 | 106 | Drone otherDrone = new Drone("DR002"); 107 | // otherDrone = movable; // -> Sai, phải ép kiểu tường minh 108 | otherDrone = (Drone) movable; 109 | 110 | otherDrone.printPosition(); 111 | // -> DR001 is at position (15.0, 25.0, 35.0) 112 | // Không phải (0.0, 0.0, 0.0) vì otherDrone đã nhận giá trị của movable 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /2. Classes in Java/InterfaceVariables.java: -------------------------------------------------------------------------------- 1 | // Một interface có thể chứa các biến, nhưng các biến này mặc định là public, static và final; 2 | /* 3 | * - Các biến này có thể được tham chiếu trực tiếp thông qua tên của interface. 4 | * - Các biến này xuất hiện trong tất cả các class implement interface đó. 5 | */ 6 | 7 | // Ví dụ: 8 | interface Scaleable { 9 | static final int BIG = 0, MEDIUM = 1, SMALL = 2; 10 | 11 | void setScale(int size); 12 | } 13 | 14 | class Box implements Scaleable { 15 | private int size; 16 | 17 | @Override 18 | public void setScale(int size) { 19 | switch (size) { 20 | case BIG: 21 | this.size = 100; 22 | break; 23 | case MEDIUM: 24 | this.size = 50; 25 | break; 26 | case SMALL: 27 | this.size = 25; 28 | break; 29 | default: 30 | System.out.println("Invalid size"); 31 | } 32 | } 33 | 34 | public void printSize() { 35 | System.out.println("Size: " + size); 36 | } 37 | } 38 | 39 | public class InterfaceVariables { 40 | public static void main(String[] args) { 41 | Box box = new Box(); 42 | box.setScale(Scaleable.BIG); 43 | box.printSize(); // Size: 100 44 | } 45 | } 46 | 47 | // Interfaces trong lập trình hướng đối tượng nên chỉ được sử dụng để định nghĩa 48 | // hành vi, không phải để lưu trữ trạng thái hay dữ liệu cụ thể. Việc sử dụng 49 | // interfaces để chứa biến (các hằng số) làm mờ đi mục đích chính của chúng và 50 | // có thể dẫn đến một thiết kế không sạch sẽ, khiến cho các hằng số và hành vi 51 | // được gói gọn cùng nhau một cách không cần thiết. 52 | 53 | /* 54 | * - Tất cả biến trong interface đều là public static final. Điều này có nghĩa 55 | * là 56 | * chúng luôn luôn công khai và có sẵn cho bất kỳ ai sử dụng interface. Điều này 57 | * cản trở khả năng đóng gói, khiến cho việc quản lý trạng thái của chương trình 58 | * trở nên khó khăn hơn. 59 | * - Việc mỗi lớp thực thi một interface kế thừa toàn bộ các hằng số có thể gây 60 | * rối loạn API của lớp đó, khiến cho API bao gồm nhiều thành phần không cần 61 | * thiết 62 | */ 63 | 64 | // -> Tóm lại, không nên sử dụng interface để chứa biến, hãy sử dụng class hoặc 65 | // enum thay thế. -------------------------------------------------------------------------------- /2. Classes in Java/NestedClass.java: -------------------------------------------------------------------------------- 1 | import java.util.HashMap; 2 | import java.util.ArrayList; 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | // Lớp chồng nhau (nested class) là một lớp được định nghĩa bên trong một lớp khác. 7 | 8 | // Ví dụ: 9 | 10 | // ContactBook là danh bạ điện thoại, bao gồm danh sách các liên hệ và các nhóm. 11 | class ContactBook { 12 | public List contacts = new ArrayList<>(); 13 | public Map groups = new HashMap<>(); 14 | 15 | // Lớp Contact đại diện cho một liên hệ trong danh bạ, gồm tên và số điện thoại. 16 | /* 17 | * Tương tác với lớp bên ngoài: 18 | * - Các inner class có thể truy cập đầy đủ tới tất cả các phương thức và biến 19 | * của lớp chứa nó. 20 | * - Inner class thường không thể truy cập một cách trực tiếp từ bên ngoài lớp 21 | * chứa nó nếu không thông qua một instance của lớp chứa. 22 | * - Ví dụ, để tạo một instance của lớp Contact, ta cần thông qua một instance 23 | * của lớp ContactBook như sau 24 | * ContactBook myBook = new ContactBook(); 25 | * ContactBook.Contact contact = myBook.new Contact("Alice", "1234567890"); 26 | */ 27 | class Contact { 28 | private String name; 29 | private String phone; 30 | 31 | // Là một lớp, Contact cũng có hàm tạo. 32 | // Dùng this để tham chiếu đến biến của lớp Contact. 33 | public Contact(String name, String phone) { 34 | this.name = name; 35 | this.phone = phone; 36 | } 37 | 38 | public void displayContact() { 39 | System.out.println("Name: " + name + ", Phone: " + phone); 40 | } 41 | } 42 | 43 | // Lớp Group đại diện cho một nhóm trong danh bạ, bao gồm tên nhóm và danh sách 44 | // các thành viên. 45 | class Group { 46 | String name; 47 | List members = new ArrayList<>(); 48 | 49 | public Group(String name) { 50 | this.name = name; 51 | } 52 | 53 | public void addMember(Contact contactName) { 54 | members.add(contactName); 55 | } 56 | 57 | public void displayGroup() { 58 | System.out.println("Group: " + name); 59 | System.out.println("Members:"); 60 | for (Contact member : members) { 61 | System.out.println(member.name); 62 | } 63 | } 64 | } 65 | 66 | public void addContact(Contact contact) { 67 | contacts.add(contact); 68 | } 69 | 70 | public void addGroup(String name) { 71 | Group group = new Group(name); 72 | groups.put(name, group); 73 | } 74 | } 75 | 76 | // Lớp NestedClass chứa hàm main để chạy chương trình. 77 | public class NestedClass { 78 | public static void main(String[] args) { 79 | ContactBook myBook = new ContactBook(); 80 | 81 | ContactBook.Contact contact1 = myBook.new Contact("Alice", "1234567890"); 82 | ContactBook.Contact contact2 = myBook.new Contact("Bob", "0987654321"); 83 | 84 | myBook.addContact(contact1); 85 | myBook.addContact(contact2); 86 | 87 | myBook.addGroup("Friends"); 88 | myBook.groups.get("Friends").addMember(contact1); 89 | myBook.groups.get("Friends").addMember(contact2); 90 | 91 | System.out.println("_____________DISPLAY CONTACTS_____________"); 92 | for (ContactBook.Contact contact : myBook.contacts) { 93 | contact.displayContact(); 94 | } 95 | 96 | System.out.println("_____________DISPLAY GROUP_____________"); 97 | myBook.groups.get("Friends").displayGroup(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /2. Classes in Java/Reflection.java: -------------------------------------------------------------------------------- 1 | // Trong phần này, chúng ta sẽ tìm hiểu về Java Reflection API, hỗ trợ bởi gói java.lang.reflect. 2 | /* 3 | * - Reflection là khả năng của một lớp hoặc đối tượng để kiểm tra chính nó. 4 | * - Reflection cho phép mã Java nhìn vào đối tượng (hay chính xác hơn là lớp của đối tượng) và xác định cấu trúc của nó. 5 | * - Các phương thức trong class Class: 6 | * + Field[] getFields(): Lấy ra tất cả các biến public của class và các biến được kế thừa từ superclass. 7 | * + Field getField(String name): Lấy ra biến public có tên name của class hoặc được kế thừa từ superclass. 8 | * + Field[] getDeclaredFields(): Lấy ra tất cả các biến của class, bao gồm cả private, protected, default và public, nhưng không bao gồm biến của superclass. 9 | * + Field getDeclaredField(String name): Lấy ra biến có tên name của class, bao gồm cả private, protected, default và public, nhưng không bao gồm biến của superclass. 10 | * + Method[] getMethods(): Lấy ra tất cả các phương thức public của class và được kế thừa từ superclass. 11 | * + Method getMethod(String name, Class... parameterTypes): Lấy ra phương thức public có tên name và kiểu tham số parameterTypes của class hoặc được kế thừa từ superclass. 12 | * + Method[] getDeclaredMethods(): Lấy ra tất cả các phương thức của class, bao gồm cả private, protected, default và public, nhưng không bao gồm phương thức của superclass. 13 | * + Method getDeclaredMethod(String name, Class... parameterTypes): Lấy ra phương thức có tên name và kiểu tham số parameterTypes của class, bao gồm cả private, protected, default và public, nhưng không bao gồm phương thức của superclass. 14 | * + Constructor[] getConstructors(): Lấy ra tất cả các constructor public của class. 15 | * + Constructor getConstructor(Class... parameterTypes): Lấy ra constructor public với kiểu tham số parameterTypes của class. 16 | * + Constructor[] getDeclaredConstructors(): Lấy ra tất cả các constructor của class, bao gồm cả private, protected, default và public, nhưng không bao gồm constructor của superclass. 17 | * + Constructor getDeclaredConstructor(Class... parameterTypes): Lấy ra constructor với kiểu tham số parameterTypes của class, bao gồm cả private, protected, default và public, nhưng không bao gồm constructor của superclass. 18 | * + Class[] getInterfaces(): Lấy ra tất cả các interface mà class này triển khai. 19 | * + Class[] getDeclaredClasses(): Lấy ra tất cả các inner class được khai báo bên trong class này. 20 | */ 21 | 22 | import java.lang.reflect.Field; 23 | import java.lang.reflect.Method; 24 | 25 | @SuppressWarnings("all") 26 | class Dinosaur { 27 | 28 | public String species; 29 | 30 | Dinosaur(String species) { 31 | this.species = species; 32 | } 33 | 34 | public void roar() { 35 | System.out.println("Roar"); 36 | } 37 | 38 | private void eat() { 39 | System.out.println("Eat"); 40 | } 41 | 42 | protected void sleep() { 43 | System.out.println("Sleep"); 44 | } 45 | } 46 | 47 | @SuppressWarnings("all") 48 | class BabyDinosaur extends Dinosaur { 49 | 50 | private int weight; 51 | private int age; 52 | 53 | BabyDinosaur(String species, int weight, int age) { 54 | super(species); 55 | this.weight = weight; 56 | this.age = age; 57 | } 58 | 59 | public void play() { 60 | System.out.println("Play"); 61 | } 62 | 63 | private void crawl() { 64 | System.out.println("Crawl"); 65 | } 66 | 67 | protected void run() { 68 | System.out.println("Run"); 69 | } 70 | } 71 | 72 | public class Reflection { 73 | public static void main(String[] args) { 74 | Class dinosaur = Dinosaur.class; 75 | 76 | System.out.println("--Methods of Dinosaur class"); 77 | Method[] methods = dinosaur.getMethods(); 78 | for (Method method : methods) { 79 | System.out.println(method.getName()); 80 | } 81 | 82 | System.out.println("--Fields of Dinosaur class"); 83 | Field[] fields = dinosaur.getFields(); 84 | for (Field field : fields) { 85 | System.out.println(field.getName()); 86 | } 87 | 88 | Class babyDinosaur = BabyDinosaur.class; 89 | 90 | System.out.println("--Fields of BabyDinosaur class"); 91 | Field[] subfields = babyDinosaur.getFields(); 92 | for (Field field : subfields) { 93 | System.out.println(field.getName()); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /2. Classes in Java/StaticInnerClass.java: -------------------------------------------------------------------------------- 1 | // Tiếp tục về lớp chồng nhau (nested class, inner class). 2 | // 3. Static Inner Classes 3 | /* 4 | * - Một inner class có thể được định nghĩa là static. 5 | * - Một static inner class không yêu cầu một instance của lớp bên ngoài để tồn tại. Nó có thể tồn tại độc lập với lớp chứa nó. 6 | * - Static inner class có thể được sử dụng giống như một lớp độc lập ở cấp cao nhất (top-level class). 7 | * -> Bạn có thể tạo một instance của static inner class mà không cần một thể hiện của lớp bên ngoài. 8 | * - static inner class giúp cấu trúc mã nguồn gọn gàng hơn bằng cách nhóm các lớp liên quan mật thiết vào với nhau, 9 | * thay vì phải tạo nhiều lớp độc lập, từ đó làm tăng số lượng lớp trong gói. 10 | */ 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | // Xét ví dụ sau: 16 | /* 17 | * - Class Department chứa một static inner class Employee. 18 | * - Department quản lý thông tin về một phòng ban trong công ty, bao gồm tên phòng ban và danh sách nhân viên. 19 | * - Employee chứa thông tin về một nhân viên, bao gồm tên, mã số và chức vụ. 20 | */ 21 | 22 | class Department { 23 | private String name; 24 | private List employees; 25 | 26 | public Department(String name) { 27 | this.name = name; 28 | this.employees = new ArrayList<>(); 29 | } 30 | 31 | public void addEmployee(String empName, String empId, String position) { 32 | Employee newEmployee = new Employee(empName, empId, position); 33 | employees.add(newEmployee); 34 | } 35 | 36 | public List getEmployees() { 37 | return employees; 38 | } 39 | 40 | // Static inner class 41 | public static class Employee { 42 | private String name; 43 | private String id; 44 | private String position; 45 | 46 | public Employee(String name, String id, String position) { 47 | this.name = name; 48 | this.id = id; 49 | this.position = position; 50 | } 51 | 52 | public String getName() { 53 | return name; 54 | } 55 | 56 | public String getId() { 57 | return id; 58 | } 59 | 60 | public String getPosition() { 61 | return position; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return "Employee{" + 67 | "name='" + name + '\'' + 68 | ", id='" + id + '\'' + 69 | ", position='" + position + '\'' + 70 | '}'; 71 | } 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "Department{" + 77 | "name='" + name + '\'' + 78 | ", employees=" + employees + 79 | '}'; 80 | } 81 | } 82 | 83 | public class StaticInnerClass { 84 | public static void main(String[] args) { 85 | Department itDepartment = new Department("IT"); 86 | itDepartment.addEmployee("John", "0001", "Developer"); 87 | itDepartment.addEmployee("Jane", "0002", "Designer"); 88 | 89 | System.out.println(itDepartment); 90 | 91 | // Khởi tạo Employee không thông qua Department 92 | Department.Employee manager = new Department.Employee("Alice", "0010", "Manager"); 93 | System.out.println("Manager: " + manager); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /2. Classes in Java/SubInterface.java: -------------------------------------------------------------------------------- 1 | // Một interface có thể extends một hoặc nhiều interface khác. 2 | // Điều này giúp chúng ta tạo ra một cấu trúc phân cấp của các interface, giúp chúng ta quản lý và sắp xếp các hành vi một cách có tổ chức. 3 | 4 | /* 5 | * - Lưu ý, khác với class chỉ kế thừa từ một class cha duy nhất, một class có thể 6 | * implements nhiều interface; còn một interface có thể extends nhiều interface khác. 7 | * - Một interface không thể kế thừa từ class khác. 8 | * - Một class implement một interface thì cũng phải implement tất cả interface mà 9 | * interface đó extends. 10 | */ 11 | 12 | // Ví dụ: 13 | 14 | // Interface Vehicle định nghĩa các hành vi cơ bản của một phương tiện giao thông. 15 | interface Vehicle { 16 | void start(); 17 | 18 | void stop(); 19 | } 20 | 21 | // Interface FlyingVehicle định nghĩa các hành vi của một phương tiện bay. 22 | interface FlyingVehicle extends Vehicle { 23 | void fly(); 24 | 25 | void land(); 26 | } 27 | 28 | // Interface LandVehicle định nghĩa các hành vi của một phương tiện đường bộ. 29 | interface LandVehicle extends Vehicle { 30 | void drive(); 31 | 32 | void brake(); 33 | } 34 | 35 | // Interface AmphibiousVehicle kế thừa từ cả FlyingVehicle và LandVehicle, định 36 | // nghĩa 37 | // thêm hành vi floatOnWater(). 38 | interface AmphibiousVehicle extends FlyingVehicle, LandVehicle { 39 | void floatOnWater(); 40 | } 41 | 42 | // Lớp HybridDrone thực thi interface AmphibiousVehicle. 43 | // -> Nó phải override tất cả các phương thức của các interface mà nó implement. 44 | class HybridDrone implements AmphibiousVehicle { 45 | @Override 46 | public void start() { 47 | System.out.println("HybridDrone is starting..."); 48 | } 49 | 50 | @Override 51 | public void stop() { 52 | System.out.println("HybridDrone is stopping..."); 53 | } 54 | 55 | @Override 56 | public void fly() { 57 | System.out.println("HybridDrone is flying..."); 58 | } 59 | 60 | @Override 61 | public void land() { 62 | System.out.println("HybridDrone is landing..."); 63 | } 64 | 65 | @Override 66 | public void drive() { 67 | System.out.println("HybridDrone is driving..."); 68 | } 69 | 70 | @Override 71 | public void brake() { 72 | System.out.println("HybridDrone is braking..."); 73 | } 74 | 75 | @Override 76 | public void floatOnWater() { 77 | System.out.println("HybridDrone is floating on water..."); 78 | } 79 | } 80 | 81 | public class SubInterface { 82 | public static void main(String[] args) { 83 | HybridDrone drone = new HybridDrone(); 84 | drone.start(); 85 | drone.fly(); 86 | drone.land(); 87 | drone.drive(); 88 | drone.brake(); 89 | drone.floatOnWater(); 90 | drone.stop(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /2. Classes in Java/Subclass.java: -------------------------------------------------------------------------------- 1 | @SuppressWarnings("unused") 2 | class Superclass { 3 | int superVar = 5; 4 | private int privateVar = 10; 5 | 6 | void display() { 7 | System.out.println("This is the display method of superclass"); 8 | } 9 | 10 | void print() { 11 | System.out.println("This is the print method of superclass"); 12 | } 13 | } 14 | 15 | public class Subclass extends Superclass { 16 | // Lớp trong Java tồn tại trong một hệ thống phân cấp. 17 | /* 18 | * - Một lớp có thể kế thừa từ một lớp khác, được gọi là lớp cha (superclass). 19 | * - Lớp con (subclass) kế thừa tất cả các thuộc tính và phương thức (mà không 20 | * phải private) của lớp cha và có thể sử dụng chúng như thể chúng được khai báo 21 | * ngay trong chính lớp đó. 22 | * - Sử dụng từ khóa extends để kế thừa từ một lớp cha. 23 | */ 24 | 25 | // Một class chỉ có thể kế thừa từ một class khác. 26 | 27 | // Shadowed variable (biến bị che phủ) là một biến cục bộ hoặc biến của lớp 28 | // con có cùng tên với một biến của lớp cha. 29 | // Biến của lớp con sẽ che phủ biến của lớp cha. 30 | 31 | double superVar = 10; 32 | 33 | // Gọi đến biến superVar của lớp cha -> dùng tham chiếu super 34 | int var = super.superVar; 35 | 36 | // Lớp con có thể định nghĩa thêm thuộc tính và phương thức của riêng mình. 37 | // Lớp con cũng có thể ghi đè (override) phương thức của lớp cha. 38 | 39 | // Ghi đè phương thức print của lớp cha. 40 | @Override 41 | void print() { 42 | System.out.println("This is the print method of subclass"); 43 | } 44 | 45 | // Sử dụng annotation @Override có tác dụng kiểm tra xem phương thức có thực sự 46 | // ghi đè phương thức của lớp cha hay không. 47 | 48 | public static void main(String[] args) { 49 | Subclass subObj = new Subclass(); 50 | 51 | // Khi một lớp con kế thừa từ lớp cha, đối tượng của lớp con có thể được gán 52 | // cho một biến của lớp cha. -> Tính đa hình (Polymorphism) 53 | Superclass superObj = subObj; 54 | 55 | System.out.println("shadowed superVar: " + subObj.superVar); 56 | // -> 10 (biến của lớp con che phủ biến của lớp cha) 57 | 58 | System.out.println("superVar through super.superVar: " + subObj.var); // -> 5 59 | 60 | // System.out.println("privateVar: " + obj.privateVar); 61 | // Lỗi: privateVar has private access in Superclass 62 | 63 | subObj.display(); // -> This is the display method of superclass 64 | subObj.print(); // -> This is the print method of subclass 65 | 66 | superObj.print(); // -> This is the print method of subclass (gọi phương thức của lớp con) 67 | 68 | // Nếu là lớp cha 69 | Superclass obj = new Superclass(); 70 | obj.print(); // -> This is the print method of superclass 71 | 72 | Subsubclass subsubObj = new Subsubclass(); 73 | 74 | System.out.println("superVar of Subsubclass: " + subsubObj.superVar); 75 | // -> 10.0 76 | } 77 | 78 | } 79 | 80 | // Lớp con có thể được tiếp tục kế thừa bởi một lớp khác. 81 | class Subsubclass extends Subclass { 82 | // Lớp SubSubclass kế thừa tất cả thuộc tính và phương thức của lớp Subclass. 83 | // Lớp SubSubclass cũng có thể định nghĩa thêm thuộc tính và phương thức của 84 | // riêng mình. 85 | 86 | double var = super.superVar; 87 | } 88 | 89 | // Phương thức đè (overridden method) trông như là chúng che phủ phương thức của 90 | // lớp cha, như đối với biến. 91 | /* 92 | * Nhưng một phương thức đè thực sự mạnh hơn thế: 93 | * - Khi có nhiều triển khai của 1 phương thức trong hệ thống phân cấp của đối 94 | * tượng, phương thức nào thuộc lớp "có nguồn gốc gần nhất" (hay là thấp nhất 95 | * trong hệ thống phân cấp) luôn luôn ghi đè tất cả các phương thức trước đó. 96 | * - Tham chiếu this, super trong phương thức đè sẽ lần lượt là tham chiếu của 97 | * lớp hiện tại và lớp cha của lớp hiện tại. 98 | */ 99 | -------------------------------------------------------------------------------- /2. Classes in Java/SuperConstructor.java: -------------------------------------------------------------------------------- 1 | class Person { 2 | String name; 3 | int age; 4 | 5 | Person(String name, int age) { 6 | this.name = name; 7 | this.age = age; 8 | } 9 | } 10 | 11 | class Student extends Person { 12 | int studentID; 13 | 14 | Student(String name, int age, int studentID) { 15 | super(name, age); 16 | // Gọi constructor của lớp cha (Person) để khởi tạo name và age. 17 | // Phương thức khởi tạo này phải được gọi đầu tiên trong constructor của lớp 18 | // con. 19 | this.studentID = studentID; 20 | } 21 | } 22 | 23 | class Teacher extends Person { 24 | String subject; 25 | 26 | Teacher(String name, int age, String subject) { 27 | super(name, age); 28 | this.subject = subject; 29 | // super(name, age); // -> báo lỗi nếu không đặt dòng này ở đầu constructor 30 | } 31 | 32 | Teacher(String name, int age) { 33 | this(name, age, "Professional"); // -> gọi constructor khác trong cùng lớp 34 | } 35 | } 36 | 37 | public class SuperConstructor { 38 | public static void main(String[] args) { 39 | Student student = new Student("John", 21, 1001); 40 | Teacher teacher = new Teacher("Alice", 30, "Math"); 41 | 42 | System.out.println("Student: " + student.name + " - " + student.age + " - " + student.studentID); 43 | System.out.println("Teacher: " + teacher.name + " - " + teacher.age + " - " + teacher.subject); 44 | } 45 | } 46 | 47 | // Quy tắc đầy đủ: phương thức tạo (constructor) và khởi tạo (initialization). 48 | /* 49 | * - Nếu constructor không bắt đầu bằng một lời gọi đến constructor khác của lớp 50 | * hiện tại (qua this()) hoặc lớp cha (qua super()), Java sẽ tự động chèn một 51 | * lời gọi ngầm định đến constructor mặc định (không có tham số) của lớp cha 52 | * (super()). 53 | * - Nếu constructor bắt đầu bằng một lời gọi đến constructor của lớp cha qua 54 | * super(), điều này cho phép truyền các tham số cụ thể và chọn constructor phù 55 | * hợp của lớp cha để gọi. 56 | * - Nếu constructor bắt đầu bằng một lời gọi đến constructor khác trong cùng 57 | * lớp qua this(), điều này cho phép tái sử dụng mã trong các constructor khác 58 | * nhau. 59 | */ -------------------------------------------------------------------------------- /2. Classes in Java/TheClassClass.java: -------------------------------------------------------------------------------- 1 | // Trong Java, mỗi class tại thời điểm chạy đều được biểu diễn bởi 1 instance của class java.lang.Class. 2 | /* 3 | * - Class này cung cấp các phương thức để truy cập thông tin về class như tên class, các fields, methods, constructors, ... 4 | * - Mỗi loại đối tượng bạn sử dụng có một đối tượng Class tương ứng, và đối tượng này có trách nhiệm tạo ra cácinstance của loại đó. 5 | */ 6 | 7 | // Ví dụ sau: 8 | 9 | @SuppressWarnings("rawtypes") 10 | public class TheClassClass { 11 | public static void main(String[] args) { 12 | // Một đối tượng của class String 13 | String str = "Hello"; 14 | 15 | // Lấy ra class của đối tượng str 16 | Class strClass = str.getClass(); 17 | 18 | // Cung có thể tham chiếu trực tiếp tới class thông qua tên class 19 | Class strClass2 = String.class; 20 | 21 | // In ra tên class của đối tượng str 22 | System.out.println(strClass.getName()); // In ra: java.lang.String 23 | System.out.println(strClass2.getName()); // In ra: java.lang.String 24 | 25 | // Phương thức forName() của class Class dùng để tạo một đối tượng Class từ tên 26 | // class 27 | try { 28 | Class strClass3 = Class.forName("java.lang.String"); 29 | System.out.println(strClass3.getName()); // In ra: java.lang.String 30 | } catch (ClassNotFoundException e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /2. Classes in Java/TheObjectClass.java: -------------------------------------------------------------------------------- 1 | // Trong Java, mọi lớp đều kế thừa từ lớp Object. 2 | /* - Lớp Object là lớp cha của tất cả các lớp trong Java. 3 | * - Nó chứa các phương thức mà mọi lớp kế thừa từ nó. 4 | * - Lớp Object cung cấp một số phương thức mặc định, nhưng chúng có thể được ghi đè bởi các lớp con để cung cấp cài đặt cụ thể. 5 | * - Dưới đây là một số phương thức static quang trọng của lớp Object: 6 | * + Object clone(): Trả về một bản sao của đối tượng hiện tại. 7 | * + boolean equals(Object obj): So sánh hai đối tượng. 8 | * + void finalize(): Được gọi trước khi một đối tượng bị thu hồi bởi garbage collector. 9 | * + Class getClass(): Trả về đối tượng Class của đối tượng hiện tại. 10 | * + int hashCode(): Trả về mã băm của đối tượng. 11 | * + void notify(): Thức tỉnh một luồng đang chờ đợi trên đối tượng hiện tại. 12 | * + void notifyAll(): Thức tỉnh tất cả các luồng đang chờ đợi trên đối tượng hiện tại. 13 | * + String toString(): Trả về chuỗi biểu diễn của đối tượng. 14 | * + void wait(): Chờ cho đến khi một luồng khác gọi notify() hoặc notifyAll() trên đối tượng hiện tại. 15 | */ 16 | 17 | // Ví dụ về phương thức toString(): 18 | class Pet { 19 | private String name; 20 | private String species; 21 | private int age; 22 | 23 | public Pet(String name, String species, int age) { 24 | this.name = name; 25 | this.species = species; 26 | this.age = age; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "Pet [name=" + name + ", species=" + species + ", age=" + age + "]"; 32 | } 33 | } 34 | 35 | @SuppressWarnings("unused") 36 | class Cat { 37 | private String name; 38 | private int age; 39 | 40 | public Cat(String name, int age) { 41 | this.name = name; 42 | this.age = age; 43 | } 44 | } 45 | 46 | public class TheObjectClass { 47 | public static void main(String[] args) { 48 | Pet pet = new Pet("Tom", "Cat", 3); 49 | System.out.println(pet.toString()); 50 | 51 | // Lớp Object cung cấp phương thức toString() mặc định. 52 | // Khi không ghi đè phương thức này, 53 | // kết quả sẽ là: .@<địa chỉ bộ nhớ> 54 | Object obj = new Object(); 55 | System.out.println(obj.toString()); 56 | 57 | Cat cat = new Cat("Tom", 3); 58 | System.out.println(cat.toString()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /2. Classes in Java/WithinMethods.java: -------------------------------------------------------------------------------- 1 | // Tiếp tục về nội lớp. 2 | // 2. Nội lớp (inner class) còn có thể được định nghĩa trong một phương thức. 3 | /* 4 | * Quyền truy cập: 5 | * - Nội lớp có thể truy cập tất cả các biến và đối số của phương thức chứa nó. 6 | * - Nội lớp cũng có thể truy cập các thành viên của lớp chứa phương thức ấy. 7 | * Phạm vi sử dụng: Nội lớp chỉ có thể được sử dụng trong phương thức chứa nó. 8 | */ 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | // Ví dụ: 14 | 15 | // Lớp Car đại diện cho một chiếc xe, bao gồm trạng thái của động cơ và mức nhiên liệu. 16 | // Chứa phương thức startEngine() để khởi động động cơ. 17 | class Car { 18 | private boolean engineRunning = false; 19 | private int fuelLevel = 0; 20 | 21 | public Car(int fuelLevel) { 22 | this.fuelLevel = fuelLevel; 23 | } 24 | 25 | public void startEngine() { 26 | // Nội lớp EngineStarter kiểm tra mức nhiên liệu trước khi khởi động động cơ. 27 | class EngineStarter { 28 | private void checkFuel() { 29 | if (fuelLevel <= 0) { 30 | throw new IllegalStateException("Cannot start engine: No fuel"); 31 | } 32 | } 33 | 34 | private void initiateStartup() { 35 | engineRunning = true; 36 | System.out.println("Engine is now running."); 37 | } 38 | 39 | public void start() { 40 | checkFuel(); 41 | initiateStartup(); 42 | } 43 | } 44 | 45 | /* 46 | * - EngineStarter có thể truy cập các thành viên nội bộ của Car và ngược lại, 47 | * nhưng không gian bên ngoài không thể truy cập trực tiếp đến EngineStarter. 48 | * - EngineStarter làm cho việc khởi động động cơ trở nên riêng biệt với phần 49 | * còn lại của lớp Car, giúp giảm sự phức tạp của phương thức startEngine() và 50 | * tập trung vào một nhiệm vụ cụ thể. 51 | */ 52 | 53 | EngineStarter starter = new EngineStarter(); 54 | starter.start(); 55 | } 56 | 57 | public boolean isEngineRunning() { 58 | return engineRunning; 59 | } 60 | } 61 | 62 | public class WithinMethods { 63 | public static void main(String[] args) { 64 | Car myCar = new Car(10); // Khởi tạo xe với 10 đơn vị nhiên liệu 65 | myCar.startEngine(); // Khởi động động cơ 66 | System.out.println("Is the engine running? -> " + myCar.isEngineRunning()); 67 | 68 | Car anotherCar = new Car(0); // Khởi tạo xe với 0 đơn vị nhiên liệu 69 | anotherCar.startEngine(); // Khởi động động cơ 70 | System.out.println("Is the engine running? -> " + anotherCar.isEngineRunning()); 71 | // -> IllegalStateException: Cannot start engine: No fuel 72 | 73 | System.out.println("--------------------------------------------------"); 74 | 75 | EventNotifier notifier = new EventNotifier(); 76 | notifier.addUser("Alice"); 77 | notifier.addUser("Bob"); 78 | 79 | // Gửi thông điệp 80 | notifier.notifyUsers("System will be down for maintenance tonight."); 81 | } 82 | } 83 | 84 | // Có một số hạn chế khi sử dụng nội lớp trong phương thức: 85 | /* 86 | * - Thời gian sống của biến: Khi một phương thức kết thúc, các biến địa phương 87 | * (local) của nó thường biến mất. Tuy nhiên, bất kỳ đối tượng nào được tạo ra 88 | * trong phương thức, bao gồm các instance của nội lớp vẫn tồn tại cho đến khi 89 | * chúng không còn được tham chiếu. Điều này dẫn đến, bất kỳ biến địa phương nào 90 | * được sử dụng trong nội lớp cũng phải tồn tại sau khi phuơng thức kết thúc. 91 | * -> Để đảm bảo các biến địa phương có thể được sử dụng an toàn bởi nội lớp sau 92 | * khi phương thức kết thúc, chúng phải được khai báo là final hoặc effectively 93 | * final. 94 | */ 95 | 96 | // Ví dụ khác: 97 | 98 | // Lớp EventNotifier thông báo một thông điệp cụ thể đến một danh sách người 99 | // dùng. 100 | class EventNotifier { 101 | private List userList = new ArrayList<>(); 102 | 103 | // Phương thức thêm người dùng vào danh sách thông báo 104 | public void addUser(String user) { 105 | userList.add(user); 106 | } 107 | 108 | // Phương thức thông báo người dùng với một thông điệp cụ thể 109 | public void notifyUsers(final String message) { 110 | // Inner class để gửi thông báo 111 | class Notifier { 112 | // Phương thức gửi thông điệp 113 | public void sendNotification() { 114 | for (String user : userList) { 115 | System.out.println("Notifying " + user + ": " + message); 116 | } 117 | } 118 | } 119 | 120 | // Tạo một instance của inner class và sử dụng nó 121 | Notifier notifier = new Notifier(); 122 | notifier.sendNotification(); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /3. Core Ultilities/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/3. Core Ultilities/.gitkeep -------------------------------------------------------------------------------- /3. Core Ultilities/Collections.java: -------------------------------------------------------------------------------- 1 | public class Collections { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /3. Core Ultilities/Iteration.java: -------------------------------------------------------------------------------- 1 | public class Iteration { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /3. Core Ultilities/LoggingAPI.java: -------------------------------------------------------------------------------- 1 | public class LoggingAPI { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /3. Core Ultilities/Math.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/3. Core Ultilities/Math.java -------------------------------------------------------------------------------- /3. Core Ultilities/Properties.java: -------------------------------------------------------------------------------- 1 | public class Properties { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /3. Core Ultilities/Random.java: -------------------------------------------------------------------------------- 1 | public class Random { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /3. Core Ultilities/StringTips.java: -------------------------------------------------------------------------------- 1 | // String trong Java là một class trong Java. 2 | /* Tính chất của String: 3 | * - String là bất biến (immutable), không thể thay đổi giá trị của String sau khi đã tạo. 4 | * - Mỗi khi cố gắng thay đổi giá trị của String, một đối tượng mới sẽ được tạo ra. 5 | * - String được lưu trữ trong String Pool, một vùng nhớ đặc biệt trong bộ nhớ Heap. 6 | * Khi một chuỗi được tạo thông qua dấu ngoặc kép mà không sử dụng từ khóa new, Java sẽ 7 | * kiểm tra xem chuỗi đó đã tồn tại trong Pool chưa. Nếu có, Java sẽ sử dụng lại đối tượng đó thay vì tạo mới. 8 | * - String là final class, không thể kế thừa. 9 | * - Các phương thức thông dụng của lớp String: 10 | * # Kiểm tra, so sánh: 11 | * + boolean equals(Object obj): So sánh chuỗi hiện tại với chuỗi obj. 12 | * + boolean equalsIgnoreCase(String anotherString): So sánh chuỗi hiện tại với chuỗi anotherString, không phân biệt chữ hoa chữ thường. 13 | * + int compareTo(String str): So sánh chuỗi hiện tại với chuỗi str, theo thứ tự từ điển. 14 | * + int compareToIgnoreCase(String str): So sánh chuỗi hiện tại với chuỗi str, không phân biệt chữ hoa chữ thường. 15 | * + boolean startsWith(String prefix): Kiểm tra xem chuỗi có bắt đầu bằng tiền tố chỉ định không. 16 | * + boolean endsWith(String suffix): Kiểm tra xem chuỗi có kết thúc bằng hậu tố chỉ định không. 17 | * + boolean contains(CharSequence s): Kiểm tra xem chuỗi có chứa chuỗi s không. 18 | * # Tìm kiếm, truy xuất: 19 | * + int indexOf(int ch): Trả về vị trí đầu tiên của ký tự ch trong chuỗi. 20 | * + int lastIndexOf(int ch): Trả về vị trí cuối cùng của ký tự ch trong chuỗi. 21 | * + String substring(int beginIndex, int endIndex): Trả về chuỗi con từ vị trí beginIndex đến vị trí endIndex - 1. 22 | * + char charAt(int index): Trả về ký tự ở vị trí index trong chuỗi. 23 | * # Chỉnh sửa, thay thế: 24 | * + String replace(char oldChar, char newChar): Thay thế tất cả các ký tự oldChar trong chuỗi bằng ký tự newChar. 25 | * + String trim(): Xóa khoảng trắng ở đầu và cuối chuỗi. 26 | * # Chuyển đổi: 27 | * + String toLowerCase(): Chuyển đổi chuỗi thành chữ thường. 28 | * + String toUpperCase(): Chuyển đổi chuỗi thành chữ hoa. 29 | * + String valueOf(int i): Chuyển đổi một số nguyên sang chuỗi. 30 | */ 31 | 32 | // StringBuffer là một lớp trong Java được sử dụng để tạo và quản lý chuỗi có thể thay đổi (mutable). 33 | /* Tính chất của StringBuffer: 34 | * - StringBuffer là mutable, có thể thay đổi giá trị của chuỗi sau khi đã tạo. 35 | * - StringBuffer được lưu trữ trong bộ nhớ Heap. 36 | * - Hiệu quả hơn String khi thực hiện nhiều thao tác chỉnh sửa chuỗi do không tạo ra nhiều đối tượng mới mỗi lần chỉnh sửa. 37 | * - Các phương thức thông dụng của lớp StringBuffer: 38 | * # Thêm, chèn: 39 | * + StringBuffer append(String s): Thêm chuỗi s vào cuối chuỗi hiện tại. 40 | * + StringBuffer insert(int offset, String s): Chèn chuỗi s vào vị trí offset trong chuỗi hiện tại. 41 | * # Xóa, thay thế: 42 | * + StringBuffer delete(int startIndex, int endIndex): Xóa chuỗi con từ vị trí startIndex đến vị trí endIndex - 1. 43 | * + StringBuffer deleteCharAt(int index): Xóa ký tự ở vị trí index. 44 | * + StringBuffer replace(int startIndex, int endIndex, String s): Thay thế chuỗi con từ vị trí startIndex đến vị trí endIndex - 1 bằng chuỗi s. 45 | * # Đảo ngược: 46 | * + StringBuffer reverse(): Đảo ngược chuỗi. 47 | * # Chuyển đổi: 48 | * + String toString(): Chuyển đổi StringBuffer thành String. 49 | * + StringBuffer append(int i): Thêm một số nguyên vào cuối chuỗi. 50 | * + StringBuffer append(double d): Thêm một số thực vào cuối chuỗi. 51 | * ... 52 | */ 53 | 54 | @SuppressWarnings("unused") 55 | public class StringTips { 56 | public static void main(String[] args) { 57 | System.out.println("--- String"); 58 | // Khởi tạo String 59 | String str1 = "Hello World!"; 60 | String str2 = "Hello World!"; 61 | 62 | // So sánh String 63 | System.out.println(str1 == str2); // true (vì str1 và str2 cùng trỏ đến một đối tượng trong String Pool) 64 | System.out.println(str1.equals(str2)); // true 65 | 66 | String str3 = new String("Hello World!"); // -> dùng new thì sẽ tạo ra một đối tượng mới trong Heap 67 | 68 | System.out.println(str1 == str3); // false (vì str3 trỏ đến một đối tượng mới được tạo ra trong Heap) 69 | 70 | // Chuyển sang chữ hoa 71 | str1.toUpperCase(); 72 | System.out.println(str1); // Hello World! (vì String là immutable, nên giá trị của str1 không thay đổi) 73 | System.out.println(str1.toUpperCase()); // HELLO WORLD! 74 | 75 | System.out.println("--- String Buffer"); 76 | // String Buffer 77 | // StringBuffer sb1 = "Hello World!"; // -> lỗi vì không thể gán một String vào 78 | // một StringBuffer trực tiếp 79 | StringBuffer sb1 = new StringBuffer("Hello"); 80 | 81 | // Thêm chuỗi vào cuối 82 | sb1.append(" World!"); 83 | System.out.println(sb1); // Hello World! (vì StringBuffer là mutable, nên giá trị của sb1 thay đổi) 84 | 85 | System.out.println("--- Compare String and StringBuffer"); 86 | 87 | // So sánh hiệu suất giữa String và StringBuffer 88 | String str = "Hello"; 89 | 90 | long start = System.currentTimeMillis(); 91 | for (int i = 0; i < 10000; i++) { 92 | str += "World"; 93 | } 94 | long end = System.currentTimeMillis(); 95 | 96 | System.out.println("Time for String: " + (end - start) + "ms"); // -> Khoảng 100 ms 97 | 98 | StringBuffer sb = new StringBuffer("Hello"); 99 | 100 | start = System.currentTimeMillis(); 101 | for (int i = 0; i < 10000; i++) { 102 | sb.append("World"); 103 | } 104 | end = System.currentTimeMillis(); 105 | 106 | System.out.println("Time for StringBuffer: " + (end - start) + "ms"); // -> Chỉ khoảng dưới 1 ms 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /3. Core Ultilities/Time.java: -------------------------------------------------------------------------------- 1 | public class Time { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /4. IO Facilities/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/4. IO Facilities/.gitkeep -------------------------------------------------------------------------------- /5. Data Structures and Algorithms/MaxSubarray.java: -------------------------------------------------------------------------------- 1 | public class MaxSubarray { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /5. Data Structures and Algorithms/SumOfArray.java: -------------------------------------------------------------------------------- 1 | // Đề bài: Tính tổng của mảng số nguyên 2 | // Đầu vào: n, mảng số nguyên arr[] chứa n phần tử. 3 | // Đầu ra: Tổng của mảng arr[]. 4 | 5 | /* Nhận xét: 6 | * - Nếu sử dụng scanner để đọc dữ liệu input sẽ rất chậm và bị TLE. 7 | * -> Sử dụng BufferedReader để đọc dữ liệu input. 8 | * - BufferedReader là một class trong Java để đọc dữ liệu từ input stream (như file, console, socket). 9 | * - Đọc dữ liệu từ input stream nhanh hơn so với Scanner. 10 | * - Đọc dữ liệu từ input stream theo dòng (line) bằng phương thức readLine(). 11 | * - Để đọc dữ liệu từ input stream, cần tạo một đối tượng InputStreamReader để đọc dữ liệu từ input stream. 12 | * - Để tạo một đối tượng BufferedReader, cần truyền một đối tượng InputStreamReader vào constructor của BufferedReader. 13 | */ 14 | 15 | import java.io.BufferedReader; 16 | import java.io.IOException; 17 | import java.io.InputStreamReader; 18 | 19 | public class SumOfArray { 20 | // throws IOException có tác dụng bắt lỗi nếu có ngoại lệ xảy ra 21 | public static void main(String[] args) throws IOException { 22 | // Sử dụng BufferedReader để đọc dữ liệu từ bàn phím nhanh hơn 23 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 24 | 25 | // Sử dụng phương thức parseInt để chuyển chuỗi thành số nguyên 26 | int n = Integer.parseInt(reader.readLine()); 27 | 28 | long sum = 0; 29 | // Sử dụng phương thức split để tách chuỗi thành mảng 30 | String[] arr = reader.readLine().split(" "); 31 | 32 | for (int i = 0; i < n; i++) { 33 | sum += Long.parseLong(arr[i]); 34 | } 35 | 36 | System.out.println(sum); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /5. Data Structures and Algorithms/SumTwoInts.java: -------------------------------------------------------------------------------- 1 | import java.math.BigInteger; 2 | import java.util.Scanner; 3 | 4 | // Đề bài: Cộng 2 số nguyên a, b (0 <= a, b <= 10^19). 5 | // Đầu vào: 2 số nguyên a, b. 6 | // Đầu ra: Tổng của 2 số nguyên a, b. 7 | 8 | // Nhận xét: 9 | /* - Tổng của 2 số nằm trong khoảng từ 0 đến 2 * 10^19. 10 | * - Kiểu số nguyên trong Java lớn nhất là Long (64 bit) với giá trị tối đa là 2^63 - 1 < 2 * 10^19. 11 | * - Do đó, không thể lưu trữ số nguyên lớn trong kiểu dữ liệu Long. 12 | * -> Sử dụng class BigInteger trong Java để lưu trữ số nguyên lớn. 13 | * - Class này cung cấp một số phương thức để thực hiện các phép toán số học trên số nguyên lớn. 14 | * + add(BigInteger val) -> (this + val): Tổng. 15 | * + subtract(BigInteger val) -> (this - val): Hiệu. 16 | * + multiply(BigInteger val) -> (this * val): Tích. 17 | * + divide(BigInteger val) -> (this / val): Thương (phép chia nguyên). 18 | * + mod(BigInteger val) -> (this % val): Phần dư. 19 | * + pow(int exponent) -> (this ^ exponent): Lũy thừa. 20 | * + abs() -> |this|: Trị tuyệt đối. 21 | * ... 22 | */ 23 | 24 | // ################################################################# 25 | // ################ Sum of Two Integers in Java ################### 26 | // ################################################################# 27 | public class SumTwoInts { 28 | // Cộng hai số nguyên lớn (BigInteger) trong Java: 29 | 30 | public static void main(String[] args) { 31 | Scanner scanner = new Scanner(System.in); 32 | 33 | System.out.println("--Using BigInteger class"); 34 | BigInteger num1 = scanner.nextBigInteger(); 35 | BigInteger num2 = scanner.nextBigInteger(); 36 | 37 | // Cộng hai số nguyên lớn 38 | BigInteger sum = num1.add(num2); 39 | 40 | System.out.println("Sum: " + sum); 41 | 42 | System.out.println("--Using SumByString"); 43 | String num1Str = scanner.next(); 44 | String num2Str = scanner.next(); 45 | 46 | SumByString sumByString = new SumByString(num1Str, num2Str); 47 | System.out.println("Sum: " + sumByString.add()); 48 | 49 | scanner.close(); 50 | } 51 | } 52 | 53 | // Sử dụng phép cộng thông thường số nguyên (nhập vào duới dạng chuỗi kí tự). 54 | /* 55 | * - Đọc 2 số nguyên a, b vào 2 chuỗi kí tự. 56 | * - Thực hiện phép cộng từ hàng đơn vị đến hàng cao nhất. 57 | */ 58 | class SumByString { 59 | private String num1; 60 | private String num2; 61 | 62 | public SumByString(String num1, String num2) { 63 | this.num1 = num1; 64 | this.num2 = num2; 65 | } 66 | 67 | public String add() { 68 | StringBuilder result = new StringBuilder(); 69 | int carry = 0; // Số nhớ 70 | int i = num1.length() - 1; // Vị trí hàng đơn vị của số thứ nhất, là (length - 1) vì index bắt đầu từ 0 71 | int j = num2.length() - 1; 72 | 73 | while (i >= 0 || j >= 0) { 74 | int sum = carry; 75 | // Cộng từ hàng đơn vị đến hàng cao nhất 76 | if (i >= 0) { 77 | sum += num1.charAt(i--) - '0'; 78 | } 79 | if (j >= 0) { 80 | sum += num2.charAt(j--) - '0'; 81 | } 82 | 83 | carry = sum / 10; 84 | result.append(sum % 10); 85 | } 86 | 87 | if (carry > 0) { 88 | result.append(carry); 89 | } 90 | 91 | return result.reverse().toString(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /6. Network Programming/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/6. Network Programming/.gitkeep -------------------------------------------------------------------------------- /ArgumentPassing.md: -------------------------------------------------------------------------------- 1 | # Truyền tham số trong Java 2 | 3 | Hãy luôn nhớ rằng, **Java chỉ sử dụng truyền tham trị**. Khi bạn truyền tham số vào hàm trong Java, tùy thuộc vào kiểu dữ liệu của tham số (kiểu nguyên thủy hay kiểu không nguyên thủy), việc tương tác với vùng nhớ stack và heap sẽ khác nhau. 4 | 5 | ## 1. Kiểu nguyên thủy 6 | 7 | Xét đoạn mã sau đây: 8 | 9 | ```java 10 | public class Main { 11 | public static void setX(int y){ 12 | y = 2; 13 | } 14 | 15 | public static void main(String args[]){ 16 | int x = 1; 17 | System.out.println(x); // -> 1 18 | 19 | setX(x); 20 | System.out.println(x); // -> 1 21 | } 22 | } 23 | ``` 24 | 25 | Trong ví dụ này: 26 | 27 | - Biến `x` được khai báo và khởi tạo giá trị là `1` trong phương thức `main()`. 28 | - `x` là biến địa phương của phương thức `main()` được lưu trữ trên vùng nhớ stack của phương thức `main()`. 29 | - Khi gọi phương thức `setX(x)`, giá trị của `x` được sao chép vào một biến mới tên là `y` của phương thức `setX()`. 30 | - Trong phương thức `setX()`, biến `y` được thay đổi giá trị thành `2`. Tuy nhiên, sự thay đổi này chỉ ảnh hưởng đến `y`, biến địa phương trong `setX()`, và không ảnh hưởng gì đến `x` ở trong `main()` vì `y` là một bản sao riêng biệt của `x`. 31 | - Do đó, khi in ra giá trị của `x` sau khi gọi `setX()`, kết quả vẫn là `1` như giá trị ban đầu. 32 | 33 |
34 | 35 | Minh họa 4 36 | 37 |
38 | 39 | Xét tiếp đoạn mã sau: 40 | 41 | ```java 42 | public class Main { 43 | public static void setX(int x) { 44 | x = 2; 45 | } 46 | 47 | public static void main(String args[]) { 48 | int x = 1; 49 | System.out.println(x); // -> 1 50 | 51 | setX(x); 52 | System.out.println(x); // -> 1 53 | } 54 | } 55 | ``` 56 | 57 | Mặc dù phương thức `setX()` sử dụng tham số tên là `x` (giống như tên biến `x` trong phương thức `main()`) nhưng nó vẫn là biến địa phương của phương thức `setX()`. Việc đặt tên tham số trong phương thức `setX()` là `x` hoặc `y` (hoặc bất kỳ tên nào khác) không ảnh hưởng đến cơ chế truyền tham trị của Java, nhưng nó có thể ảnh hưởng đến tính rõ ràng và dễ đọc của mã. Việc chọn tên tham số có thể làm tăng hoặc giảm sự rõ ràng của mã, đặc biệt là trong việc hiểu rằng các biến trong các phương thức khác nhau là độc lập với nhau. 58 | 59 | - Khi dùng cùng tên `x`: Trong ví dụ gần nhất, cả biến địa phương trong `main()` và tham số của `setX()` đều được đặt tên là `x`. Điều này có thể gây ra nhầm lẫn khi đọc và hiểu mã, vì có vẻ như cùng một biến `x` đang được sử dụng trong cả hai phương thức, mặc dù thực tế chúng là hai biến hoàn toàn độc lập. 60 | 61 | - Khi dùng tên khác nhau (`y`): Trong ví dụ trước đó, biến trong `main()` là `x` và tham số trong `setX()` là `y`. Sự khác biệt trong đặt tên này giúp làm rõ rằng biến `y` trong `setX()` chỉ là một bản sao của giá trị của `x` và là một biến hoàn toàn độc lập. Điều này làm giảm khả năng gây nhầm lẫn về việc liệu thay đổi `y` có ảnh hưởng đến `x` hay không. 62 | 63 | ## 2. Kiểu không nguyên thủy 64 | 65 | Xét đoạn mã sau đây: 66 | 67 | ```java 68 | public class Main { 69 | int x; 70 | 71 | Main(int y) { 72 | x = y; 73 | } 74 | 75 | static void changeX(int x) { 76 | x = 10; 77 | } 78 | 79 | static void changeX(Main object) { 80 | object.x = 10; 81 | } 82 | 83 | void changeX() { 84 | this.x = 20; 85 | } 86 | 87 | public static void main(String[] args) { 88 | Main obj = new Main(5); 89 | 90 | changeX(obj.x); 91 | System.out.println(obj.x); // -> 5 92 | 93 | changeX(obj); 94 | System.out.println(obj.x); // -> 10, thực sự bị thay đổi 95 | 96 | obj.changeX(); 97 | System.out.println(obj.x); // -> 20, thực sự bị thay đổi 98 | } 99 | } 100 | ``` 101 | 102 | - Phương thức `changeX(int x)`: 103 | - Gọi phương thức: `changeX(obj.x)`: Trong phương thức này, `x` là một tham số kiểu nguyên thủy. Khi phương thức này được gọi, giá trị của `obj.x` (là `5`) được truyền vào phương thức. Trong stack của thread, một khung mới (stack frame) được tạo cho lời gọi `changeX(int)`, và giá trị `5` được sao chép vào biến địa phương `x` của khung này. 104 | - Thực thi: Giá trị của biến địa phương `x` trong phương thức được thay đổi thành `10`, nhưng điều này không ảnh hưởng đến biến `x` của đối tượng `obj` trong `main`, vì chỉ bản sao của giá trị được thay đổi. 105 | - Kết quả: `System.out.println(obj.x); // -> 5` - Giá trị của `x` trong đối tượng `obj` không thay đổi vì chỉ giá trị sao chép trong stack của phương thức `changeX(int)` được thay đổi. 106 | 107 | - Phương thức `changeX(Main obj)`: 108 | - Gọi phương thức: `changeX(object)`: Ở đây, tham chiếu đến đối tượng `Main` được truyền vào. Trong stack, một khung mới được tạo cho `changeX(Main)`, và tham chiếu tới `obj` được sao chép vào biến địa phương. 109 | - Thực thi: Tham chiếu này trỏ đến cùng một đối tượng trên heap như tham chiếu ban đầu trong `main`. Do đó, khi thuộc tính `x` của đối tượng được thay đổi thành `10` trong phương thức, nó thực sự thay đổi đối tượng trên heap. 110 | - Kết quả: `System.out.println(obj.x); // -> 10` - Giá trị của `x` trong đối tượng `obj` được cập nhật thành `10` do sự thay đổi trực tiếp trên đối tượng trên heap. 111 | 112 | Chi tiết như sau: 113 | 114 |
115 | 116 | Minh họa 5 117 | 118 |
119 | 120 | - Phương thức `changeX()` của lớp `Main`: 121 | - Gọi phương thức: `obj.changeX()`: Phương thức này là một phương thức phi tĩnh của lớp `Main`, do đó nó cần một thực thể của lớp để gọi. 122 | - Thực thi: Trong phương thức này, `this.x = 20`; đề cập đến biến `x` của đối tượng `obj` thông qua từ khóa `this`, chỉ thị rằng thay đổi nên áp dụng cho đối tượng mà phương thức được gọi từ. 123 | - Kết quả: `System.out.println(obj.x); // -> 20` - Giá trị của `x` được thay đổi thành `20` trên cùng một đối tượng `obj` trên heap. 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lập trình hướng đối tượng với Java 2 | 3 | ![intro](images/logo.png) 4 | 5 |

6 | Static Badge 7 | Static Badge 8 | Static Badge 9 | GitHub commit activity 10 | GitHub last commit 11 | GitHub Issues or Pull Requests 12 | GitHub top language 13 | GitHub language count 14 | GitHub code size in bytes 15 | GitHub Repo stars 16 |

17 | 18 |

19 | Typing SVG 20 |

21 | 22 | Chào mừng bạn đến với kho lưu trữ **Lập trình hướng đối tượng với Java**! Nơi đây cung cấp một nguồn tài nguyên toàn diện để khám phá và học hỏi lập trình hướng đối tượng qua ngôn ngữ Java. 23 | 24 | > Lập trình hướng đối tượng (OOP - Object-Oriented Programming) là một mô hình lập trình phổ biến được sử dụng rộng rãi trong phát triển phần mềm. Nó được dựa trên các khái niệm về "đối tượng", là các thực thể kết hợp cả dữ liệu (thuộc tính) và các phương thức (hành vi). OOP tập trung vào việc thiết kế phần mềm dựa trên các đối tượng tương tác với nhau, thay vì quy trình và logic như trong lập trình hướng thủ tục. 25 | 26 | ## Giới thiệu về lập trình hướng đối tượng 27 | 28 |
29 | 30 | Các nguyên tắc cơ bản của lập trình hướng đối tượng 31 |
32 | 33 | Lập trình hướng đối tượng dựa trên 4 nguyên tắc cơ bản, đó là: Đóng gói, Kế thừa, Đa hình, và Trừu tượng hóa. Mỗi nguyên tắc này cung cấp một cách tiếp cận để giải quyết các vấn đề trong phát triển phần mềm và giúp quản lý mã nguồn một cách hiệu quả hơn. 34 | 35 | **1. Đóng gói** (Encapsulation): Đây là khái niệm che giấu chi tiết triển khai bên trong của đối tượng, ngăn người dùng trực tiếp truy cập vào dữ liệu bên trong. Đóng gói giúp bảo vệ dữ liệu và hành vi của đối tượng khỏi sự can thiệp không mong muốn và cung cấp một giao diện thống nhất để tương tác với đối tượng. 36 | 37 |
38 | 39 | Đoạn mã minh họa 40 | 41 | ```java 42 | // Lớp Employee (Nhân viên) chứa các thuộc tính: tên, tuổi, lương 43 | public class Employee { 44 | // Các biến dữ liệu riêng tư để ngăn chặn truy cập trực tiếp từ bên ngoài lớp 45 | private String name; 46 | private int age; 47 | private double salary; 48 | 49 | // Constructor (hàm tạo) để khởi tạo các giá trị của đối tượng 50 | public Employee(String name, int age, double salary) { 51 | this.name = name; 52 | this.age = age; 53 | this.salary = salary; 54 | } 55 | 56 | // Các phương thức getter để truy cập các biến dữ liệu 57 | public String getName() { 58 | return name; 59 | } 60 | 61 | public int getAge() { 62 | return age; 63 | } 64 | 65 | public double getSalary() { 66 | return salary; 67 | } 68 | 69 | // Các phương thức setter để cập nhật giá trị của các biến dữ liệu 70 | public void setName(String name) { 71 | this.name = name; 72 | } 73 | 74 | public void setAge(int age) { 75 | this.age = age; 76 | } 77 | 78 | public void setSalary(double salary) { 79 | this.salary = salary; 80 | } 81 | } 82 | 83 | // Lớp Main để thực thi mã 84 | public class Main { 85 | public static void main(String[] args) { 86 | // Tạo đối tượng của lớp Employee 87 | Employee emp = new Employee("John Doe", 30, 50000); 88 | 89 | // Truy cập và cập nhật thông qua các phương thức getter và setter 90 | System.out.println("Employee name: " + emp.getName()); 91 | emp.setSalary(55000); 92 | System.out.println("Updated salary: " + emp.getSalary()); 93 | } 94 | } 95 | ``` 96 | 97 | Trong ví dụ này, các biến `name`, `age`, và `salary` được đánh dấu là `private`, điều này có nghĩa là chúng không thể được truy cập trực tiếp từ bên ngoài lớp `Employee`. Thay vào đó, các phương thức `getName()`, `getAge()`, `getSalary()`, `setName()`, `setAge()`, và `setSalary()` được cung cấp để truy cập và cập nhật giá trị của các biến này, đảm bảo tính đóng gói. 98 | 99 |
100 | 101 | **2. Kế thừa** (Inheritance): Kế thừa cho phép một lớp mới kế thừa các thuộc tính và phương thức từ một lớp đã có sẵn. Lớp mới này có thể bổ sung thêm hoặc sửa đổi các thành phần thừa kế để đáp ứng nhu cầu riêng của nó, giúp tái sử dụng và mở rộng mã nguồn một cách hiệu quả. 102 | 103 |
104 | 105 | Đoạn mã minh họa 106 | 107 | ```java 108 | // Lớp cơ sở (hay lớp cha): Employee 109 | public class Employee { 110 | private String name; 111 | private int age; 112 | 113 | // Constructor của lớp Employee 114 | public Employee(String name, int age) { 115 | this.name = name; 116 | this.age = age; 117 | } 118 | 119 | // Getter và Setter 120 | public String getName() { 121 | return name; 122 | } 123 | 124 | public void setName(String name) { 125 | this.name = name; 126 | } 127 | 128 | public int getAge() { 129 | return age; 130 | } 131 | 132 | public void setAge(int age) { 133 | this.age = age; 134 | } 135 | 136 | // Phương thức để hiển thị thông tin 137 | public void displayInfo() { 138 | System.out.println("Name: " + name + ", Age: " + age); 139 | } 140 | } 141 | 142 | // Lớp dẫn xuất (hay lớp con): Manager kế thừa từ Employee 143 | public class Manager extends Employee { 144 | private double salary; 145 | 146 | // Constructor của lớp Manager 147 | public Manager(String name, int age, double salary) { 148 | super(name, age); // Gọi constructor của lớp cơ sở (lớp cha) 149 | this.salary = salary; 150 | } 151 | 152 | // Phương thức mới của lớp Manager 153 | @Override 154 | public void displayInfo() { 155 | // Gọi phương thức displayInfo của lớp cơ sở (lớp cha) 156 | super.displayInfo(); 157 | System.out.println("Salary: " + salary); 158 | } 159 | } 160 | 161 | public class Main { 162 | public static void main(String[] args) { 163 | // Tạo đối tượng Manager 164 | Manager manager = new Manager("Alice Johnson", 42, 75000); 165 | manager.displayInfo(); // Hiển thị thông tin của Manager 166 | } 167 | } 168 | ``` 169 | 170 | - Trong ví dụ này: 171 | - Lớp `Employee` là lớp cơ sở, chứa thông tin cơ bản như tên và tuổi. 172 | - Lớp `Manager` là lớp dẫn xuất từ `Employee` và bổ sung thêm thuộc tính `salary`. 173 | - Lớp `Manager` sử dụng từ khóa `extends` để kế thừa từ lớp `Employee`. Constructor của `Manager` gọi `super(name, age)`; để khởi tạo các thuộc tính được kế thừa từ lớp `Employee`. 174 | - Phương thức `displayInfo()` được ghi đè trong lớp `Manager` để thêm thông tin về mức lương, đồng thời gọi phương thức `displayInfo()` của lớp cơ sở để hiển thị thông tin cơ bản. 175 | 176 |
177 | 178 | **3. Đa hình** (Polymorphism): Đa hình là khả năng mà theo đó các lớp khác nhau có thể được sử dụng thông qua cùng một giao diện. Phương thức có thể được định nghĩa trong một lớp cơ sở và được thay thế bởi các phương thức có cùng tên trong các lớp dẫn xuất, cho phép các đối tượng được xử lý thông qua giao diện chung mà không cần biết kiểu dữ liệu cụ thể của chúng. 179 | 180 | Trong Java, tính đa hình được thể hiện thông qua việc ghi đè phương thức (method overriding) và nạp chồng phương thức (method overloading). 181 | 182 |
183 | 184 | Ghi đè phương thức 185 | 186 | ```java 187 | class Animal { 188 | void speak() { 189 | System.out.println("Animal speaks"); 190 | } 191 | } 192 | 193 | // Lớp Dog (chó) kế thừa lớp Animal (động vật) 194 | class Dog extends Animal { 195 | // Lớp Dog ghi đè phương thức speak() của lớp cha 196 | @Override 197 | void speak() { 198 | System.out.println("Dog barks"); 199 | } 200 | } 201 | 202 | // Lớp Cat (mèo) kế thừa lớp Animal (động vật) 203 | class Cat extends Animal { 204 | // Lớp Dog ghi đè phương thức speak() của lớp cha 205 | @Override 206 | void speak() { 207 | System.out.println("Cat meows"); 208 | } 209 | } 210 | 211 | public class Main { 212 | public static void main(String[] args) { 213 | Animal myAnimal = new Animal(); 214 | Animal myDog = new Dog(); 215 | Animal myCat = new Cat(); 216 | 217 | myAnimal.speak(); // Prints "Animal speaks" 218 | myDog.speak(); // Prints "Dog barks" 219 | myCat.speak(); // Prints "Cat meows" 220 | } 221 | } 222 | ``` 223 | 224 | Trong ví dụ này, phương thức `speak()` được ghi đè trong các lớp `Dog` và `Cat`. Khi gọi phương thức `speak()` trên đối tượng của lớp con, Java xác định phương thức nào sẽ được gọi tại thời điểm chạy, tùy thuộc vào loại đối tượng mà biến tham chiếu đến. 225 | 226 |
227 | 228 |
229 | 230 | Nạp chồng phương thức 231 | 232 | ```java 233 | class Printer { 234 | // In chuỗi 235 | void print(String data) { 236 | System.out.println("String: " + data); 237 | } 238 | 239 | // In số nguyên 240 | void print(int data) { 241 | System.out.println("Integer: " + data); 242 | } 243 | 244 | // In số thực 245 | void print(double data) { 246 | System.out.println("Double: " + data); 247 | } 248 | } 249 | 250 | public class Main { 251 | public static void main(String[] args) { 252 | Printer printer = new Printer(); 253 | 254 | // Gọi phương thức print nạp chồng 255 | printer.print("Hello, World!"); 256 | printer.print(123); 257 | printer.print(98.76); 258 | } 259 | } 260 | ``` 261 | 262 | - Trong ví dụ này: 263 | - Lớp `Printer` có ba phiên bản của phương thức `print`, mỗi phiên bản xử lý một kiểu dữ liệu cụ thể: `String`, `int`, và `double`. 264 | - Mỗi lần gọi phương thức `print`, Java sẽ xác định phiên bản phù hợp dựa trên kiểu dữ liệu của đối số truyền vào. 265 | 266 |
267 | 268 |
269 | 270 | Triển khai giao diện 271 | 272 | Tính đa hình cũng được thể hiện rất rõ ràng qua cơ chế sử dụng interface. Interface trong Java là một cách để đạt được đa hình ở mức độ cao hơn, cho phép một lớp triển khai (implement) nhiều interface và một interface có thể được triển khai bởi nhiều lớp khác nhau. 273 | 274 | Giả sử chúng ta có một interface `CanFly` và hai lớp `Bird` và `Airplane` cả hai đều triển khai interface này: 275 | 276 | ```java 277 | interface CanFly { 278 | void fly(); 279 | } 280 | 281 | class Bird implements CanFly { 282 | public void fly() { 283 | System.out.println("The bird flaps its wings to fly."); 284 | } 285 | } 286 | 287 | class Airplane implements CanFly { 288 | public void fly() { 289 | System.out.println("The airplane turns on its engines to fly."); 290 | } 291 | } 292 | 293 | public class TestPolymorphism { 294 | public static void main(String[] args) { 295 | CanFly myBird = new Bird(); 296 | CanFly myAirplane = new Airplane(); 297 | 298 | myBird.fly(); // Output: The bird flaps its wings to fly. 299 | myAirplane.fly(); // Output: The airplane turns on its engines to fly. 300 | } 301 | } 302 | ``` 303 | 304 | - Trong ví dụ này: 305 | - Interface `CanFly` định nghĩa phương thức `fly()` mà không cung cấp phần thân phương thức. 306 | - Cả `Bird` và `Airplane` đều triển khai phương thức `fly()` theo cách riêng của chúng. 307 | - Cả hai đối tượng `myBird` và `myAirplane` đều được tham chiếu thông qua kiểu interface `CanFly`, và khi gọi phương thức `fly()`, đa hình cho phép chúng ta không cần quan tâm đến việc đối tượng thuộc lớp nào; chúng ta chỉ biết rằng chúng có thể bay. 308 | 309 |
310 | 311 | **4. Trừu tượng hóa** (Abstraction): Trừu tượng hóa cho phép lập trình viên tập trung vào những gì một đối tượng làm mà không cần quan tâm đến cách thực hiện. Nó tạo ra một lớp cơ sở mô tả một giao diện tổng quát mà các lớp dẫn xuất sẽ thực thi, đơn giản hóa việc quản lý sự phức tạp của hệ thống. 312 | 313 | Trong Java, trừu tượng hóa có thể được thực hiện bằng hai cách: 314 | 315 |
316 | 317 | Lớp trừu tượng 318 | 319 | Lớp trừu tượng (Abstract Class) là một lớp không thể tạo đối tượng và có thể chứa phương thức trừu tượng không có phần thân. 320 | 321 | ```java 322 | abstract class Animal { 323 | // Phương thức trừu tượng 324 | abstract void makeSound(); 325 | 326 | // Phương thức bình thường 327 | void breathe() { 328 | System.out.println("Breathing..."); 329 | } 330 | } 331 | 332 | class Dog extends Animal { 333 | // Triển khai phương thức trừu tượng trong lớp con 334 | void makeSound() { 335 | System.out.println("Bark"); 336 | } 337 | } 338 | 339 | public class Main { 340 | public static void main(String[] args) { 341 | Animal myDog = new Dog(); 342 | myDog.makeSound(); // In ra "Bark" 343 | myDog.breathe(); // In ra "Breathing..." 344 | } 345 | } 346 | ``` 347 | 348 | - Trong ví dụ này: 349 | - `Animal` là một lớp trừu tượng có phương thức trừu tượng `makeSound()`. 350 | - `Dog` là lớp kế thừa từ `Animal` và phải cung cấp triển khai cụ thể cho phương thức trừu tượng `makeSound()`. 351 | 352 |
353 | 354 |
355 | 356 | Giao diện 357 | 358 | Giao diện (Interface) chỉ có thể chứa các phương thức trừu tượng mà không có triển khai. 359 | 360 | ```java 361 | interface Vehicle { 362 | void start(); 363 | void stop(); 364 | } 365 | 366 | class Car implements Vehicle { 367 | public void start() { 368 | System.out.println("Car starting"); 369 | } 370 | 371 | public void stop() { 372 | System.out.println("Car stopping"); 373 | } 374 | } 375 | 376 | class Bike implements Vehicle { 377 | public void start() { 378 | System.out.println("Bike starting"); 379 | } 380 | 381 | public void stop() { 382 | System.out.println("Bike stopping"); 383 | } 384 | } 385 | 386 | public class Main { 387 | public static void main(String[] args) { 388 | Vehicle myCar = new Car(); 389 | Vehicle myBike = new Bike(); 390 | 391 | myCar.start(); // In ra "Car starting" 392 | myCar.stop(); // In ra "Car stopping" 393 | myBike.start(); // In ra "Bike starting" 394 | myBike.stop(); // In ra "Bike stopping" 395 | } 396 | } 397 | ``` 398 | 399 | Ở đây, `Vehicle` là một interface định nghĩa hai phương thức trừu tượng: `start()` và `stop()`. Các lớp `Car` và `Bike` triển khai interface này và cung cấp triển khai cụ thể cho mỗi phương thức. 400 | 401 |
402 | 403 |
404 | 405 | Phân biệt lớp trừu tượng và giao diện 406 | 407 | **Sự giống nhau:** 408 | 409 | - Cả hai không thể được khởi tạo trực tiếp. Chúng đều là cấu trúc dùng để định nghĩa mẫu cho các lớp khác, chứ không phải để tạo đối tượng trực tiếp từ chúng. 410 | - Cả giao diện và lớp trừu tượng đều được sử dụng để mở rộng hoặc triển khai bởi các lớp khác. Chúng cung cấp một cơ sở để định nghĩa các phương thức mà lớp con có thể hoặc phải triển khai. 411 | - Chúng đóng vai trò quan trọng trong kế thừa, nơi chúng định nghĩa một bộ khung hoặc mẫu cho các lớp khác để tuân theo hoặc mở rộng. 412 | 413 | **Sự khác nhau:** 414 | 415 |
416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 |
Lớp trừu tượngGiao diện
Mục đích- Thường được sử dụng khi các lớp con có nhiều chức năng chung và cần một nơi chung để đặt mã có thể tái sử dụng.
- Giúp định nghĩa một khuôn mẫu cơ sở cho các lớp con, đảm bảo tất cả lớp con kế thừa nó phải tuân theo một thiết kế chung.
- Thường được sử dụng để định nghĩa một hợp đồng cho một chức năng mà không cần quan tâm đến cách triển khai.
- Lý tưởng cho các chức năng có thể được triển khai rất khác nhau.
- Tốt cho việc định nghĩa tập hợp các phương thức mà một lớp có thể thực hiện.
Triển khai- Có thể chứa cả phương thức trừu tượng (không có phần thân) lẫn phương thức không trừu tượng (có phần thân).
- Lớp trừu tượng cũng có thể chứa các trường dữ liệu và constructor.
- Ban đầu chỉ cho phép định nghĩa các phương thức trừu tượng không có phần thân.
- Từ Java 8 trở đi, giao diện còn cho phép định nghĩa các phương thức có thân thông qua default methods và static methods.
Đa kế thừa- Một lớp chỉ có thể kế thừa từ một lớp trừu tượng duy nhất.
- Điều này hạn chế tính linh hoạt của đa kế thừa như trong giao diện.
- Java cho phép một lớp có thể thực hiện nhiều giao diện cùng một lúc, cung cấp một hình thức đa kế thừa.
Hàm tạo- Có thể có constructor, giúp thiết lập các điều kiện ban đầu cho lớp.- Không thể có constructor.
Trường dữ liệu- Khác với giao diện, lớp trừu tượng có thể có các trường dữ liệu không phải là static và final.
- Các lớp con có thể có các thuộc tính riêng biệt được kế thừa từ lớp trừu tượng.
- Giao diện không hỗ trợ trường dữ liệu không phải là static và final.
- Mọi biến được khai báo trong giao diện đều tự động được coi là public static final.
453 | 454 |
455 | 456 |
457 | 458 |
459 | 460 |
461 | 462 | Ưu điểm của lập trình hướng đối tượng 463 |
464 | 465 | Lập trình hướng đối tượng mang lại nhiều ưu điểm vượt trội trong phát triển phần mềm, giúp nó trở thành một trong những mô hình thiết kế và lập trình chính trong ngành công nghệ thông tin. Dưới đây là những ưu điểm của nó: 466 | 467 | - **Tái sử dụng mã**: OOP cho phép lập trình viên sử dụng lại mã nguồn thông qua cơ chế kế thừa. Lớp con có thể kế thừa tính năng từ lớp cha mà không cần phải viết lại mã đó. Điều này giúp giảm bớt lượng công việc lập trình, giảm thiểu các lỗi tiềm ẩn và tăng tốc độ phát triển phần mềm. 468 | 469 | - **Dễ dàng bảo trì và sửa lỗi**: Cấu trúc đóng gói trong OOP giúp che giấu chi tiết triển khai, chỉ cung cấp giao diện (interface) cần thiết cho người dùng. Điều này làm cho việc bảo trì và cập nhật hệ thống trở nên dễ dàng hơn, vì thay đổi bên trong một đối tượng không ảnh hưởng tới các đối tượng khác. 470 | 471 | - **Mô hình hóa thực tiễn**: OOP cho phép lập trình viên mô hình hóa các thực thể thực tế dưới dạng đối tượng phần mềm, làm cho mã nguồn dễ hiểu và quản lý hơn. Việc sử dụng các đối tượng như là biểu diễn của dữ liệu và hành vi trong thế giới thực giúp phát triển phần mềm trở nên trực quan và gần gũi hơn với người lập trình. 472 | 473 | - **Tính mở rộng**: Nhờ vào khả năng kế thừa và đóng gói, OOP dễ dàng mở rộng. Một lớp mới có thể được tạo ra với sự kế thừa từ một hoặc nhiều lớp đã tồn tại mà không làm ảnh hưởng tới những lớp đó. Điều này giúp hệ thống phát triển một cách linh hoạt và thích ứng với nhu cầu mới một cách nhanh chóng. 474 | 475 | - **Tính bảo mật**: Đóng gói không chỉ giúp ẩn đi chi tiết triển khai mà còn cung cấp một lớp bảo mật. Dữ liệu bên trong một đối tượng được bảo vệ khỏi sự truy cập trực tiếp từ bên ngoài, đảm bảo tính toàn vẹn và an toàn của dữ liệu. 476 | 477 |
478 | 479 |
480 | 481 | Lý do bắt đầu học lập trình hướng đối tượng bằng Java 482 |
483 | 484 | Java là một trong những ngôn ngữ lập trình phổ biến nhất được sử dụng để giảng dạy lập trình hướng đối tượng vì nhiều lý do chính đáng. 485 | 486 | - **Java là một ngôn ngữ thuần túy hướng đối tượng**, nghĩa là mọi thứ trong Java đều là đối tượng hoặc lớp. Các khái niệm như kế thừa, đóng gói, đa hình, và trừu tượng hoàn toàn được tích hợp sẵn trong ngôn ngữ. Điều này giúp người học dễ dàng tiếp cận và thực hành các nguyên lý OOP một cách tự nhiên trong quá trình lập trình. 487 | 488 | - **Java có cú pháp khá rõ ràng và dễ hiểu.** Cú pháp của Java dựa trên C/C++, nhưng đã loại bỏ một số tính năng phức tạp như con trỏ trực tiếp, làm cho nó trở thành ngôn ngữ lý tưởng để giảng dạy cho người mới bắt đầu. Việc loại bỏ những tính năng phức tạp này giúp người học tập trung vào việc hiểu các khái niệm cốt lõi của OOP mà không bị sa lầy vào các chi tiết khó hiểu. 489 | 490 | - **Java là ngôn ngữ độc lập nền tảng**, có thể chạy trên bất kỳ hệ điều hành nào có máy ảo Java (JVM). Điều này có nghĩa là các chương trình Java có thể phát triển và thực thi một cách nhất quán trên các nền tảng khác nhau mà không cần thay đổi mã. Sự linh hoạt này là lý tưởng cho môi trường học tập, nơi sinh viên và giáo viên có thể sử dụng nhiều loại phần cứng và phần mềm. 491 | 492 |
493 | 494 | ## Mục lục 495 | 496 | Trong khi sử dụng kho lưu trữ này cho việc học tập, bạn nên tuân theo nguyên tắc sắp xếp được đề cập sau đây. Hãy đọc các mục theo đúng quy tắc từ trên xuống dưới, những phần đầu tiên sẽ là cơ sở của những nội dung tiếp theo. Riêng phần *Phụ lục* bạn có thể tham khảo bất cứ lúc nào, nó chứa một số cuốn sách tôi sử dụng cho việc thiết kế nên kho lưu trữ này. 497 | 498 | > Để đồng bộ và dễ kiểm soát, lớp chứa phương thức main để thực thi mã được đặt tên giống như nội dung bài học. Các lớp khác trong bài học để minh họa. Chẳng hạn, bài học về Interface nằm trong `Interface.java` có các lớp: `Movable`, `Trackable`, `Drone`, `Interface` thì các lớp `Movable`, `Trackable`, `Drone` có tác dụng diễn giải cho kiến thức bài học, còn lớp `Interface` để thực thi mã từ các lớp trước nó. 499 | 500 |
501 | Phần 1: Giới thiệu 502 | 503 | - [**Bắt đầu với Java**](#bắt-đầu-với-java) 504 | - Máy ảo Java 505 | - So sánh Java với một số ngôn ngữ lập trình khác: tính di động, tốc độ và an toàn 506 | - Quản lý động bộ nhớ 507 | - Khả năng phát hiện và xử lý lỗi 508 | - [**Naming Convention**](#quy-ước-định-danh) 509 | - [**Unified Modeling Language**](#ngôn-ngữ-mô-hình-hóa-thống-nhất) 510 | - [**The Java Language**](1.%20The%20Java%20Language/) 511 | - [HelloWorld](1.%20The%20Java%20Language/HelloWorld.java) 512 | - [HelloJava](1.%20The%20Java%20Language/HelloJava.java) 513 | - [DataTypes](1.%20The%20Java%20Language/DataTypes.java) 514 | - [Wrappers](1.%20The%20Java%20Language/Wrappers.java) 515 | - [Boxing](1.%20The%20Java%20Language/Boxing.java) 516 | - [Enumerations](1.%20The%20Java%20Language/Enumerations.java) 517 | - [Arrays](1.%20The%20Java%20Language/Arrays.java) 518 | - [ArgumentPassing](1.%20The%20Java%20Language/ArgumentPassing.java) 519 | - [MethodOverloading](1.%20The%20Java%20Language/MethodOverloading.java) 520 | - [ObjectsInJava](1.%20The%20Java%20Language/ObjectsInJava.java) 521 | - [InitializerBlock](1.%20The%20Java%20Language/InitializerBlock.java) 522 | - [ObjectCreation](1.%20The%20Java%20Language/ObjectCreation.java) 523 | - [ObjectDestruction](1.%20The%20Java%20Language/ObjectDestruction.java) 524 | - [ThisReference](1.%20The%20Java%20Language/ThisReference.java) 525 | 526 |
527 | 528 |
529 | 530 | Phần 2: Làm việc với đối tượng và lớp trong Java 531 | 532 | - [**Classes in Java**](2.%20Classes%20in%20Java/) 533 | - [Subclass](2.%20Classes%20in%20Java/Subclass.java) 534 | - [Casting](2.%20Classes%20in%20Java/Casting.java) 535 | - [SuperConstructor](2.%20Classes%20in%20Java/SuperConstructor.java) 536 | - [Abstract](2.%20Classes%20in%20Java/Abstract.java) 537 | - [Interface](2.%20Classes%20in%20Java/Interface.java) 538 | - [Callback](2.%20Classes%20in%20Java/Callback.java) 539 | - [InterfaceVariables](2.%20Classes%20in%20Java/InterfaceVariables.java) 540 | - [SubInterface](2.%20Classes%20in%20Java/SubInterface.java) 541 | - **Inner Classes** 542 | - [NestedClass](2.%20Classes%20in%20Java/NestedClass.java) 543 | - [InnerClass](2.%20Classes%20in%20Java/InnerClass.java) 544 | - [AdapterClass](2.%20Classes%20in%20Java/AdapterClass.java) 545 | - [WithinMethods](2.%20Classes%20in%20Java/WithinMethods.java) 546 | - [StaticInnerClass](2.%20Classes%20in%20Java/StaticInnerClass.java) 547 | - [AnonymousInnerClass](2.%20Classes%20in%20Java/AnonymousInnerClass.java) 548 | - [EventHandling](2.%20Classes%20in%20Java/EventHandling.java) 549 | - **The Object Class** 550 | - [TheObjectClass](2.%20Classes%20in%20Java/TheObjectClass.java) 551 | - [Equals](2.%20Classes%20in%20Java/Equals.java) 552 | - [Hashcode](2.%20Classes%20in%20Java/Hashcode.java) 553 | - [Clone](2.%20Classes%20in%20Java/Clone.java) 554 | - **The Class Class** 555 | - [TheClassClass](2.%20Classes%20in%20Java/TheClassClass.java) 556 | - [Reflection](2.%20Classes%20in%20Java/Reflection.java) 557 | - **Annotations** 558 | - [Annotations](2.%20Classes%20in%20Java/Annotations.java) 559 | - **Generics** 560 | 561 |
562 | 563 |
564 | 565 | Phần 3: Công cụ cơ bản và thiết yếu của Java 566 | 567 | - [**Core Ultilities**](3.%20Core%20Ultilities/) 568 | - [StringTips](3.%20Core%20Ultilities/StringTips.java) 569 | - [Math](3.%20Core%20Ultilities/Math.java) 570 | - [Random](3.%20Core%20Ultilities/Random.java) 571 | - [Time](3.%20Core%20Ultilities/Time.java) 572 | - [Collections](3.%20Core%20Ultilities/Collections.java) 573 | - [Iteration](3.%20Core%20Ultilities/Iteration.java) 574 | - [Properties](3.%20Core%20Ultilities/Properties.java) 575 | - [LoggingAPI](3.%20Core%20Ultilities/LoggingAPI.java) 576 | 577 |
578 | 579 |
580 | 581 | Phần 4: Tiện ích vào - ra với Java 582 | 583 | - [**IO Facilities**](4.%20IO%20Facilities/) 584 | - Streams 585 | - Pipes 586 | - Files 587 | - DataCompression 588 | - Buffers 589 | 590 |
591 | 592 |
593 | 594 | Phần 5: Cấu trúc dữ liệu và Thuật toán bằng Java 595 | 596 | > Mục này gần như cùng thứ tự với kho lưu trữ của tôi: [**Cấu trúc dữ liệu và Thuật toán sử dụng C/C++**](https://github.com/HaiAu2501/Data-Structures-and-Algorithms-using-C), chỉ khác là được viết bằng ngôn ngữ Java. Sẽ có một số khác biệt giữa hai ngôn ngữ, bạn đọc có thể tự đối chiếu chúng. 597 | 598 | - **Tuần 1: Làm quen với Java** 599 | - [SumTwoInts](5.%20Data%20Structures%20and%20Algorithms/SumTwoInts.java) 600 | - [SumOfArray](5.%20Data%20Structures%20and%20Algorithms/SumOfArray.java) 601 | - [MaxSubarray](5.%20Data%20Structures%20and%20Algorithms/MaxSubarray.java) 602 | - **Tuần 2: Thuật toán Đệ quy & Thuật toán Quay lui** 603 | - **Tuần 3: Thuật toán Nhánh cận & Thuật toán Tham lam** 604 | - **Tuần 4: Thuật toán Quy hoạch động** 605 | - **Tuần 5: Ngăn xếp & Hàng đợi** 606 | 607 |
608 | 609 |
610 | 611 | Phần 6: Lập trình mạng sử dụng Java 612 | 613 |
614 | 615 |
616 | 617 | Phụ lục 618 | 619 | - [**Chỉ định truy cập**](#chỉ-định-truy-cập) 620 | - [**Tài liệu tham khảo**](#tài-liệu-tham-khảo) 621 | - [**Tải xuống tài liệu tham khảo**](documents) 622 | - [**Dự án của tôi**](.projects) 623 | - [**TicTacToe**](.projects/TicTacToe.java) 624 | 625 |
626 | 627 | ## Bắt đầu với Java 628 | 629 |
630 | 631 | 1. Máy ảo Java (JVM) 632 |
633 | 634 | Java vừa là ngôn ngữ lập trình vừa được **biên dịch** (complied) vừa được **thông dịch** (interpreted). Trong Java, mã nguồn được biên dịch thành bytecode, đó là các chỉ thị nhị phân đơn giản hoạt động như mã máy cho máy tính. Tuy nhiên, khác với C hay C++, bytecode của Java không phải là mã máy bản địa cho bất kỳ loại vi xử lý cụ thể nào mà là cho một máy ảo Java (JVM), một nền tảng chung cho mọi hệ thống. 635 | 636 | Bytecode này sau đó được máy ảo Java thông dịch và thực thi như thể nó là mã máy bản địa. JVM hoạt động giống như một hệ điều hành thực sự trong việc quản lý bộ nhớ và xử lý các lệnh, đảm bảo an toàn và di động của mã. Mọi đặc điểm của ngôn ngữ Java đều được định nghĩa rõ ràng, không phụ thuộc vào hệ thống nền tảng cụ thể nào, giúp Java có khả năng chạy đồng nhất trên nhiều nền tảng khác nhau mà không cần chỉnh sửa mã. 637 | 638 | JVM cung cấp một môi trường thực thi an toàn, nơi nó thực hiện các chức năng tương tự như một hệ điều hành. Nó quản lý bộ nhớ, thực thi các lệnh dựa trên ngăn xếp, và xử lý các kiểu dữ liệu nguyên thủy. Việc này giảm thiểu các rủi ro bảo mật và tăng tính ổn định của ứng dụng. 639 | 640 |
641 | 642 |
643 | 644 | 2. So sánh Java với một số ngôn ngữ lập trình khác: tính di động, tốc độ và an toàn 645 |
646 | 647 | Dù có vẻ ngoài tương tự như C và C++ về cú pháp, Java không phải là hậu duệ trực tiếp của C hay là phiên bản tiếp theo của C++. Java có nhiều điểm chung với các ngôn ngữ động như Smalltalk và Lisp hơn là với C. Sự giống nhau chỉ dừng lại ở cú pháp bên ngoài như sử dụng nhiều dấu ngoặc nhọn và dấu chấm phẩy. Java thừa hưởng triết lý của C về một ngôn ngữ tốt nên gọn nhẹ, dễ nhớ nhưng lại mở rộng vốn từ vựng qua các gói lớp Java. 648 | 649 | Ngôn ngữ kịch bản như Perl, Python và Ruby rất phổ biến vì chúng phù hợp cho các ứng dụng an toàn, được kết nối mạng. Tuy nhiên, hầu hết các ngôn ngữ kịch bản không được thiết kế cho lập trình quy mô lớn nghiêm túc. Chúng thường không phù hợp cho các dự án lớn hay phức tạp vì cấu trúc chương trình lỏng lẻo và hệ thống kiểu dữ liệu đơn giản. 650 | 651 | Java cung cấp một nền tảng an toàn để phát triển các framework cấp cao hơn và thậm chí là các ngôn ngữ khác, kết hợp sự đơn giản và tính năng của Java cho phép phát triển nhanh chóng và dễ dàng thay đổi ứng dụng. Java cũng đã học hỏi từ các tính năng của Smalltalk và cải tiến chúng, đặc biệt là trong việc sử dụng bộ kiểm tra bytecode để đảm bảo tính chính xác của mã Java biên dịch, giúp nâng cao hiệu suất và đảm bảo an toàn hơn so với Smalltalk. 652 | 653 | Java được thiết kế để là một ngôn ngữ an toàn, không chỉ chống lại các lỗi phần mềm mà còn các vấn đề thường gặp trong thiết kế và lập trình. Java cung cấp nhiều lớp bảo vệ, từ kiểm tra an toàn của mã trước khi chạy cho đến cách thức mà trình tải lớp (class loader), một cơ chế tải bytecode của trình thông dịch Java, tạo ra một "bức tường" xung quanh các lớp không đáng tin cậy. Những tính năng này là nền tảng cho các chính sách bảo mật cấp cao, cho phép hoặc không cho phép các loại hoạt động khác nhau trên từng ứng dụng. 654 | 655 | Java bắt đầu từ một "tấm bảng trắng" và do đó có thể tránh được những tính năng phức tạp hoặc gây tranh cãi có trong các ngôn ngữ khác. Ví dụ, Java không cho phép lập trình viên tái định nghĩa các toán tử (như + hay -), không có tiền xử lý mã nguồn như macros hay #define statements, những thứ thường được dùng trong các ngôn ngữ khác để hỗ trợ sự phụ thuộc vào nền tảng. 656 | 657 | Java cũng cung cấp một cấu trúc gói (package) rõ ràng để tổ chức các tệp lớp, giúp trình biên dịch xử lý một số chức năng của công cụ make truyền thống một cách hiệu quả. Mọi thông tin kiểu dữ liệu đều được bảo toàn trong các lớp Java đã biên dịch, không cần tới các tệp tiêu đề nguồn thừa như trong C/C++. Điều này khiến mã Java dễ đọc và ít cần đến ngữ cảnh hơn. 658 | 659 | Java chỉ hỗ trợ kế thừa đơn (mỗi lớp chỉ có một lớp "cha" duy nhất) nhưng cho phép kế thừa nhiều giao diện (interface). Giao diện trong Java, tương tự như lớp trừu tượng trong C++, xác định hành vi của một đối tượng mà không định nghĩa thực thi của nó. Đây là một cơ chế mạnh mẽ cho phép nhà phát triển định nghĩa một "hợp đồng" về hành vi của đối tượng mà có thể được sử dụng và tham chiếu một cách độc lập với bất kỳ thực thi đối tượng cụ thể nào. 660 | 661 |
662 | 663 |
664 | 665 | 3. Quản lý động bộ nhớ 666 |
667 | 668 | Java loại bỏ việc sử dụng con trỏ có thể tham chiếu tới bất kỳ khu vực bộ nhớ nào và thêm vào thu gom rác tự động cùng mảng cấp cao. Những tính năng này giúp loại bỏ nhiều vấn đề liên quan đến an toàn, khả năng chuyển đổi và tối ưu hóa mà các ngôn ngữ khác thường gặp phải. Trong Java, các đối tượng không còn được sử dụng sẽ tự động được thu hồi bộ nhớ, giảm thiểu lỗi do quản lý bộ nhớ thủ công. 669 | 670 | Java không sử dụng con trỏ theo nghĩa truyền thống mà thay vào đó là các tham chiếu, định kiểu chặt chẽ và an toàn hơn. Các đối tượng trong Java, ngoại trừ các kiểu nguyên thủy, được truy cập qua tham chiếu. Điều này cho phép xây dựng các cấu trúc dữ liệu phức tạp một cách an toàn về kiểu dữ liệu mà không có rủi ro liên quan đến con trỏ trong C/C++. 671 | 672 |
673 | 674 |
675 | 676 | 4. Khả năng phát hiện và xử lý lỗi 677 |
678 | 679 | Java được thiết kế để xử lý lỗi một cách thông minh và hiệu quả, nhờ vào cơ chế quản lý ngoại lệ mạnh mẽ. Trong Java, các lỗi không chỉ được bắt và xử lý tại một nơi cụ thể trong chương trình thông qua khối mã "catch", mà còn được đóng gói thành các đối tượng ngoại lệ. Mỗi đối tượng này mang thông tin về nguyên nhân gây ra lỗi, giúp lập trình viên dễ dàng hiểu và xử lý lỗi một cách chính xác. Trình biên dịch Java đòi hỏi phương thức phải tuyên bố các ngoại lệ mà nó có thể phát sinh, hoặc là phải tự xử lý chúng ngay lập tức. Điều này giúp đưa thông tin lỗi lên cùng mức độ quan trọng với các thông tin khác như kiểu dữ liệu trả về hay tham số của phương thức. Qua đó, khi lập trình, bạn có thể dự đoán và chuẩn bị sẵn sàng cho các tình huống có thể xảy ra, đảm bảo rằng ứng dụng của bạn sẽ hoạt động ổn định và an toàn hơn. 680 | 681 |
682 | 683 | ## Quy ước định danh 684 | 685 | Trong lập trình và phát triển phần mềm, **quy ước định danh** (naming convention) là một tập hợp các quy tắc cho việc chọn tên các biến, hàm, lớp, và các đối tượng khác trong mã nguồn. Quy ước định danh giúp làm cho mã nguồn dễ đọc, dễ hiểu và dễ bảo trì hơn. Kho lưu trữ của tôi tuân thủ nghiêm ngặt quy ước này, được đề cập dưới đây. 686 | 687 |
688 | 689 | Quy ước định danh 690 |
    691 | 692 |
  1. Lớp (Class) và Giao diện (Interface):
  2. 693 | 694 | - *Lớp:* Tên lớp luôn bắt đầu bằng chữ cái in hoa (PascalCase). Nếu tên lớp bao gồm nhiều từ, mỗi từ cũng phải bắt đầu bằng chữ cái in hoa. Ví dụ: `Student`, `Car`, `ColorChooser`. 695 | 696 | - *Giao diện:* Giống như class, tên interface cũng sử dụng PascalCase. Thường thì tên interface sẽ bắt đầu bằng các chữ cái viết hoa như `I` hoặc sử dụng các hậu tố/suffix như `able` hoặc `ible` để mô tả tính năng, chẳng hạn như `Runnable`, `Accessible`. 697 | 698 |
  3. Phương thức (Method)/Hàm (Function) và Biến (Variable):
  4. 699 | 700 | - Tên phương thức luôn bắt đầu bằng chữ thường và theo sau là camelCase. Tên phương thức thường là các động từ hoặc cụm động từ mô tả hành động mà phương thức đó thực hiện. Ví dụ: `getName()`, `calculateTotalWidth()`. 701 | 702 | - Tên biến cũng nên bắt đầu bằng chữ thường và theo sau là camelCase. Tên biến nên rõ ràng và mô tả được giá trị mà chúng đại diện. Ví dụ: `height`, `numberOfStudents`. 703 | 704 |
  5. Gói (Package)
  6. 705 | 706 | - Tên gói nên được viết thường hoàn toàn để tránh xung đột với tên lớp và interface. 707 | 708 |
709 | 710 | Một ví dụ về lớp `Dog`: 711 | 712 | ```java 713 | package com.example.animals; 714 | 715 | /** 716 | * A generic Dog class that can be used as a base class for specific breeds. 717 | */ 718 | public class Dog { 719 | private String name; 720 | private int age; 721 | 722 | /** 723 | * Constructor for Dog class. 724 | * 725 | * @param name The name of the dog. 726 | * @param age The age of the dog. 727 | */ 728 | public Dog(String name, int age) { 729 | this.name = name; 730 | this.age = age; 731 | } 732 | 733 | /** 734 | * Returns the name of the dog. 735 | * 736 | * @return The name of the dog. 737 | */ 738 | public String getName() { 739 | return name; 740 | } 741 | 742 | /** 743 | * Sets the name of the dog. 744 | * 745 | * @param name New name for the dog. 746 | */ 747 | public void setName(String name) { 748 | this.name = name; 749 | } 750 | 751 | /** 752 | * Returns the age of the dog. 753 | * 754 | * @return The age of the dog. 755 | */ 756 | public int getAge() { 757 | return age; 758 | } 759 | 760 | /** 761 | * Sets the age of the dog. 762 | * 763 | * @param age New age for the dog. 764 | */ 765 | public void setAge(int age) { 766 | this.age = age; 767 | } 768 | 769 | /** 770 | * Provides a string representation of the dog. 771 | * 772 | * @return A string describing the dog's details. 773 | */ 774 | @Override 775 | public String toString() { 776 | return "Dog[name=" + name + ", age=" + age + "]"; 777 | } 778 | } 779 | ``` 780 | 781 |
782 | 783 | ## Chỉ định truy cập 784 | 785 |
786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 |
Chỉ định truy cậppublicprotecteddefaultprivate
Cùng package
Bên trong lớp
Lớp conKhông
Lớp khácKhông
Khác packageLớp conKhôngKhông
Lớp khácKhôngKhôngKhông
836 |
837 | 838 | ## Ngôn ngữ mô hình hóa thống nhất 839 | 840 | Unified Modeling Language (UML) là một công cụ mô hình hóa đồ họa tiêu chuẩn, được thiết kế để phác họa, mô tả và tài liệu về các khía cạnh khác nhau của phần mềm ứng dụng. Nó đặc biệt hữu ích trong lĩnh vực lập trình hướng đối tượng, vì nó cung cấp một cách thức trực quan để thể hiện các lớp, đối tượng, và các mối quan hệ giữa chúng. UML bao gồm nhiều loại sơ đồ khác nhau, nhưng trong bối cảnh lập trình hướng đối tượng, sơ đồ lớp (class diagrams) và sơ đồ tương tác (interaction diagrams) là hai loại được sử dụng phổ biến nhất. 841 | 842 |
843 | 844 | Ý nghĩa đối với Lập trình hướng đối tượng 845 | 846 | - UML cho phép lập trình viên mô tả cấu trúc của hệ thống bằng cách sử dụng sơ đồ lớp. Sơ đồ này thể hiện các lớp trong hệ thống, các thuộc tính, phương thức của chúng, và quan trọng nhất là mối quan hệ giữa các lớp như kế thừa, liên kết, tổng hợp và kết hợp. Điều này giúp lập trình viên hiểu và thiết kế hệ thống một cách hệ thống hơn. 847 | 848 | - Một khi sơ đồ UML đã được hoàn thiện, nó có thể dùng như là cơ sở để viết mã nguồn. Trong OOP, việc chuyển từ sơ đồ lớp sang mã nguồn (thường là Java, C#, hay C++) là khá trực tiếp, do sự tương đồng giữa các khái niệm mô tả trong UML và cấu trúc lớp trong các ngôn ngữ lập trình này. 849 | 850 | - UML giúp định nghĩa rõ ràng các mối quan hệ giữa các đối tượng, làm nổi bật cách thức giao tiếp và tương tác giữa chúng qua sơ đồ tương tác như sơ đồ tuần tự (sequence diagrams) và sơ đồ cộng tác (collaboration diagrams). Điều này giúp lập trình viên hiểu được luồng dữ liệu và kiểm soát trong ứng dụng. 851 | 852 | - Trước khi bắt đầu viết mã, UML giúp nhóm phát triển có thể phát hiện và sửa chữa những vấn đề về thiết kế. Sự thống nhất trong cách sử dụng UML còn giúp các thành viên trong nhóm dễ dàng hiểu ý tưởng của nhau, từ đó tăng hiệu quả phối hợp làm việc. 853 | 854 | - UML cung cấp tài liệu đầy đủ cho phần mềm, điều này rất hữu ích trong giai đoạn bảo trì và nâng cấp phần mềm. Một sơ đồ UML tốt có thể giúp người mới tham gia dự án nắm bắt nhanh chóng cấu trúc và chức năng của hệ thống. 855 | 856 |
857 | 858 |
859 | 860 | Sơ đồ lớp 861 | 862 | Class Diagram (Sơ đồ lớp) là một trong những loại sơ đồ được sử dụng trong UML và nó đóng một vai trò quan trọng trong việc mô hình hóa hệ thống phần mềm. Class Diagram cung cấp một cái nhìn tổng quát về cấu trúc của một ứng dụng bằng cách hiển thị các lớp của hệ thống, các thuộc tính và phương thức của chúng, và mối quan hệ giữa các lớp đó. Các mối quan hệ này có thể bao gồm: liên kết (association), kế thừa (inheritance), thực thi (realization), phụ thuộc (dependency), kết tập (aggregation), tổng hợp (composition). 863 | 864 | ### 1. Ký hiệu lớp 865 | 866 | Một lớp đại diện cho một khái niệm bao gồm trạng thái (thuộc tính) và hành vi (phương thức). Mỗi thuộc tính có một kiểu. Mỗi phương thức có một chữ ký. Tên lớp là thông tin bắt buộc duy nhất. 867 | 868 | - Tên Lớp: Tên của lớp xuất hiện trong phần đầu tiên. 869 | - Thuộc tính Lớp: 870 | - Các thuộc tính được hiển thị trong phần thứ hai. 871 | - Kiểu thuộc tính được hiển thị sau dấu hai chấm. 872 | - Các thuộc tính tương ứng với các biến thành viên (các thành viên dữ liệu) trong mã. 873 | - Phương thức Lớp (Các phương thức): 874 | - Các phương thức được hiển thị trong phần thứ ba. Đây là các dịch vụ mà lớp cung cấp. 875 | - Kiểu trả về của phương thức được hiển thị sau dấu hai chấm ở cuối chữ ký phương thức. 876 | - Kiểu trả về của các tham số phương thức được hiển thị sau dấu hai chấm theo sau tên tham số. Các phương thức tương ứng với các phương thức lớp trong mã. 877 | 878 | Xét ví dụ sau: 879 | 880 |
881 | 882 | ![Minh họa 1](images/01.png) 883 | 884 |
885 | 886 | Sơ đồ UML này tương ứng với class `Animal` trong mã như sau: 887 | 888 | ```java 889 | public class Animal { 890 | // Các thuộc tính của lớp 891 | public String name; // Biến thành viên công khai cho tên 892 | private int age; // Biến thành viên riêng tư cho tuổi 893 | private String species; // Biến thành viên riêng tư cho loài 894 | 895 | // Phương thức getter cho thuộc tính name 896 | public String getName() { 897 | return name; 898 | } 899 | 900 | // Phương thức setter cho thuộc tính name 901 | public void setName(String name) { 902 | this.name = name; 903 | } 904 | } 905 | ``` 906 | 907 | ### 2. Ký hiệu khả năng truy cập 908 | 909 | Các ký hiệu `+`, `-` và `#` đặt trước tên thuộc tính và phương thức trong một lớp biểu thị mức độ khả năng truy cập của thuộc tính và phương thức đó. Cụ thể: 910 | 911 | - Dấu `+` biểu thị thuộc tính hoặc phương thức công khai (public). 912 | - Dấu `-` biểu thị thuộc tính hoặc phương thức riêng tư (private). 913 | - Dấu `#` biểu thị thuộc tính hoặc phương thức bảo vệ (protected). 914 | - Nếu không có ký hiệu gì, thì là mặc định (default). 915 | 916 | ### 3. Ký hiệu quan hệ giữa các lớp 917 | 918 |
919 | 920 | ![Minh họa 2](images/02.png) 921 | 922 |
923 | 924 | - **Liên kết (Association)** là mối quan hệ giữa hai lớp mà trong đó các đối tượng của một lớp kết nối với các đối tượng của lớp kia. Quan hệ này có thể là một chiều hoặc hai chiều. 925 | - **Kế thừa (Inheritance)** là quan hệ mà một lớp (lớp con) kế thừa các thuộc tính và phương thức từ một lớp khác (lớp cha). Lớp con cũng có thể thêm hoặc ghi đè các phương thức và thuộc tính của lớp cha. 926 | - **Thực thi (Realization)** là mối quan hệ giữa một lớp và một interface, nơi lớp đảm nhận trách nhiệm triển khai tất cả các phương thức được định nghĩa trong interface đó. Nếu có một interface `Flyable` có phương thức `fly()`, lớp `Bird` có thể thực thi interface này bằng cách triển khai phương thức `fly()`. 927 | - **Phụ thuộc (Dependency)** xảy ra khi một lớp sử dụng một lớp khác; ví dụ, như là tham số trong một phương thức. Sự thay đổi trong lớp được sử dụng có thể ảnh hưởng đến lớp sử dụng. 928 | - **Kết tập (Aggregation)** là một dạng đặc biệt của liên kết, mô tả mối quan hệ "có một" (has-a) nhưng không bắt buộc, và các đối tượng tồn tại độc lập với nhau. Một lớp `School` có thể có nhiều đối tượng của lớp `Teacher`, nhưng các giáo viên vẫn tồn tại độc lập với trường học. 929 | - **Tổng hợp (Composition)** cũng là một dạng đặc biệt của liên kết, thể hiện mối quan hệ "chứa" (contains) giữa các đối tượng, trong đó đối tượng con không tồn tại độc lập khi đối tượng cha bị hủy. Lớp `House` có thể chứa nhiều đối tượng của lớp `Room`. Khi đối tượng `House` bị hủy, các `Room` cũng sẽ bị hủy theo. 930 | 931 |
932 | 933 | Phân biệt Kế thừa và Kết tập 934 | 935 |
936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 |
Kế thừaKết tập
Định nghĩa- Kế thừa là một cơ chế trong đó một lớp (gọi là lớp con) có thể kế thừa tính chất và phương thức của một lớp khác (gọi là lớp cha).
- Cho phép lớp con tái sử dụng mã lệnh của lớp cha mà không cần phải viết lại.
- Tương ứng với mối quan hệ "is-a".
- Kết tập là một dạng của quan hệ "có" giữa hai lớp.
- Biểu thị một mối quan hệ trong đó một đối tượng có thể chứa hoặc tham chiếu đến đối tượng khác, nhưng hai đối tượng có thể tồn tại độc lập với nhau.
- Tương ứng với mối quan hệ "has-a".
Mục đích- Tái sử dụng mã lệnh và cải thiện tính mô-đun hóa của ứng dụng.
- Cũng hỗ trợ tính đa hình, cho phép phương thức của lớp con có thể ghi đè hoặc mở rộng phương thức của lớp cha.
- Ví dụ: Lớp Animal có phương thức move(), lớp Bird và Fish có thể kế thừa lớp Animal và sử dụng hoặc ghi đè phương thức move() để phù hợp với bản chất di chuyển của chúng.
- Biểu diễn mối quan hệ giữa các đối tượng, nơi các đối tượng có thể có mối quan hệ một phần với nhau nhưng không phụ thuộc hoàn toàn vào nhau.
- Giúp tăng cường sự linh hoạt trong cấu trúc dữ liệu và quản lý các đối tượng phức tạp.
- Ví dụ, lớp Square có thể có các đối tượng của lớp Point. Mỗi hình vuông bao gồm bốn đối tượng của lớp điểm (chỉ các đỉnh) nhưng các điểm có thể tồn tại độc lập mà không là đỉnh của hình vuông.
Sự phụ thuộc- Lớp con phụ thuộc mạnh mẽ vào lớp cha vì nó sử dụng trực tiếp các tính chất và phương thức của lớp cha. - Các đối tượng liên quan có thể tồn tại độc lập; chúng không phụ thuộc mạnh mẽ vào nhau.
Khả năng- Mở rộng hoặc tùy chỉnh lớp cha.
- Mô hình hóa mối quan hệ phân cấp.
- Quản lý các đối tượng có liên kết.
- Mô hình hóa mối quan hệ thực thể.
968 | 969 |
970 | 971 |
972 | 973 | Xét ví dụ sau đây: 974 | 975 |
976 | 977 | ![Minh họa 3](images/03.png) 978 | 979 |
980 | 981 | Triển khai sơ đồ UML này trong Java như sau: 982 | 983 | ```java 984 | public class Person { 985 | private String name; 986 | private int age; 987 | private Address address; // Liên kết sử dụng lớp Address 988 | 989 | public Person(String name, int age, Address address) { 990 | this.name = name; 991 | this.age = age; 992 | this.address = address; 993 | } 994 | 995 | // Getter và Setter cho các thuộc tính 996 | public String getName() { 997 | return name; 998 | } 999 | 1000 | public void setName(String name) { 1001 | this.name = name; 1002 | } 1003 | 1004 | public int getAge() { 1005 | return age; 1006 | } 1007 | 1008 | public void setAge(int age) { 1009 | this.age = age; 1010 | } 1011 | 1012 | public Address getAddress() { 1013 | return address; 1014 | } 1015 | 1016 | public void setAddress(Address address) { 1017 | this.address = address; 1018 | } 1019 | } 1020 | 1021 | // Định nghĩa lớp Student kế thừa từ Person 1022 | public class Student extends Person { 1023 | public Student(String name, int age, Address address) { 1024 | super(name, age, address); 1025 | } 1026 | } 1027 | 1028 | // Định nghĩa lớp Teacher kế thừa từ Person 1029 | public class Teacher extends Person { 1030 | public Teacher(String name, int age, Address address) { 1031 | super(name, age, address); 1032 | } 1033 | } 1034 | 1035 | // Định nghĩa lớp Staff kế thừa từ Person 1036 | public class Staff extends Person { 1037 | public Staff(String name, int age, Address address) { 1038 | super(name, age, address); 1039 | } 1040 | } 1041 | 1042 | // Định nghĩa lớp Address 1043 | public class Address { 1044 | private String street; 1045 | private String city; 1046 | private String country; 1047 | 1048 | public Address(String street, String city, String country) { 1049 | this.street = street; 1050 | this.city = city; 1051 | this.country = country; 1052 | } 1053 | 1054 | // Getter và Setter cho các thuộc tính 1055 | public String getStreet() { 1056 | return street; 1057 | } 1058 | 1059 | public void setStreet(String street) { 1060 | this.street = street; 1061 | } 1062 | 1063 | public String getCity() { 1064 | return city; 1065 | } 1066 | 1067 | public void setCity(String city) { 1068 | this.city = city; 1069 | } 1070 | 1071 | public String getCountry() { 1072 | return country; 1073 | } 1074 | 1075 | public void setCountry(String country) { 1076 | this.country = country; 1077 | } 1078 | } 1079 | 1080 | // Định nghĩa lớp Department chứa Teacher và Course 1081 | public class Department { 1082 | private String name; 1083 | private List teachers; // Aggregation 1084 | private List courses; // Composition 1085 | 1086 | public Department(String name) { 1087 | this.name = name; 1088 | this.teachers = new ArrayList<>(); 1089 | this.courses = new ArrayList<>(); 1090 | } 1091 | 1092 | // Phương thức thêm giáo viên và khóa học 1093 | public void addTeacher(Teacher teacher) { 1094 | teachers.add(teacher); 1095 | } 1096 | 1097 | public void addCourse(Course course) { 1098 | courses.add(course); 1099 | } 1100 | } 1101 | 1102 | // Định nghĩa lớp Course 1103 | public class Course { 1104 | private String courseName; 1105 | private List teachers; // Association 1106 | 1107 | public Course(String courseName) { 1108 | this.courseName = courseName; 1109 | this.teachers = new ArrayList<>(); 1110 | } 1111 | 1112 | public void addTeacher(Teacher teacher) { 1113 | teachers.add(teacher); 1114 | } 1115 | } 1116 | 1117 | // Định nghĩa interface Maintainable 1118 | public interface Maintainable { 1119 | void maintain(); 1120 | } 1121 | 1122 | // Định nghĩa lớp School 1123 | public class School implements Maintainable { 1124 | private String name; 1125 | private List departments; // Composition 1126 | 1127 | public School(String name) { 1128 | this.name = name; 1129 | this.departments = new ArrayList<>(); 1130 | } 1131 | 1132 | public void addDepartment(Department department) { 1133 | departments.add(department); 1134 | } 1135 | 1136 | // Triển khai phương thức từ interface Maintainable 1137 | @Override 1138 | public void maintain() { 1139 | System.out.println("Maintaining the school facilities..."); 1140 | // Logic bảo trì cơ sở vật chất có thể được thực hiện ở đây 1141 | } 1142 | } 1143 | ``` 1144 | 1145 |
1146 | 1147 | ## Tài liệu tham khảo 1148 | 1149 | - Patrick Niemeyer, Jonathan Knudsen; *Learning Java*; Third Edition. 1150 | - Marc Loy, Patrick Niemeyer, Jonathan Knudsen; *Learning Java: An Introduction to Real-World Programming with Java*; Fifth Edition. 1151 | -------------------------------------------------------------------------------- /documents/Effective Java.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/documents/Effective Java.pdf -------------------------------------------------------------------------------- /documents/Learning Java ~ An Introduction to Real-World Programming with Java.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/documents/Learning Java ~ An Introduction to Real-World Programming with Java.pdf -------------------------------------------------------------------------------- /documents/Learning Java.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/documents/Learning Java.pdf -------------------------------------------------------------------------------- /documents/Object-oriented programming and Java.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/documents/Object-oriented programming and Java.pdf -------------------------------------------------------------------------------- /images/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/images/01.png -------------------------------------------------------------------------------- /images/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/images/02.png -------------------------------------------------------------------------------- /images/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/images/03.png -------------------------------------------------------------------------------- /images/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/images/04.png -------------------------------------------------------------------------------- /images/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/images/05.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaiAu2501/Object-oriented-Programming-with-Java/d24c0dbf770fe70c971a6e462881911181699e94/images/logo.png --------------------------------------------------------------------------------