1. OOP (Object-Oriented Programming)
1.1. Class và Object
1.1.1. Class (Lớp)
1.1.1.1. Components (các thành phần)
1.1.1.1.1. Fields (Thuộc tính)
1.1.1.1.2. Methods (Phương thức)
1.1.1.1.3. Constructors (Hàm khởi tạo)
1.1.1.1.4. Getter và Setter
1.1.1.2. Định nghĩa
1.1.1.2.1. 1. Một lớp là một bản thiết kế hoặc khuôn mẫu (template or blueprint) cho các đối tượng
1.1.1.2.2. 2. Định nghĩa các thuộc tính (fields) và hành vi (methods) mà các đối tượng của lớp sẽ có
1.1.1.2.3. 3. Để định nghĩa một lớp, sử dụng từ khóa class theo sau là tên của lớp
1.1.1.3. Ví dụ
1.1.2. Object (Đối tượng)
1.1.2.1. Định nghĩa
1.1.2.1.1. 1. Đối tượng là một thực thể của lớp
1.1.2.1.2. 2. Mỗi đối tượng là một thể hiện (instance) cụ thể của một lớp với các giá trị thuộc tính riêng biệt
1.1.2.1.3. 3. Sử dụng từ khóa "new" để khởi tạo đối tượng
1.1.2.2. Ví dụ
1.2. Encapsulation (Đóng gói)
1.2.1. Định nghĩa
1.2.1.1. 1. Đóng gói là việc bảo vệ dữ liệu bằng cách ẩn đi các thông tin chi tiết bên trong đối tượng và chỉ cung cấp các phương thức để tương tác với dữ liệu đó
1.2.1.2. 2. Giúp bảo vệ dữ liệu khỏi sự truy cập và thay đổi từ bên ngoài một cách trực tiếp
1.2.1.3. 3. Tính đóng gói được thực hiện thông qua việc sử dụng các từ khóa như private, protected, và public
1.2.2. Access Modifier (Phạm vi truy cập)
1.2.2.1. public
1.2.2.1.1. 1. Có phạm vi truy cập rộng nhất, cho phép truy cập từ bất kỳ đâu
1.2.2.1.2. 2. Có thể được truy cập từ bên trong class, bên ngoài class, và bên ngoài package
1.2.2.2. private
1.2.2.2.1. 1. Có phạm vi truy cập hẹp nhất, chỉ cho phép truy cập từ bên trong của một lớp
1.2.2.2.2. 2. Không thể truy cập trực tiếp từ các lớp khác, kể cả các lớp con. Để truy cập hoặc sửa đổi các thành viên này, bạn cần định nghĩa các phương thức công khai (public methods) như getter và setter
1.2.2.3. protected
1.2.2.3.1. 1. Cho phép truy cập từ bên trong cùng một package hoặc từ các lớp con (subclasses) ở bất kỳ package nào khác
1.2.2.3.2. 2. Các thành phần protected chỉ có thể được truy cập bởi các lớp có quyền truy cập tương đương, giúp bảo vệ dữ liệu của lớp cha
1.2.2.4. default
1.2.2.4.1. 1. Nếu bạn không chỉ định bất kỳ cấp độ truy cập nào, mặc định sẽ là cấp độ default
1.2.2.4.2. 2. Cho phép truy cập từ bên trong cùng một package, nhưng không thể truy cập từ các package khác
1.2.3. Ví dụ
1.3. Inheritance (Kế thừa)
1.3.1. Định nghĩa
1.3.1.1. 1. Kế thừa cho phép một lớp con kế thừa các thuộc tính và phương thức của một lớp cha
1.3.1.2. 2. Giúp tái sử dụng mã nguồn và dễ dàng quản lý các đối tượng có tính chất tương tự
1.3.1.3. 3. Tính kế thừa được thực hiện bằng từ khóa extends
1.3.2. Ví dụ
1.4. Polymorphism (Đa hình)
1.4.1. Định nghĩa
1.4.1.1. 1. Đa hình cho phép một đối tượng, một phương thức có thể được xử lý dưới nhiều dạng khác nhau
1.4.1.2. 2. Giúp tăng tính linh hoạt, giúp giảm sự lặp lại mã và tạo ra mã nguồn dễ mở rộng
1.4.1.3. 3. Đa hình được thực hiện qua việc ghi đè phương thức (overriding) và nạp chồng phương thức (overloading)
1.4.2. Ví dụ
1.4.3. Overriding và Overloading
1.4.3.1. Overriding (Ghi đè)
1.4.3.1.1. 1. Cung cấp triển khai cụ thể cho phương thức trong lớp cha
1.4.3.1.2. 2. Sử dụng khi muốn thay đổi hoặc mở rộng cách một phương thức hoạt động trong lớp con
1.4.3.1.3. 3. Overriding xảy ra tại runtime (thời điểm chạy chương trình). Khi một phương thức bị ghi đè trong lớp con, phương thức ghi đè sẽ được gọi thay vì phương thức của lớp cha khi đối tượng thuộc lớp con được sử dụng. Quyết định phương thức nào sẽ được gọi được thực hiện tại thời điểm chạy chương trình
1.4.3.2. Overloading (Nạp chồng)
1.4.3.2.1. 1. Nhiều phương thức cùng tên nhưng khác số lượng hoặc kiểu tham số
1.4.3.2.2. 2. Overloading xảy ra tại compile-time (thời điểm biên dịch). Đây là quá trình mà trình biên dịch (compiler) quyết định phương thức nào sẽ được gọi dựa trên danh sách tham số (số lượng và kiểu tham số) của phương thức
1.5. Abstraction (Trừu tượng)
1.5.1. Định nghĩa
1.5.1.1. 1. Tính trừu tượng là ẩn các chi tiết triển khai và chỉ hiển thị tính năng tới người dùng, giúp tập trung vào những gì đối tượng làm thay vì nó làm như thế nào
1.5.1.2. 2. Tính trừu tượng được thực hiện thông qua các lớp trừu tượng (abstract class) và giao diện (interface)
1.5.2. Ví dụ
1.5.3. Abstract và Interface
1.5.3.1. Abstract
1.5.3.1.1. 1. Abstract không hỗ trợ đa kế thừa
1.5.3.1.2. 2. Abstract class có phương thức abstract (không có thân hàm) và phương thức non-abstract (có thân hàm)
1.5.3.1.3. 3. Sử dụng từ khóa abstract được sử dụng để khai báo
1.5.3.1.4. 4. Abstract class có thể có constructor, và các lớp con có thể gọi nó thông qua super()
1.5.3.1.5. 5. Ví dụ public abstract class AShape { public abstract void draw(); }
1.5.3.2. Interface
1.5.3.2.1. 1. Interface có hỗ trợ đa kế thừa (một lớp có thể triển khai nhiều interface cùng một lúc)
1.5.3.2.2. 2. Các phương thức trong interface mặc định là public
1.5.3.2.3. 3. Interface chỉ có phương thức abstract. Từ Java 8, có thêm các phương thức default và static
1.5.3.2.4. 4. Sử dụng từ khóa interface được sử dụng để khai báo
1.5.3.2.5. 5. Ví dụ public interface IShape { void draw(); }
2. Collection
2.1. List
2.1.1. Đặc điểm
2.1.1.1. Các phần tử được lưu trữ theo thứ tự chèn
2.1.1.2. Có thể chứa các phần tử trùng lặp
2.1.1.3. Truy cập các phần tử bằng chỉ số (index)
2.1.1.4. Các hiện thực phổ biến: ArrayList, LinkedList, Vector, Stack
2.1.2. ArrayList
2.1.2.1. Sử dụng một mảng động để lưu trữ các phần tử. Kích thước của mảng này có thể tự động tăng lên khi cần thiết
2.1.2.2. Truy cập phần tử theo chỉ số: Nhanh và hiệu quả (O(1))
2.1.2.3. Thêm/xóa phần tử: Chậm khi thêm/xóa ở giữa danh sách (O(n)) vì cần phải di chuyển các phần tử
2.1.2.4. List<String> arrayList = new ArrayList<>(); arrayList.add("Apple"); arrayList.add("Banana");
2.1.3. LinkedList
2.1.3.1. Sử dụng một danh sách liên kết kép (doubly linked list) để lưu trữ các phần tử
2.1.3.2. Mỗi phần tử là một nút (node) chứa dữ liệu và liên kết đến phần tử trước và sau
2.1.3.3. Truy cập phần tử theo chỉ số chậm hơn (O(n)) vì phải duyệt từ đầu danh sách
2.1.3.4. Thêm/xóa phần tử: Nhanh khi thêm/xóa ở đầu hoặc giữa danh sách (O(1) cho đầu/cuối, O(n) cho giữa)
2.1.3.5. List<String> linkedList = new LinkedList<>(); linkedList.add("Apple"); linkedList.add("Banana");
2.2. Set
2.2.1. Đặc điểm
2.2.1.1. Không chứa các phần tử trùng lặp
2.2.1.2. Không đảm bảo thứ tự của các phần tử (trừ LinkedHashSet và TreeSet)
2.2.1.3. Các hiện thực phổ biến: HashSet, LinkedHashSet, TreeSet
2.2.2. HashSet
2.2.2.1. HashSet sử dụng một bảng băm (hash table) để lưu trữ các phần tử
2.2.2.2. Không duy trì thứ tự của các phần tử
2.2.2.3. Yêu cầu: Các phần tử cần phải ghi đè phương thức hashCode() và equals() để hoạt động chính xác
2.2.2.4. Cho phép lưu trữ giá trị null
2.2.2.5. Set<String> hashSet = new HashSet<>(); hashSet.add("Apple"); hashSet.add("Banana");
2.2.3. TreeSet
2.2.3.1. Sử dụng một cây đỏ-đen (red-black tree) để lưu trữ các phần tử
2.2.3.2. Duy trì các phần tử theo thứ tự tự nhiên hoặc theo Comparator được cung cấp
2.2.3.3. Yêu cầu: Các phần tử cần phải triển khai giao diện Comparable hoặc cần cung cấp một Comparator khi tạo TreeSet
2.2.3.4. KHÔNG cho phép lưu trữ giá trị null
2.2.3.5. Set<String> treeSet = new TreeSet<>(); treeSet.add("Apple"); treeSet.add("Banana");
2.3. Map
2.3.1. Đặc điểm
2.3.1.1. Tập hợp các cặp khóa - giá trị (key-value)
2.3.1.2. KHÔNG cho phép các khóa trùng lặp và mỗi khóa chỉ có thể ánh xạ đến một giá trị duy nhất
2.3.1.3. Các hiện thực phổ biến: HashMap, LinkedHashMap, TreeMap, Hashtable
2.3.1.4. Các cặp khóa-giá trị (key-value) không có thứ tự (trừ LinkedHashMap và TreeMap)
2.3.2. HashMap
2.3.2.1. Sử dụng một bảng băm (hash table) để lưu trữ các cặp khóa - giá trị
2.3.2.2. KHÔNG duy trì thứ tự của các phần tử
2.3.2.3. Cho phép một khoá (key) null và nhiều giá trị (value) null
2.3.2.4. Yêu cầu: Các khóa phải ghi đè phương thức hashCode() và equals() để hoạt động chính xác
2.3.2.5. Map<String, Integer> hashMap = new HashMap<>(); hashMap.put("Apple", 10); hashMap.put("Banana", 20);
2.3.3. TreeMap
2.3.3.1. Sử dụng một cây đỏ - đen (red-black tree) để lưu trữ các cặp khóa - giá trị
2.3.3.2. Duy trì thứ tự của các phần tử theo thứ tự tự nhiên hoặc theo Comparator được cung cấp
2.3.3.3. Yêu cầu: Các khóa phải triển khai giao diện Comparable hoặc cần cung cấp một Comparator khi tạo TreeMap
2.3.3.4. KHÔNG cho phép khóa null, nhưng cho phép giá trị null
2.3.3.5. Map<String, Integer> treeMap = new TreeMap<>(); treeMap.put("Banana", 20); treeMap.put("Apple", 10);
3. Introduction Java (Giới thiệu về Java)
3.1. Giới thiệu về Java
3.1.1. 1. Java là một ngôn ngữ lập trình và nền tảng (platform) mạnh mẽ được phát triển bởi Sun Microsystems (hiện nay thuộc Oracle Corporation) và được giới thiệu vào năm 1995
3.1.2. 2. Java được thiết kế với mục tiêu cho phép các nhà phát triển viết chương trình một lần và chạy ở bất cứ đâu (write once, run anywhere - WORA), có nghĩa là các ứng dụng Java có thể chạy trên bất kỳ thiết bị nào hỗ trợ Java như Linux, MacOS, Windows
3.1.3. 3. Java là ngôn ngữ lập trình hướng đối tượng, nghĩa là mọi thứ trong Java đều được xem là một đối tượng. Điều này giúp tổ chức và quản lý mã nguồn dễ dàng hơn, tăng khả năng tái sử dụng và mở rộng
3.1.4. 4. Java hỗ trợ lập trình đa luồng (Multithreaded), cho phép nhiều luồng thực thi đồng thời, giúp cải thiện hiệu suất của các ứng dụng đa nhiệm
3.2. JDK, JRE và JVM
3.2.1. Java Development Kit (JDK)
3.2.1.1. 1. JDK là một bộ công cụ phát triển phần mềm cần thiết để phát triển các ứng dụng Java
3.2.1.2. 2. JDK bao gồm một tập hợp các công cụ và thư viện giúp các nhà phát triển viết, biên dịch, gỡ lỗi và chạy các chương trình Java (Java Compiler (javac), JRE, JVM...)
3.2.2. Java Runtime Environment (JRE)
3.2.2.1. 1. JRE là một môi trường thực thi cung cấp các thư viện và các thành phần cần thiết để chạy các ứng dụng Java
3.2.2.2. 2. JRE bao gồm JVM và các thư viện lõi của Java
3.2.3. Java Virtual Machine (JVM)
3.2.3.1. 1. JVM là một máy ảo cho phép các chương trình Java chạy trên bất kỳ nền tảng nào
3.2.3.2. 2. JVM chuyển đổi bytecode Java thành mã máy và thực thi nó
3.3. Qui tắc đặt tên
3.3.1. Tên biến (Variable Names)
3.3.1.1. 1. Tên biến nên được viết theo kiểu camel case, bắt đầu bằng chữ cái viết thường và mỗi từ tiếp theo bắt đầu bằng chữ cái viết hoa
3.3.1.2. 2. Tên biến nên rõ ràng và mô tả ý nghĩa của biến đó
3.3.2. Tên lớp (Class Names)
3.3.2.1. 1. Tên lớp nên được viết theo kiểu Pascal case, bắt đầu bằng chữ cái viết hoa và mỗi từ tiếp theo cũng bắt đầu bằng chữ cái viết hoa
3.3.2.2. 2. Tên lớp thường là danh từ hoặc cụm danh từ, vì chúng đại diện cho các đối tượng
3.3.3. Tên phương thức (Method Names)
3.3.3.1. 1. Tên phương thức nên được viết theo kiểu camel case, bắt đầu bằng chữ cái viết thường và mỗi từ tiếp theo bắt đầu bằng chữ cái viết hoa
3.3.3.2. 2. Tên phương thức thường là động từ hoặc cụm động từ, vì chúng đại diện cho hành động hoặc chức năng
3.3.4. Tên hằng số (Constant Names)
3.3.4.1. 1. Tên hằng số nên được viết bằng chữ hoa và các từ được phân tách bởi dấu gạch dưới
3.3.4.2. 2. Tên hằng số nên rõ ràng và mô tả giá trị của hằng số đó
3.3.5. Tên gói (Package Names)
3.3.5.1. 1. Tên gói nên được viết bằng chữ thường
3.3.5.2. 2. Thông thường, tên gói bắt đầu với tên miền đảo ngược của tổ chức hoặc công ty, sau đó là tên dự án hoặc mô-đun
3.3.6. Tên tham số (Parameter Names)
3.3.6.1. 1. Tên tham số nên được viết theo kiểu camel case, bắt đầu bằng chữ cái viết thường và mỗi từ tiếp theo bắt đầu bằng chữ cái viết hoa
3.3.6.2. 2. Tên tham số nên rõ ràng và mô tả dữ liệu mà tham số đó đại diện
3.4. Kiểu dữ liệu
3.4.1. Kiểu dữ liệu nguyên thủy (Primitive Data Types)
3.4.1.1. Số nguyên (Integer Types)
3.4.1.1.1. byte
3.4.1.1.2. short
3.4.1.1.3. int
3.4.1.1.4. long
3.4.1.2. Số thực (Floating-point Types)
3.4.1.2.1. float
3.4.1.2.2. double
3.4.1.3. Ký tự (Character Type)
3.4.1.3.1. char
3.4.1.4. Logic (Boolean Type)
3.4.1.4.1. boolean
3.4.2. Kiểu dữ liệu tham chiếu (Reference Data Types)
3.4.2.1. Đối tượng (Objects)
3.4.2.1.1. Đối tượng được tạo từ các lớp và có thể chứa các biến (fields) và phương thức (methods)
3.4.2.1.2. Ví dụ: Student st = new Student();
3.4.2.2. Mảng (Arrays)
3.4.2.2.1. Mảng là một đối tượng có thể chứa nhiều giá trị cùng kiểu dữ liệu
3.4.2.2.2. Ví dụ: int[] arr = {1, 2, 3, 4, 5}; String[] names = {"Alice", "Bob", "Charlie"};
3.4.2.3. Boxing và Unboxing
3.4.2.3.1. Các kiểu dữ liệu nguyên thủy có thể được chuyển đổi thành các đối tượng tương ứng (boxing) và ngược lại (unboxing)
3.4.2.3.2. Boxing
3.4.2.3.3. Unboxing
3.4.2.4. Integer, Double, Boolean...
3.4.2.4.1. Các lớp Integer, Double, và các lớp khác trong gói java.lang là các lớp bao (Wrapper Class) cho các kiểu dữ liệu nguyên thủy tương ứng int, double, và các kiểu dữ liệu nguyên thủy khác
3.4.2.4.2. Các lớp bao này cung cấp các phương thức và thuộc tính để làm việc với các giá trị của kiểu dữ liệu nguyên thủy đó như các đối tượng
3.4.2.4.3. Lớp Integer là lớp bao cho kiểu dữ liệu nguyên thủy int
3.4.2.4.4. Lớp Double là lớp bao cho kiểu dữ liệu nguyên thủy double
3.4.2.4.5. Lớp Boolean là lớp bao cho kiểu dữ liệu nguyên thủy boolean
3.4.2.4.6. Lớp Character là lớp bao cho kiểu dữ liệu nguyên thủy char
3.4.2.4.7. Lớp Long là lớp bao cho kiểu dữ liệu nguyên thủy long
3.4.2.4.8. Lớp Float là lớp bao cho kiểu dữ liệu nguyên thủy float
3.4.3. Bộ nhớ Heap và Stack
3.4.3.1. Static Area
3.4.3.1.1. Các biến tĩnh được lưu trữ trong vùng bộ nhớ Static Area (hay còn gọi là Method Area)
3.4.3.1.2. Chúng được chia sẻ bởi tất cả các đối tượng của lớp và có thể truy cập trực tiếp thông qua tên lớp mà không cần tạo đối tượng
3.4.3.2. Constant Pool
3.4.3.2.1. Các hằng số được lưu trữ trong Constant Pool, một phần của vùng bộ nhớ Runtime Constant Pool trong bộ nhớ của JVM
3.4.3.2.2. Hằng số là các giá trị không thay đổi trong suốt vòng đời của chương trình
3.4.3.3. Heap
3.4.3.3.1. Heap là một vùng nhớ trong bộ nhớ của máy tính được sử dụng để lưu trữ các đối tượng và dữ liệu được cấp phát động trong quá trình chạy của chương trình
3.4.3.3.2. Các đối tượng được cấp phát trong Heap tồn tại cho đến khi không còn tham chiếu nào trỏ đến chúng và bị thu gom rác (Garbage Collected) để giải phóng bộ nhớ
3.4.3.3.3. Các đối tượng trong Heap có thể được chia sẻ và truy cập từ nhiều phần của chương trình
3.4.3.3.4. Heap thường được sử dụng để lưu trữ các đối tượng như đối tượng của lớp, mảng, các đối tượng được cấp phát động.
3.4.3.4. Stack
3.4.3.4.1. Stack là một vùng nhớ trong bộ nhớ của máy tính được sử dụng để lưu trữ các biến cục bộ và thông tin liên quan đến thực thi hàm của chương trình
3.4.3.4.2. Dùng để lưu trữ biến cục bộ, tham số hàm và các thông tin thực thi, tồn tại trong thời gian thực thi của hàm và được tự động giải phóng khi hàm kết thúc
3.5. Phạm vi của biến
3.5.1. Biến cục bộ (Local Variables)
3.5.1.1. Biến cục bộ được khai báo bên trong một phương thức, constructor hoặc khối code (block)
3.5.1.2. Chỉ có thể sử dụng trong phương thức, constructor hoặc khối code mà nó được khai báo
3.5.1.3. Biến cục bộ tồn tại và có giá trị từ lúc khởi tạo đến khi thoát khỏi phương thức, constructor hoặc khối code
3.5.1.4. Ví dụ
3.5.2. Biến thành viên (Instance Variables)
3.5.2.1. Biến thành viên được khai báo trong một lớp như một phần của lớp đó, bên ngoài các phương thức
3.5.2.2. Có thể sử dụng trong bất kỳ phương thức, constructor hoặc khối code nào của lớp đó
3.5.2.3. Biến thành viên tồn tại cùng với đối tượng và được khởi tạo khi đối tượng được tạo ra, và tồn tại cho đến khi đối tượng bị thu hồi
3.5.2.4. Ví dụ
3.5.3. Biến tĩnh (Static Variables)
3.5.3.1. Biến tĩnh được khai báo bên trong một lớp với từ khóa static, và là biến của lớp chứ không phải của từng đối tượng
3.5.3.2. Có thể sử dụng trong bất kỳ phương thức, constructor hoặc khối code nào của lớp đó thông qua tên lớp
3.5.3.3. Biến tĩnh tồn tại cùng với lớp và được khởi tạo khi lớp được nạp vào bộ nhớ, và tồn tại cho đến khi lớp bị xóa khỏi bộ nhớ
3.5.3.4. Ví dụ
3.6. Type Casting (Ép kiểu)
3.6.1. Ép kiểu ngầm định (Implicit Casting)
3.6.1.1. Thực hiện ép kiểu ngầm định tự động khi không có mất mát dữ liệu
3.6.1.2. Xảy ra khi gán một giá trị của kiểu dữ liệu nhỏ hơn vào một biến của kiểu dữ liệu lớn hơn
3.6.1.3. Ví dụ: int numInt = 10; double numDouble = numInt; // Ép kiểu ngầm định từ int sang double
3.6.2. Ép kiểu tường minh (Explicit Casting)
3.6.2.1. Ép kiểu này phải được thực hiện một cách rõ ràng bởi lập trình viên
3.6.2.2. Xảy ra khi bạn gán một giá trị của kiểu dữ liệu lớn hơn vào một biến của kiểu dữ liệu nhỏ hơn
3.6.2.3. Có thể dẫn đến mất mát dữ liệu
3.6.2.4. Ví dụ: double numDouble = 10.5; int numInt = (int) numDouble; // Ép kiểu tường minh từ double sang int
3.7. Từ khoá
3.7.1. final
3.7.1.1. final là một từ khoá (keyword)
3.7.1.2. Sử dụng để khai báo một hằng số (constant), một biến, hoặc một phương thức không thể thay đổi sau khi được khởi tạo hoặc gán giá trị
3.7.1.3. Khi một biến được khai báo là final, giá trị của nó không thể thay đổi
3.7.2. static
3.7.2.1. Được sử dụng để chỉ ra rằng thành phần (biến, phương thức, lớp con) thuộc về lớp chứ không phải của từng đối tượng cụ thể của lớp đó
3.7.2.2. Có thể được áp dụng cho biến, phương thức, khối mã (block) và lớp
3.7.2.3. Sử dụng static khi bạn muốn chia sẻ dữ liệu hoặc hành vi giữa các đối tượng của cùng một lớp hoặc khi không cần phải tạo đối tượng mà vẫn có thể truy cập
3.7.3. finally
3.7.3.1. finally là một khối mã (block) trong một câu lệnh try-catch-finally
3.7.3.2. Được sử dụng để chứa các câu lệnh mà bạn muốn luôn được thực thi, bất kể có xảy ra exception hay không trong khối try-catch
3.7.3.3. Thường được sử dụng để giải phóng tài nguyên, như đóng các luồng hoặc đóng kết nối cơ sở dữ liệu
3.7.4. finalize
3.7.4.1. Là một phương thức
3.7.4.2. Sử dụng để tự động giải phóng tài nguyên hoặc thực hiện các hoạt động dọn dẹp trước khi đối tượng bị thu hồi
3.7.4.3. Thay vì sử dụng finalize(), nên sử dụng các cơ chế như try-with-resources để tự động giải phóng tài nguyên. Điều này giúp mã của bạn trở nên an toàn hơn và dễ bảo trì hơn
3.8. Loop (Vòng lặp)
3.8.1. for
3.8.1.1. Dùng để lặp qua một tập hợp các phần tử hoặc thực hiện một khối mã một số lần cố định
3.8.1.2. Ví dụ: for (int i = 0; i < 5; i++) { System.out.println("Iteration " + i); }
3.8.2. while
3.8.2.1. Thực hiện một khối mã cho đến khi một điều kiện không còn đúng nữa
3.8.2.2. Ví dụ: int i = 0; while (i < 5) { System.out.println("Iteration " + i); i++; }
3.8.3. do-while
3.8.3.1. Tương tự như vòng lặp while, nhưng sẽ thực hiện ít nhất một lần trước khi kiểm tra điều kiện
3.8.3.2. Ví dụ: int i = 0; do { System.out.println("Iteration " + i); i++; } while (i < 5);
3.9. Array (Mảng)
3.9.1. Mảng là một cấu trúc dữ liệu dùng để lưu trữ một tập hợp các phần tử cùng kiểu dữ liệu trong một không gian liên tục
3.9.2. Mảng có kích thước cố định, nghĩa là số lượng phần tử mà nó có thể chứa phải được xác định trước và không thể thay đổi sau khi mảng được tạo
3.9.3. Ví dụ: int[] numbers; int[] numbers = new int[5]; // Mảng số nguyên có 5 phần tử int[] numbers = {1, 2, 3, 4, 5}; // Khởi tạo mảng số nguyên với các giá trị ban đầu
3.9.4. Sử dụng chỉ số (index) của mảng để truy cập và sửa đổi phần tử. Chỉ số bắt đầu từ 0 đến (độ dài mảng - 1)
3.9.5. Để lấy độ dài của mảng, bạn có thể sử dụng thuộc tính length
3.9.6. Mảng Một Chiều (One-Dimensional Arrays)
3.9.6.1. Mảng một chiều là một danh sách các phần tử có cùng kiểu dữ liệu được lưu trữ theo một chiều
3.9.6.2. Ví dụ: int[] numbers = new int[5]; // Mảng số nguyên có kích thước 5 double[] prices = {10.5, 20.3, 15.8, 30.2}; // Mảng số thực có kích thước 4
3.9.7. Mảng Đa Chiều (Multi-Dimensional Arrays)
3.9.7.1. Mảng đa chiều là một mảng có nhiều chiều. Bạn có thể tạo mảng đa chiều bằng cách sử dụng mảng của mảng
3.9.7.2. Ví dụ: int[][] matrix = new int[3][3]; // Mảng đa chiều 3x3 double[][][] cube = new double[2][2][2]; // Mảng đa chiều 3 chiều có kích thước 2x2x2
3.10. String (Chuỗi)
3.10.1. String là một lớp được sử dụng để đại diện cho chuỗi các ký tự
3.10.2. Có thể khởi tạo một đối tượng String bằng cách sử dụng từ khóa new hoặc bằng cách gán giá trị trực tiếp cho một biến String
3.10.3. Một đối tượng String trong Java là Immutable, nghĩa là một khi đã được tạo ra, giá trị của nó không thể thay đổi
3.10.4. Bất kỳ thay đổi nào trên một đối tượng String đều tạo ra một đối tượng mới
3.10.5. Để so sánh hai đối tượng String, bạn không nên sử dụng toán tử "==" mà nên sử dụng phương thức "equals()"
3.10.6. Đối với các thao tác cần thay đổi nhiều lần trên chuỗi, bạn nên sử dụng StringBuilder hoặc StringBuffer thay vì String vì chúng là Mutable
3.10.7. Lớp String cung cấp nhiều phương thức để thực hiện các thao tác trên chuỗi như so sánh, cắt, nối, tách, chuyển đổi kiểu dữ liệu...
3.10.8. Ví dụ: String str1 = "Hello"; // Khởi tạo bằng cách gán giá trị trực tiếp String str2 = new String("World"); // Khởi tạo bằng cách sử dụng từ khóa new StringBuilder sb = new StringBuilder(); sb.append("Hello"); sb.append(" World"); String result = sb.toString(); // "Hello World"
3.10.9. Method
3.10.9.1. length()
3.10.9.1.1. length(): Trả về độ dài của chuỗi
3.10.9.1.2. String str = "Hello"; int length = str.length(); // 5
3.10.9.2. isEmpty()
3.10.9.2.1. Kiểm tra chuỗi có rỗng hay không
3.10.9.2.2. String str = ""; boolean isEmpty = str.isEmpty(); // true
3.10.9.3. contains(CharSequence s)
3.10.9.3.1. Kiểm tra chuỗi có chứa chuỗi con nhất định hay không
3.10.9.3.2. String str = "Hello, world!"; boolean contains = str.contains("world"); // true
3.10.9.4. equals(Object anObject)
3.10.9.4.1. So sánh chuỗi với một đối tượng khác
3.10.9.4.2. String str1 = "Hello"; String str2 = "Hello"; boolean isEqual = str1.equals(str2); // true
3.10.9.5. equalsIgnoreCase(String anotherString)
3.10.9.5.1. So sánh chuỗi với một chuỗi khác, không phân biệt chữ hoa chữ thường
3.10.9.5.2. String str1 = "Hello"; String str2 = "hello"; boolean isEqualIgnoreCase = str1.equalsIgnoreCase(str2); // true
3.10.9.6. substring(int beginIndex) substring(int beginIndex, int endIndex)
3.10.9.6.1. Trích xuất một phần của chuỗi từ chỉ số bắt đầu đến chỉ số kết thúc
3.10.9.6.2. String str = "Hello, world!"; String substr = str.substring(7, 12); // "world"
3.10.9.7. toLowerCase()
3.10.9.7.1. Chuyển đổi chuỗi thành chữ thường
3.10.9.7.2. String str = "Hello, WORLD!"; String lowerStr = str.toLowerCase(); // "hello, world!"
3.10.9.8. toUpperCase()
3.10.9.8.1. Chuyển đổi chuỗi thành chữ hoa
3.10.9.8.2. String str = "Hello, world!"; String upperStr = str.toUpperCase(); // "HELLO, WORLD!"
3.10.9.9. trim()
3.10.9.9.1. Loại bỏ khoảng trắng ở đầu và cuối chuỗi
3.10.9.9.2. String str = " Hello, world! "; String trimmedStr = str.trim(); // "Hello, world!"
3.10.9.10. replace(char oldChar, char newChar)
3.10.9.10.1. Thay thế tất cả các ký tự xuất hiện của ký tự cũ bằng ký tự mới
3.10.9.11. split(String regex)
3.10.9.11.1. Chia chuỗi thành mảng các chuỗi con dựa trên biểu thức chính quy
3.10.9.11.2. String str = "apple,banana,orange"; String[] fruits = str.split(","); // ["apple", "banana", "orange"]
3.10.9.12. concat(String str)
3.10.9.12.1. Nối chuỗi này với chuỗi khác
3.10.9.12.2. String str1 = "Hello"; String str2 = " world"; String concatenatedStr = str1.concat(str2); // "Hello world"
3.10.9.13. compareTo(String anotherString)
3.10.9.13.1. Sử dụng để xác định thứ tự của các chuỗi
3.10.9.13.2. 0: Nếu hai chuỗi bằng nhau
3.10.9.13.3. Âm (negative): Nếu chuỗi hiện tại đứng trước
3.10.9.13.4. Dương (positive): Nếu chuỗi hiện tại đứng sau
3.10.9.13.5. String str1 = "abc"; String str2 = "def"; int result = str1.compareTo(str2); // -3
3.10.9.14. startsWith(String prefix)
3.10.9.14.1. Kiểm tra chuỗi có bắt đầu với tiền tố nhất định hay không
3.10.9.14.2. String str = "Hello, world!"; boolean starts = str.startsWith("Hello"); // true
3.10.9.15. endsWith(String suffix)
3.10.9.15.1. Kiểm tra chuỗi có kết thúc với hậu tố nhất định hay không
3.10.9.15.2. String str = "Hello, world!"; boolean ends = str.endsWith("world!"); // true
4. JDBC
4.1. Đặc điểm
4.1.1. JDBC (Java Database Connectivity) là một API chuẩn của Java, cho phép Java ứng dụng tương tác với các cơ sở dữ liệu quan hệ (RDBMS)
4.1.2. JDBC cung cấp các lớp và giao diện để thực hiện các thao tác như kết nối với cơ sở dữ liệu, gửi truy vấn SQL, và xử lý kết quả truy vấn
4.1.3. JDBC hỗ trợ MySQL, PostgreSQL, Oracle... thông qua các driver JDBC được cung cấp
4.2. Thành phần trong JDBC
4.2.1. DriverManager
4.2.1.1. DriverManager chịu trách nhiệm kết nối ứng dụng Java với cơ sở dữ liệu thông qua các driver thích hợp
4.2.1.2. DriverManager.getConnection(url, user, password);
4.2.2. Driver
4.2.2.1. Mỗi loại cơ sở dữ liệu có một driver riêng để quản lý kết nối và giao tiếp với cơ sở dữ liệu đó
4.2.2.2. Các driver JDBC thường tự động được tải và đăng ký khi driver nằm trong classpath (JDBC 4.0 trở lên)
4.2.3. Connection
4.2.3.1. Đại diện cho một kết nối tới cơ sở dữ liệu
4.2.3.2. Cung cấp các phương thức để tạo ra các đối tượng Statement và quản lý các giao dịch
4.2.3.3. Connection connection = DriverManager.getConnection(url, user, password);
4.2.4. Statement
4.2.4.1. Statement
4.2.4.1.1. Sử dụng để gửi các truy vấn SQL tĩnh tới cơ sở dữ liệu
4.2.4.1.2. KHÔNG thể xử lý các tham số động
4.2.4.1.3. Nguy cơ bị tấn công SQL Injection
4.2.4.1.4. Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
4.2.4.2. PreparedStatement
4.2.4.2.1. PreparedStatement thường nhanh hơn Statement vì nó đã được biên dịch sẵn khi được tạo và có thể được sử dụng nhiều lần với các tham số khác nhau, giảm thiểu thời gian biên dịch lại truy vấn
4.2.4.2.2. Mở rộng của Statement
4.2.4.2.3. Cho phép gửi các truy vấn SQL có tham số
4.2.4.2.4. Giúp bảo vệ ứng dụng khỏi các cuộc tấn công SQL Injection
4.2.4.2.5. PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM users WHERE id = ?"); preparedStatement.setInt(1, userId); ResultSet resultSet = preparedStatement.executeQuery();
4.2.4.3. CallableStatement
4.2.4.3.1. Được sử dụng để gọi các Stored Procedure trong cơ sở dữ liệu
4.2.4.3.2. Đối với Stored Procedure, bạn sử dụng cú pháp: {call procedure_name(?, ?, ...)}
4.2.4.3.3. Đối với Function, bạn sử dụng cú pháp: {? = call function_name(?, ?, ...)}
4.2.5. ResultSet
4.2.5.1. Cung cấp các phương thức để truy cập và xử lý dữ liệu trả về từ cơ sở dữ liệu
4.2.5.2. Duyệt qua các bản ghi: Sử dụng các phương thức next() để di chuyển con trỏ đến bản ghi tiếp theo trong kết quả
4.2.5.3. Trích xuất dữ liệu từ các cột: Sử dụng các phương thức như getInt(), getString(), getDouble() để trích xuất dữ liệu từ các cột của bản ghi hiện tại
4.2.5.4. while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); }
4.2.6. SQLException
4.2.6.1. Đại diện cho các lỗi liên quan đến cơ sở dữ liệu
4.2.6.2. SQLException được ném ra để thông báo các vấn đề xảy ra trong quá trình giao tiếp với cơ sở dữ liệu
4.2.6.3. try { Connection connection = DriverManager.getConnection(url, user, password); } catch (SQLException e) { e.printStackTrace(); }
4.2.7. Các phương thức execute()
4.2.7.1. execute()
4.2.7.1.1. Thực thi một câu lệnh SQL bất kỳ, bao gồm cả các loại câu lệnh SELECT, INSERT, UPDATE, DELETE và các loại khác
4.2.7.1.2. Trả về một giá trị boolean, true nếu kết quả của câu lệnh là một ResultSet (truy vấn SELECT), và false nếu câu lệnh trả về một kết quả khác (như câu lệnh UPDATE, INSERT, DELETE)
4.2.7.1.3. Statement statement = connection.createStatement(); boolean hasResultSet = statement.execute("SELECT * FROM users"); if (hasResultSet) { ResultSet resultSet = statement.getResultSet(); // Xử lý kết quả truy vấn SELECT } else { int rowsAffected = statement.getUpdateCount(); // Xử lý số lượng hàng bị ảnh hưởng bởi câu lệnh UPDATE, INSERT, DELETE }
4.2.7.2. executeQuery()
4.2.7.2.1. Sử dụng để thực thi các truy vấn SELECT và trả về một đối tượng ResultSet, chứa kết quả của truy vấn
4.2.7.2.2. Dùng cho các câu lệnh SELECT, trả về dữ liệu từ cơ sở dữ liệu
4.2.7.2.3. Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT * FROM users"); while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); String email = resultSet.getString("email"); System.out.println("ID: " + id + ", Name: " + name + ", Email: " + email); }
4.2.7.3. executeUpdate()
4.2.7.3.1. Được sử dụng để thực thi các câu lệnh SQL như INSERT, UPDATE, DELETE và trả về số lượng bản ghi bị ảnh hưởng (affected rows) bởi câu lệnh
4.2.7.3.2. Trả về một số nguyên là số lượng hàng bị ảnh hưởng bởi câu lệnh UPDATE, INSERT, DELETE
4.2.7.3.3. Statement statement = connection.createStatement(); int rowsAffected = statement.executeUpdate("UPDATE users SET email = '[email protected]' WHERE id = 1"); System.out.println("Số bản ghi được cập nhật: " + rowsAffected);
4.3. SQL Injection
4.3.1. SQL Injection là một kỹ thuật tấn công phổ biến được sử dụng để xâm nhập vào hệ thống thông qua việc chèn mã SQL độc hại vào các trường đầu vào của ứng dụng web hoặc ứng dụng cơ sở dữ liệu
4.3.2. PreparedStatement giúp phòng ngừa các cuộc tấn công SQL Injection bằng cách cho phép sử dụng tham số trong truy vấn SQL thay vì nối chuỗi thủ công
5. Exception Handing
5.1. Đặc điểm
5.1.1. Exception Handling trong Java là cách xử lý và ứng phó với các tình huống ngoại lệ (exception) xảy ra trong quá trình thực thi chương trình
5.1.2. Một ngoại lệ xảy ra khi một vấn đề không mong muốn xảy ra trong quá trình thực thi chương trình, gây ra sự cố và làm ảnh hưởng có thể dừng chương trình hoặc ảnh hưởng luồng chương trình
5.1.3. Exception Handling giúp làm cho chương trình trở nên ổn định hơn và dễ bảo trì hơn bằng cách xử lý các tình huống ngoại lệ một cách cẩn thận và điều chỉnh luồng chương trình một cách an toàn khi có lỗi xảy ra
5.2. Exception
5.2.1. Checked Exception
5.2.1.1. Là các ngoại lệ mà trình biên dịch yêu cầu bắt buộc phải xử lý
5.2.1.2. Ví dụ: IOException, SQLException...
5.2.2. Errors
5.2.2.1. Đại diện cho các lỗi nghiêm trọng mà bạn thường không cần phải xử lý trong code
5.2.2.2. Ví dụ: OutOfMemoryError, StackOverflowError
5.2.3. Unchecked Exception (Runtime Exception)
5.2.3.1. Là các ngoại lệ không được yêu cầu bắt buộc xử lý tại thời điểm biên dịch
5.2.3.2. Ví dụ: NullPointerException, ArrayIndexOutOfBoundsException...
5.3. Khối try-catch
5.3.1. try: Được sử dụng để bao bọc một khối mã có thể ném ra ngoại lệ
5.3.2. catch: Được sử dụng để xử lý ngoại lệ. Mỗi catch xử lý một loại ngoại lệ cụ thể
5.3.3. try { // Khối mã có thể ném ra ngoại lệ int result = 10 / 0; // ArithmeticException } catch (ArithmeticException e) { // Xử lý ngoại lệ ArithmeticException System.out.println("Lỗi chia cho số 0: " + e.getMessage()); }
5.4. Khối finally
5.4.1. Khối mã luôn được thực thi sau khi khối try hoặc catch kết thúc, dù có ngoại lệ xảy ra hay không
5.4.2. Sử dụng để giải phóng tài nguyên như đóng kết nối cơ sở dữ liệu
5.4.3. Connection connection = null; try { connection = DriverManager.getConnection(url, username, password); // Thực thi các thao tác với cơ sở dữ liệu } catch (SQLException e) { System.out.println("Lỗi kết nối đến cơ sở dữ liệu: " + e.getMessage()); } finally { // Đóng kết nối dù có ngoại lệ hay không try { if (connection != null) connection.close(); } catch (SQLException e) { System.out.println("Lỗi khi đóng kết nối: " + e.getMessage()); } }
5.5. throw và throws
5.5.1. throw
5.5.1.1. Sử dụng throw để tạo ra một đối tượng ngoại lệ và ném nó ra khỏi phương thức hiện tại
5.5.1.2. Cú pháp: throw new ExceptionType("Optional message");
5.5.1.3. public void validateAge(int age) { if (age < 0) { throw new IllegalArgumentException("Tuổi không được là số âm"); } // Các công việc khác khi tuổi hợp lệ }
5.5.2. throws
5.5.2.1. Khai báo rằng phương thức này có thể ném ra một hoặc nhiều loại ngoại lệ
5.5.2.2. Trong khai báo phương thức để chỉ ra phương thức có thể ném ra ngoại lệ và bắt buộc bất kỳ phương thức nào gọi phương thức này cũng phải xử lý hoặc tiếp tục ném ngoại lệ
5.5.2.3. public void readFile(String fileName) throws IOException { // Đọc file và xử lý các ngoại lệ liên quan tới file }
5.6. Custom Exception
5.6.1. Bạn có thể tự định nghĩa các ngoại lệ (custom exceptions) để xử lý các tình huống đặc biệt hoặc lỗi cụ thể mà không phù hợp với các ngoại lệ có sẵn
5.6.2. Giúp cho mã của bạn có thể xử lý các tình huống đặc biệt một cách rõ ràng và linh hoạt hơn
5.6.3. Định nghĩa một lớp kế thừa từ lớp Exception hoặc các lớp con của Exception (ví dụ như RuntimeException) để tạo ra ngoại lệ của riêng bạn
5.6.4. // Định nghĩa một ngoại lệ checked exception public class InvalidAgeException extends Exception { public InvalidAgeException() { super("Tuổi không hợp lệ"); } public InvalidAgeException(String message) { super(message); } public InvalidAgeException(String message, Throwable cause) { super(message, cause); } }
6. JAVA IO
6.1. Đặc điểm
6.1.1. Java IO (Input/Output) là một phần quan trọng của ngôn ngữ lập trình Java, cung cấp các cơ chế để xử lý dữ liệu đầu vào và đầu ra từ và đến nhiều nguồn khác nhau như tệp tin, mạng, hoặc các luồng dữ liệu khác
6.1.2. Java cung cấp một số lớp và giao diện để làm việc với các hoạt động IO, bao gồm đọc và ghi dữ liệu, xử lý tệp tin, và truy cập mạng
6.1.3. Có hai loại chính của luồng (streams) là Byte Streams và Character Streams, được sử dụng để thực hiện các hoạt động đầu vào và đầu ra với dữ liệu dưới dạng byte và ký tự tương ứng
6.2. Byte Streams (luồng byte)
6.2.1. Đặc điểm
6.2.1.1. Để đọc và ghi dữ liệu dưới dạng byte trong Java, bạn có thể sử dụng các lớp Byte Streams như InputStream và OutputStream cùng với các lớp con của chúng như FileInputStream, FileOutputStream, BufferedInputStream, và BufferedOutputStream
6.2.2. InputStream và OutputStream
6.2.2.1. Là các lớp cơ sở trừu tượng cho các luồng byte đầu vào và đầu ra, tương ứng
6.3. Character Streams (luồng ký tự)
6.3.1. Đặc điểm
6.3.1.1. Phù hợp cho việc làm việc với các tệp tin văn bản, cần xử lý các ký tự Unicode
6.3.1.2. Để đọc và ghi dữ liệu dưới dạng ký tự trong Java, bạn có thể sử dụng các lớp Character Streams như Reader và Writer cùng với các lớp con của chúng như FileReader, FileWriter, BufferedReader, và BufferedWriter
6.3.1.3. Để tăng hiệu suất, chúng ta sử dụng một BufferedWriter để ghi dữ liệu, sử dụng một BufferedReader để đọc dữ liệu
6.3.2. Reader và Writer
6.3.2.1. Là các lớp cơ sở trừu tượng cho các luồng ký tự đầu vào và đầu ra, tương ứng