[{"content":" Chào mừng các bạn đã quay trở lại với blog! 👋\nĐây là series bài viết \u0026ldquo;Java Cho Người Mới Bắt Đầu\u0026rdquo;, được thiết kế để dẫn dắt bạn đi từ những khái niệm đầu tiên cho đến việc nắm vững các kiến thức nền tảng của một trong những ngôn ngữ lập trình phổ biến nhất thế giới.\nDù bạn là sinh viên mới tiếp cận lập trình hay một người muốn chuyển sang lĩnh vực Java, chuỗi bài viết này sẽ cung cấp cho bạn một lộ trình học tập rõ ràng và dễ hiểu.\n📘 Lộ trình học tập (Danh sách bài viết) # Series được chia thành các bài học nhỏ, đi từ cơ bản đến phức tạp.\nBài 1: Nhập Môn Java: Hướng Dẫn Toàn Diện Cho Người Mới Bắt Đầu Bài 2: Lập Trình Hướng Đối Tượng (OOP) Trong Java Bài 3: Java Collections Framework - List, Set, và Map Bài 4: Lập Trình Đa Luồng (Multithreading) Trong Java Bài 5: Xây Dựng Ứng Dụng Web Với Spring Boot 🎯 Mục tiêu của Series # Nền tảng vững chắc: Nắm vững cú pháp Java và các khái niệm cơ bản. Tư duy hướng đối tượng: Hiểu và áp dụng 4 trụ cột của OOP (Đóng gói, Kế thừa, Đa hình, Trừu tượng). Xử lý dữ liệu: Sử dụng thành thạo List, Set, Map trong Java Collections Framework. Lập trình hiện đại: Làm quen với đa luồng và xây dựng ứng dụng web cơ bản với Spring Boot. 🚀 Lời khuyên để học hiệu quả # Đọc và Thực hành: Đọc kỹ từng bài theo thứ tự và gõ lại các ví dụ code. Thực hành lại ngay với JDK + VS Code / IntelliJ Ghi chú các khái niệm cốt lõi: OOP, Collection, Thread, API, Dependency Injection. Đừng ngại thử và sai — đó là cách học lập trình nhanh nhất! 🧩 Tiếp theo: Bài 1 - Nhập Môn Java: Hướng Dẫn Toàn Diện Cho Người Mới Bắt Đầu\n","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/java-beginner/","section":"Tất cả bài viết","summary":"Một chuỗi bài viết hướng dẫn toàn diện về lập trình Java, từ những khái niệm cơ bản nhất như biến, kiểu dữ liệu, cấu trúc điều khiển cho đến lập trình hướng đối tượng.","title":"Series: Java Cho Người Mới Bắt Đầu","type":"posts"},{"content":" Chào mừng các bạn đã quay trở lại với blog! 👋\nĐây là series bài viết \u0026ldquo;JavaScript Cho Người Mới Bắt Đầu\u0026rdquo;, được thiết kế để dẫn dắt bạn đi từ con số không đến việc hiểu rõ cách \u0026ldquo;thổi hồn\u0026rdquo; vào các trang web tĩnh. JavaScript là ngôn ngữ lập trình của web, là mảnh ghép thứ ba không thể thiếu bên cạnh HTML và CSS.\nDù bạn là sinh viên mới bắt đầu, một designer muốn làm trang web của mình tương tác hơn, hay một lập trình viên backend muốn hiểu rõ cách front-end vận hành, chuỗi bài viết này sẽ cung cấp cho bạn một lộ trình học tập rõ ràng và thực tế.\n📘 Lộ trình học tập (Danh sách bài viết) # Series được chia thành các bài học cốt lõi, tập trung vào những gì bạn thực sự cần để bắt đầu:\nBài 1: JavaScript là gì? Từ \u0026ldquo;Bộ não\u0026rdquo; của Web đến \u0026ldquo;Kẻ thống trị\u0026rdquo; Mọi Nền tảng Bài 2: Hiểu Sâu về var, let, và const - Scope và Hoisting Bài 3: JavaScript Bất đồng bộ (Async) - Từ Callbacks, Promises đến Async/Await Bài 4: DOM là gì? \u0026ldquo;Quyền năng\u0026rdquo; Thay đổi Trang Web với JavaScript Bài 5: 10 Tính năng ES6+ \u0026ldquo;Thay đổi cuộc chơi\u0026rdquo; Bạn Phải Biết 🎯 Mục tiêu của Series # Giúp người học nắm vững cú pháp JavaScript cơ bản Hiểu rõ cách JavaScript tương tác và thay đổi HTML (DOM) Làm quen với việc xử lý các sự kiện của người dùng Tạo nền tảng vững chắc để học các thư viện/framework hiện đại 🚀 Lời khuyên để học hiệu quả # Đọc từng bài theo thứ tự: Các khái niệm được xây dựng nối tiếp nhau Thực hành ngay lập tức: Chỉ cần VS Code và trình duyệt web (F12 Console) Ghi chú các khái niệm cốt lõi: variable, function, DOM, event Đừng sợ lỗi: Console errors là người thầy tốt nhất của bạn! 🧩 Tiếp theo: Bài 1: JavaScript là gì? Từ \u0026ldquo;Bộ não\u0026rdquo; của Web đến \u0026ldquo;Kẻ thống trị\u0026rdquo; Mọi Nền tảng\n","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/js-beginner/","section":"Tất cả bài viết","summary":"Một chuỗi bài viết hướng dẫn toàn diện về JavaScript, từ cú pháp cơ bản đến DOM và xử lý sự kiện, giúp bạn làm chủ ngôn ngữ lập trình của web.","title":"Series: JavaScript Cho Người Mới Bắt Đầu","type":"posts"},{"content":"Spring Framework từ lâu đã là một thế lực trong thế giới Java để xây dựng các ứng dụng doanh nghiệp mạnh mẽ. Tuy nhiên, việc cấu hình Spring \u0026ldquo;truyền thống\u0026rdquo; có thể khá phức tạp và dài dòng. Spring Boot ra đời để giải quyết vấn đề này, mang đến một cách tiếp cận nhanh chóng và hiệu quả để tạo ra các ứng dụng Spring độc lập, sẵn sàng để chạy (production-ready).\nBài viết này sẽ hướng dẫn bạn các bước cơ bản để xây dựng một ứng dụng web RESTful API đơn giản bằng Spring Boot, từ việc khởi tạo dự án đến việc kết nối cơ sở dữ liệu.\n🤔 1. Tại Sao Lại Là Spring Boot? # Spring Boot không phải là một framework mới thay thế Spring, mà là một dự án con của Spring giúp đơn giản hóa quá trình phát triển với những ưu điểm chính:\nTự động cấu hình (Auto-configuration): Tự động cấu hình ứng dụng dựa trên các thư viện bạn thêm vào. Ví dụ, có spring-boot-starter-web thì sẽ tự cấu hình Tomcat và Spring MVC. \u0026ldquo;Opinionated\u0026rdquo; Defaults: Đưa ra các cấu hình mặc định hợp lý, giúp bạn bắt đầu nhanh chóng theo triết lý \u0026ldquo;Convention over Configuration\u0026rdquo; (Ưu tiên quy ước hơn cấu hình). Máy chủ nhúng (Embedded Server): Đi kèm các máy chủ nhúng như Tomcat, Jetty, không cần triển khai file WAR ra bên ngoài. Hệ sinh thái Starter: Cung cấp các starter dependencies để đơn giản hóa việc quản lý build. Ví dụ, chỉ cần spring-boot-starter-data-jpa là có đủ mọi thứ để làm việc với JPA và Hibernate. 🚀 2. Khởi Tạo Dự Án # Cách dễ nhất để tạo một dự án Spring Boot là sử dụng Spring Initializr.\nTruy cập https://start.spring.io. Chọn các thông số cho dự án của bạn: Project: Maven Language: Java Spring Boot: Chọn phiên bản ổn định (không chọn SNAPSHOT). Project Metadata: Điền Group, Artifact (tên dự án, ví dụ: demo-api). Packaging: Jar Java: Chọn phiên bản LTS (ví dụ: 17, 21). Trong phần Dependencies, nhấn \u0026ldquo;ADD DEPENDENCIES\u0026rdquo; và thêm: Spring Web: Để xây dựng ứng dụng web và RESTful API. Spring Data JPA: Để làm việc với cơ sở dữ liệu quan hệ. H2 Database: Một cơ sở dữ liệu trong bộ nhớ (in-memory), rất tiện cho việc phát triển. Nhấn GENERATE. Một file .zip sẽ được tải về. Hãy giải nén và mở nó bằng IDE yêu thích của bạn. 🛠️ 3. Tạo REST API Đầu Tiên # Một REST API cho phép các hệ thống khác nhau giao tiếp với nhau qua giao thức HTTP. Chúng ta sẽ tạo một API đơn giản để quản lý danh sách Product.\nBước 1: Tạo Model Product # Tạo một file Product.java để định nghĩa đối tượng của chúng ta.\n// src/main/java/com/example/demoapi/Product.java import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; @Entity // Đánh dấu đây là một JPA entity (tương ứng với một bảng trong CSDL) public class Product { @Id // Đánh dấu đây là khóa chính @GeneratedValue // Tự động tạo giá trị cho khóa chính private Long id; private String name; private double price; // Constructors, Getters, Setters... (Có thể dùng Lombok để tự động tạo) // Constructor mặc định là bắt buộc đối với JPA public Product() {} public Product(String name, double price) { this.name = name; this.price = price; } // ... getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } } Bước 2: Tạo Repository # Repository là một interface chịu trách nhiệm giao tiếp với cơ sở dữ liệu. Spring Data JPA sẽ tự động tạo ra các phương thức CRUD (Create, Read, Update, Delete) cho chúng ta.\nTạo một interface ProductRepository.java.\n// src/main/java/com/example/demoapi/ProductRepository.java import org.springframework.data.jpa.repository.JpaRepository; public interface ProductRepository extends JpaRepository\u0026lt;Product, Long\u0026gt; { // JpaRepository\u0026lt;Kiểu_Entity, Kiểu_Khóa_Chính\u0026gt; // Spring Data JPA sẽ tự động cung cấp các phương thức như save(), findById(), findAll()... } Thật vậy, chỉ cần định nghĩa interface như vậy là đủ!\nBước 3: Tạo Controller # Controller là nơi xử lý các request HTTP từ client và trả về response.\nTạo file ProductController.java.\n// src/main/java/com/example/demoapi/ProductController.java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController // Đánh dấu đây là một REST controller, các phương thức sẽ trả về JSON @RequestMapping(\u0026#34;/api/products\u0026#34;) // Tất cả các endpoint trong class này sẽ có tiền tố /api/products public class ProductController { @Autowired // Spring sẽ tự động inject (tiêm) một instance của ProductRepository vào đây private ProductRepository productRepository; // GET /api/products -\u0026gt; Lấy tất cả sản phẩm @GetMapping public List\u0026lt;Product\u0026gt; getAllProducts() { return productRepository.findAll(); } // POST /api/products -\u0026gt; Tạo một sản phẩm mới @PostMapping public Product createProduct(@RequestBody Product product) { // @RequestBody sẽ chuyển đổi JSON từ request body thành đối tượng Product return productRepository.save(product); } } Dependency Injection (@Autowired): Thay vì tự tạo new ProductRepository(), chúng ta để Spring IoC container quản lý và \u0026ldquo;tiêm\u0026rdquo; nó vào khi cần. Đây là một khái niệm cốt lõi của Spring.\n🏃 4. Chạy Và Kiểm Tra Ứng Dụng # Bây giờ, hãy mở file DemoApiApplication.java và nhấn nút \u0026ldquo;Run\u0026rdquo;. Spring Boot sẽ khởi động máy chủ Tomcat nhúng trên cổng 8080.\nBạn có thể dùng các công cụ như curl hoặc Postman để kiểm tra API:\nTạo một sản phẩm mới (POST) # URL: POST http://localhost:8080/api/products Body (JSON): { \u0026#34;name\u0026#34;: \u0026#34;Laptop Pro\u0026#34;, \u0026#34;price\u0026#34;: 1200.5 } Bạn sẽ nhận lại đối tượng vừa tạo, có kèm theo id.\nLấy tất cả sản phẩm (GET) # URL: GET http://localhost:8080/api/products Bạn sẽ nhận lại một mảng JSON chứa tất cả sản phẩm có trong cơ sở dữ liệu H2.\n🎯 Tổng kết # Chỉ với vài bước đơn giản, bạn đã xây dựng được một REST API hoàn chỉnh. Đây chính là sức mạnh của Spring Boot: giúp bạn tập trung vào logic nghiệp vụ thay vì sa đà vào việc cấu hình.\n🚀 Khởi tạo: Dùng Spring Initializr để tạo dự án nhanh chóng với các starter cần thiết (Web, JPA). 🛠️ Xây dựng: Model (@Entity): Định nghĩa đối tượng dữ liệu. Repository (JpaRepository): Giao tiếp với cơ sở dữ liệu mà không cần viết code SQL. Controller (@RestController): Xử lý request HTTP và định nghĩa các API endpoint (@GetMapping, @PostMapping). 🏃 Chạy: Spring Boot tự khởi động máy chủ nhúng, sẵn sàng để kiểm thử. Từ nền tảng này, bạn có thể tiếp tục khám phá các khía cạnh khác của hệ sinh thái Spring như Spring Security (bảo mật), validation, exception handling, và nhiều hơn nữa. Chúc mừng bạn đã bắt đầu hành trình với một trong những framework Java mạnh mẽ nhất hiện nay!\n","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/java-beginner/05_xay-dung-ung-dung-web-voi-spring-boot/","section":"Tất cả bài viết","summary":"Hướng dẫn xây dựng RESTful API đơn giản với Spring Boot, từ khởi tạo dự án, tạo Model, Repository, Controller đến kết nối cơ sở dữ liệu.","title":"Bài 5: Xây Dựng Ứng Dụng Web Với Spring Boot","type":"posts"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/","section":"Hannie's Blog","summary":"","title":"Hannie's Blog","type":"page"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/java/","section":"Tags","summary":"","title":"Java","type":"tags"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/series/java-cho-ng%C6%B0%E1%BB%9Di-m%E1%BB%9Bi-b%E1%BA%AFt-%C4%91%E1%BA%A7u/","section":"Series","summary":"","title":"Java Cho Người Mới Bắt Đầu","type":"series"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/jpa/","section":"Tags","summary":"","title":"JPA","type":"tags"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/categories/l%E1%BA%ADp-tr%C3%ACnh-java/","section":"Categories","summary":"","title":"Lập Trình Java","type":"categories"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/rest-api/","section":"Tags","summary":"","title":"REST API","type":"tags"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/spring-boot/","section":"Tags","summary":"","title":"Spring Boot","type":"tags"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"17-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/web-development/","section":"Tags","summary":"","title":"Web Development","type":"tags"},{"content":"Trong thế giới hiện đại, các ứng dụng cần phải xử lý nhiều tác vụ cùng một lúc để mang lại trải nghiệm người dùng tốt nhất và tận dụng tối đa sức mạnh của CPU đa lõi. Đây là lúc lập trình đa luồng (multithreading) và bất đồng bộ (asynchronous programming) phát huy tác dụng. Java cung cấp một hệ sinh thái phong phú để làm việc với concurrency, từ các khái niệm cơ bản như Thread và Runnable cho đến các API cấp cao như ExecutorService và CompletableFuture.\nBài viết này sẽ dẫn dắt bạn qua hành trình phát triển của lập trình đa luồng trong Java.\n🤔 1. Tại Sao Cần Đa Luồng? # Đa luồng giải quyết vấn đề này bằng cách cho phép bạn chạy các tác vụ tốn thời gian trên các luồng nền (background threads), giữ cho luồng chính luôn sẵn sàng phản hồi tương tác của người dùng.\n🕰️ 2. Cách Cổ Điển: Thread và Runnable # Đây là cách tiếp cận cơ bản nhất để tạo luồng trong Java.\nimplements Runnable (Ưu tiên) # Đây là cách được khuyến khích vì nó tách biệt tác vụ (task) khỏi cơ chế thực thi luồng (thread).\nclass MyTask implements Runnable { @Override public void run() { System.out.println(\u0026#34;Task is running in thread: \u0026#34; + Thread.currentThread().getName()); } } // Sử dụng Thread thread = new Thread(new MyTask()); thread.start(); extends Thread # Cách này ít linh hoạt hơn vì Java không hỗ trợ đa kế thừa.\nclass MyThread extends Thread { @Override public void run() { System.out.println(\u0026#34;Task is running in thread: \u0026#34; + Thread.currentThread().getName()); } } // Sử dụng MyThread myThread = new MyThread(); myThread.start(); Vấn đề: Việc quản lý thủ công các Thread (tạo mới, hủy bỏ) rất phức tạp, tốn kém tài nguyên và dễ gây ra lỗi.\n🚀 3. Cách Tốt Hơn: Executor Framework # Ra đời từ Java 5, Executor Framework là một bước tiến lớn. Nó tách biệt việc định nghĩa tác vụ (Runnable, Callable) khỏi việc thực thi và quản lý luồng.\nCác thành phần chính # ExecutorService: Giao diện quản lý việc thực thi các tác vụ bất đồng bộ. Executors: Lớp tiện ích để tạo các loại ExecutorService khác nhau. ThreadPool: ExecutorService thường quản lý một nhóm các luồng (thread pool) để tái sử dụng, giúp tiết kiệm tài nguyên. Future: Đại diện cho kết quả của một phép tính bất đồng bộ sẽ có trong tương lai. Ví dụ # import java.util.concurrent.*; public class ExecutorExample { public static void main(String[] args) throws ExecutionException, InterruptedException { // Tạo một thread pool với 2 luồng ExecutorService executor = Executors.newFixedThreadPool(2); // Gửi một tác vụ Runnable (không trả về kết quả) executor.submit(() -\u0026gt; { System.out.println(\u0026#34;Runnable task is running in: \u0026#34; + Thread.currentThread().getName()); }); // Gửi một tác vụ Callable (trả về kết quả) Future\u0026lt;String\u0026gt; future = executor.submit(() -\u0026gt; { Thread.sleep(1000); // Giả lập một tác vụ tốn thời gian return \u0026#34;Result from Callable\u0026#34;; }); System.out.println(\u0026#34;Main thread continues to run...\u0026#34;); // Lấy kết quả từ Future, luồng chính sẽ bị block ở đây cho đến khi có kết quả String result = future.get(); System.out.println(\u0026#34;Received result: \u0026#34; + result); // Rất quan trọng: phải đóng executor service khi không dùng nữa executor.shutdown(); } } Vấn đề với Future: Mặc dù tốt hơn, Future vẫn còn hạn chế. Rất khó để kết hợp nhiều Future với nhau hoặc xử lý kết quả một cách bất đồng bộ mà không block luồng chính.\n✨ 4. Cách Hiện Đại: CompletableFuture # CompletableFuture, được giới thiệu trong Java 8, là câu trả lời cho những hạn chế của Future. Nó là một bước đột phá trong lập trình bất đồng bộ của Java.\nCác đặc điểm nổi bật # Non-blocking: Xử lý kết quả ngay khi nó có sẵn mà không cần gọi get() và block luồng hiện tại. Khả năng kết hợp (Composable): Dễ dàng xâu chuỗi các tác vụ bất đồng bộ với nhau. Xử lý lỗi linh hoạt: Cung cấp các phương thức để xử lý ngoại lệ một cách mượt mà. Ví dụ # Hãy tưởng tượng chúng ta cần thực hiện một chuỗi tác vụ:\nLấy thông tin người dùng (bất đồng bộ). Dựa trên thông tin đó, lấy danh sách đơn hàng của họ (bất đồng bộ). Đếm số lượng đơn hàng. import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; public class CompletableFutureExample { public static CompletableFuture\u0026lt;String\u0026gt; getUserInfo() { return CompletableFuture.supplyAsync(() -\u0026gt; { System.out.println(\u0026#34;Getting user info...\u0026#34;); sleep(1); return \u0026#34;User123\u0026#34;; // Giả lập kết quả }); } public static CompletableFuture\u0026lt;Integer\u0026gt; getOrderCount(String userId) { return CompletableFuture.supplyAsync(() -\u0026gt; { System.out.println(\u0026#34;Getting order count for \u0026#34; + userId); sleep(1); return 15; // Giả lập kết quả }); } public static void main(String[] args) { System.out.println(\u0026#34;Main thread starts.\u0026#34;); getUserInfo() .thenCompose(userId -\u0026gt; getOrderCount(userId)) // thenCompose để xâu chuỗi các CompletableFuture .thenAccept(count -\u0026gt; { // thenAccept để xử lý kết quả cuối cùng System.out.println(\u0026#34;Final result: User has \u0026#34; + count + \u0026#34; orders.\u0026#34;); }) .exceptionally(ex -\u0026gt; { // Xử lý lỗi System.err.println(\u0026#34;An error occurred: \u0026#34; + ex.getMessage()); return null; }); System.out.println(\u0026#34;Main thread continues without blocking.\u0026#34;); // Chờ để chương trình không kết thúc sớm sleep(3); System.out.println(\u0026#34;Main thread ends.\u0026#34;); } private static void sleep(int seconds) { try { TimeUnit.SECONDS.sleep(seconds); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } Trong ví dụ trên, luồng main không hề bị block. Các tác vụ được thực thi trên một thread pool và kết quả được xử lý thông qua các callback (thenCompose, thenAccept).\n🎯 Tổng kết # Lập trình đa luồng trong Java đã có một chặng đường phát triển dài:\n🕰️ Cấp 1 (Cổ điển - Thread/Runnable): Nền tảng cơ bản nhưng quản lý thủ công, phức tạp và dễ lỗi. 🚀 Cấp 2 (Tốt hơn - ExecutorService): Tách biệt tác vụ và thực thi, quản lý luồng qua ThreadPool, nhưng Future còn hạn chế trong việc kết hợp. ✨ Cấp 3 (Hiện đại - CompletableFuture): Mạnh mẽ, linh hoạt, cho phép lập trình bất đồng bộ non-blocking và kết hợp các tác vụ một cách dễ dàng. Việc nắm vững các công cụ này sẽ giúp bạn xây dựng được những ứng dụng hiệu năng cao và có khả năng phản hồi tốt.\n","date":"16-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/java-beginner/04_lap-trinh-da-luong/","section":"Tất cả bài viết","summary":"Tìm hiểu về lập trình đa luồng trong Java, từ Thread/Runnable cơ bản, ExecutorService, đến CompletableFuture hiện đại.","title":"Bài 4: Lập Trình Đa Luồng (Multithreading) Trong Java","type":"posts"},{"content":"","date":"16-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/completablefuture/","section":"Tags","summary":"","title":"CompletableFuture","type":"tags"},{"content":"","date":"16-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/concurrency/","section":"Tags","summary":"","title":"Concurrency","type":"tags"},{"content":"","date":"16-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/core-java/","section":"Tags","summary":"","title":"Core Java","type":"tags"},{"content":"","date":"16-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/executorservice/","section":"Tags","summary":"","title":"ExecutorService","type":"tags"},{"content":"","date":"16-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/multithreading/","section":"Tags","summary":"","title":"Multithreading","type":"tags"},{"content":"Khi làm việc với Java, bạn không thể tránh khỏi việc xử lý các tập hợp dữ liệu. Java Collections Framework (JCF) cung cấp một bộ công cụ mạnh mẽ để lưu trữ và thao tác với các nhóm đối tượng này.\nViệc hiểu rõ và lựa chọn đúng cấu trúc dữ liệu cho từng bài toán là một kỹ năng quan trọng của lập trình viên Java. Bài viết này sẽ đi sâu vào ba giao diện (interface) cốt lõi của JCF: List, Set, và Map.\n🧠 1. Tổng Quan Về JCF # JCF là một tập hợp các lớp và giao diện được thiết kế để:\nLưu trữ: Cung cấp các cấu trúc dữ liệu như danh sách, tập hợp, hàng đợi, bản đồ. Thao tác: Cung cấp các thuật toán để tìm kiếm, sắp xếp, và quản lý dữ liệu. Tối ưu hóa: Các lớp triển khai được tối ưu hóa về hiệu suất cho các trường hợp sử dụng khác nhau. Cấu trúc phân cấp chính # Iterable (Interface): Giao diện gốc, cho phép duyệt qua các phần tử. Collection (Interface): Kế thừa từ Iterable, là nền tảng cho List, Set, và Queue. Map (Interface): Một cấu trúc riêng biệt, lưu trữ dữ liệu dưới dạng cặp key-value. 📚 2. List: Danh Sách Có Thứ Tự, Cho Phép Trùng Lặp # List là một tập hợp các phần tử được sắp xếp theo thứ tự và cho phép chứa các phần tử trùng lặp. Bạn có thể truy cập các phần tử thông qua chỉ số (index) của chúng, bắt đầu từ 0.\nCác lớp triển khai phổ biến # ArrayList # Cấu trúc: Dựa trên mảng động (dynamic array). Điểm mạnh: Truy cập ngẫu nhiên phần tử theo chỉ số rất nhanh (độ phức tạp O(1)). Điểm yếu: Thêm hoặc xóa phần tử ở giữa danh sách chậm vì cần phải dịch chuyển các phần tử phía sau (độ phức tạp O(n)). Khi nào dùng: Khi bạn thường xuyên đọc dữ liệu theo chỉ số và ít khi thêm/xóa ở giữa. import java.util.ArrayList; import java.util.List; List\u0026lt;String\u0026gt; fruits = new ArrayList\u0026lt;\u0026gt;(); fruits.add(\u0026#34;Apple\u0026#34;); fruits.add(\u0026#34;Banana\u0026#34;); fruits.add(\u0026#34;Apple\u0026#34;); // Cho phép trùng lặp System.out.println(fruits.get(1)); // Output: Banana LinkedList # Cấu trúc: Dựa trên danh sách liên kết đôi (doubly-linked list). Điểm mạnh: Thêm hoặc xóa phần tử ở đầu/cuối danh sách rất nhanh (O(1)). Điểm yếu: Truy cập ngẫu nhiên phần tử theo chỉ số chậm vì phải duyệt từ đầu hoặc cuối (O(n)). Khi nào dùng: Khi bạn thường xuyên thêm/xóa phần tử ở đầu/cuối danh sách. import java.util.LinkedList; import java.util.List; List\u0026lt;String\u0026gt; animals = new LinkedList\u0026lt;\u0026gt;(); animals.add(\u0026#34;Dog\u0026#34;); animals.add(\u0026#34;Cat\u0026#34;); // Thêm vào đầu danh sách ((LinkedList\u0026lt;String\u0026gt;) animals).addFirst(\u0026#34;Lion\u0026#34;); 🌿 3. Set: Tập Hợp Không Thứ Tự, Không Cho Phép Trùng Lặp # Set là một tập hợp các phần tử không chứa các phần tử trùng lặp. Nó mô phỏng khái niệm tập hợp trong toán học và thường không đảm bảo về thứ tự.\nCác lớp triển khai phổ biến # HashSet # Cấu trúc: Dựa trên bảng băm (hash table). Đặc điểm: Không đảm bảo thứ tự các phần tử. Hiệu suất rất cao cho các thao tác thêm, xóa, kiểm tra sự tồn tại (trung bình là O(1)). Khi nào dùng: Khi bạn chỉ cần lưu trữ các phần tử duy nhất và không quan tâm đến thứ tự. Đây là lựa chọn phổ biến nhất cho Set. import java.util.HashSet; import java.util.Set; Set\u0026lt;Integer\u0026gt; numbers = new HashSet\u0026lt;\u0026gt;(); numbers.add(10); numbers.add(20); numbers.add(10); // Phần tử này sẽ không được thêm vào vì đã tồn tại System.out.println(numbers); // Output: [20, 10] (thứ tự không đảm bảo) TreeSet # Cấu trúc: Dựa trên cây đỏ-đen (red-black tree). Đặc điểm: Các phần tử luôn được lưu trữ theo thứ tự tự nhiên (hoặc theo Comparator được cung cấp). Khi nào dùng: Khi bạn cần một tập hợp không trùng lặp và các phần tử phải được sắp xếp. import java.util.TreeSet; import java.util.Set; Set\u0026lt;String\u0026gt; sortedNames = new TreeSet\u0026lt;\u0026gt;(); sortedNames.add(\u0026#34;Charlie\u0026#34;); sortedNames.add(\u0026#34;Alice\u0026#34;); sortedNames.add(\u0026#34;Bob\u0026#34;); System.out.println(sortedNames); // Output: [Alice, Bob, Charlie] 🗺️ 4. Map: Lưu Trữ Dưới Dạng Cặp Key-Value # Map là một cấu trúc dữ liệu lưu trữ các cặp khóa-giá trị (key-value). Mỗi khóa (key) là duy nhất và được dùng để truy xuất giá trị (value) tương ứng. Map không kế thừa từ Collection.\nCác lớp triển khai phổ biến # HashMap # Cấu trúc: Dựa trên bảng băm. Đặc điểm: Không đảm bảo thứ tự của các cặp key-value. Hiệu suất rất cao cho các thao tác thêm, lấy, xóa (trung bình là O(1)). Khi nào dùng: Khi bạn cần tra cứu dữ liệu nhanh chóng bằng một khóa duy nhất và không quan tâm đến thứ tự. Đây là lựa chọn mặc định cho Map. import java.util.HashMap; import java.util.Map; Map\u0026lt;String, String\u0026gt; capitals = new HashMap\u0026lt;\u0026gt;(); capitals.put(\u0026#34;Vietnam\u0026#34;, \u0026#34;Hanoi\u0026#34;); capitals.put(\u0026#34;USA\u0026#34;, \u0026#34;Washington D.C.\u0026#34;); System.out.println(capitals.get(\u0026#34;Vietnam\u0026#34;)); // Output: Hanoi TreeMap # Cấu trúc: Dựa trên cây đỏ-đen. Đặc điểm: Các cặp key-value được sắp xếp theo thứ tự tự nhiên của khóa. Khi nào dùng: Khi bạn cần một bản đồ mà các khóa luôn được sắp xếp. import java.util.TreeMap; import java.util.Map; Map\u0026lt;Integer, String\u0026gt; studentGrades = new TreeMap\u0026lt;\u0026gt;(); studentGrades.put(3, \u0026#34;Charlie\u0026#34;); studentGrades.put(1, \u0026#34;Alice\u0026#34;); studentGrades.put(2, \u0026#34;Bob\u0026#34;); System.out.println(studentGrades); // Output: {1=Alice, 2=Bob, 3=Charlie} 🎯 Tổng kết # Việc lựa chọn đúng collection cho bài toán là rất quan trọng:\n📚 List: Cần một danh sách có thứ tự và cho phép trùng lặp? ArrayList: Ưu tiên truy cập nhanh theo chỉ số. LinkedList: Ưu tiên thêm/xóa nhanh ở đầu/cuối. 🌿 Set: Cần một tập hợp các phần tử duy nhất? HashSet: Ưu tiên hiệu suất cao, không cần thứ tự. TreeSet: Cần các phần tử được sắp xếp. 🗺️ Map: Cần lưu trữ và tra cứu dữ liệu bằng một khóa duy nhất? HashMap: Ưu tiên hiệu suất cao, không cần thứ tự khóa. TreeMap: Cần các khóa được sắp xếp. Hiểu rõ bản chất của các cấu trúc dữ liệu này sẽ giúp bạn viết mã Java hiệu quả và tối ưu hơn rất nhiều.\n","date":"15-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/java-beginner/03_java-collections-framework/","section":"Tất cả bài viết","summary":"Hiểu sâu về 3 interface cốt lõi của Java Collections Framework: List, Set, và Map, cùng các lớp triển khai phổ biến như ArrayList, HashSet, HashMap.","title":"Bài 3: Java Collections Framework - List, Set, và Map","type":"posts"},{"content":"","date":"15-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/collections/","section":"Tags","summary":"","title":"Collections","type":"tags"},{"content":"","date":"15-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/list/","section":"Tags","summary":"","title":"List","type":"tags"},{"content":"","date":"15-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/map/","section":"Tags","summary":"","title":"Map","type":"tags"},{"content":"","date":"15-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/set/","section":"Tags","summary":"","title":"Set","type":"tags"},{"content":" 🤔 OOP là gì? # Lập Trình Hướng Đối Tượng (OOP) Trong Java: Từ Lý Thuyết Đến Thực Tiễn\nLập trình hướng đối tượng (Object-Oriented Programming - OOP) không chỉ là một tính năng của Java, mà nó chính là triết lý, là xương sống của ngôn ngữ này. Hiểu rõ và vận dụng thành thạo các nguyên tắc OOP sẽ giúp bạn viết mã nguồn có cấu trúc, dễ bảo trì, dễ mở rộng và có khả năng tái sử dụng cao.\nBài viết này sẽ đi sâu vào bốn trụ cột của OOP trong Java: Tính đóng gói (Encapsulation), Tính kế thừa (Inheritance), Tính đa hình (Polymorphism), và Tính trừu tượng (Abstraction).\n📦 1. Tính Đóng Gói (Encapsulation) # Khái niệm # Tính đóng gói là kỹ thuật che giấu thông tin (data hiding) và các chi tiết triển khai của một đối tượng, chỉ cho phép tương tác với nó thông qua các phương thức công khai (public methods).\nHãy tưởng tượng một chiếc xe hơi. Bạn không cần biết động cơ, hộp số hoạt động chi tiết như thế nào. Bạn chỉ cần sử dụng vô lăng, chân ga, chân phanh – đó chính là các \u0026ldquo;giao diện\u0026rdquo; công khai.\nTrong Java, chúng ta thực hiện đóng gói bằng cách:\nKhai báo các thuộc tính (biến) là private. Cung cấp các phương thức public để truy cập và thay đổi các thuộc tính đó (thường gọi là getters và setters). Ví dụ # public class Student { // Thuộc tính được che giấu, không thể truy cập trực tiếp từ bên ngoài private String name; private int age; // Getter: Phương thức public để lấy giá trị của \u0026#39;name\u0026#39; public String getName() { return this.name; } // Setter: Phương thức public để thiết lập giá trị cho \u0026#39;name\u0026#39; // Có thể thêm logic kiểm tra dữ liệu ở đây public void setName(String name) { if (name != null \u0026amp;\u0026amp; !name.trim().isEmpty()) { this.name = name; } } // Tương tự cho age public int getAge() { return this.age; } public void setAge(int age) { if (age \u0026gt; 0) { this.age = age; } } } Lợi ích # Bảo vệ dữ liệu: Ngăn chặn việc thay đổi dữ liệu một cách tùy tiện. Tăng tính linh hoạt: Bạn có thể thay đổi cách triển khai bên trong một lớp mà không ảnh hưởng đến các lớp khác đang sử dụng nó. Dễ kiểm soát: Logic kiểm tra dữ liệu có thể được đặt trong các phương thức setter. 👨‍👩‍👧‍👦 2. Tính Kế Thừa (Inheritance) # Khái niệm # Tính kế thừa cho phép một lớp (gọi là lớp con - subclass) thừa hưởng lại các thuộc tính và phương thức của một lớp khác (gọi là lớp cha - superclass).\nKế thừa tạo ra mối quan hệ \u0026ldquo;is-a\u0026rdquo; (là một). Ví dụ, một Dog (Chó) \u0026ldquo;là một\u0026rdquo; Animal (Động vật). Dog sẽ có những đặc điểm chung của Animal (như ăn, ngủ) và có những đặc điểm riêng (như sủa).\nTrong Java, chúng ta sử dụng từ khóa extends để thực hiện kế thừa.\nVí dụ # // Lớp cha (Superclass) public class Animal { private String name; public Animal(String name) { this.name = name; } public void eat() { System.out.println(name + \u0026#34; is eating.\u0026#34;); } public String getName() { return name; } } // Lớp con (Subclass) kế thừa từ Animal public class Dog extends Animal { // Gọi constructor của lớp cha public Dog(String name) { super(name); // \u0026#39;super\u0026#39; dùng để tham chiếu đến lớp cha } // Phương thức riêng của Dog public void bark() { // getName() được kế thừa từ Animal System.out.println(getName() + \u0026#34; is barking: Woof! Woof!\u0026#34;); } } Lợi ích # Tái sử dụng mã: Không cần phải viết lại các thuộc tính và phương thức đã có ở lớp cha. Tổ chức mã nguồn: Tạo ra một hệ thống phân cấp logic, dễ hiểu. 🎭 3. Tính Đa Hình (Polymorphism) # Khái niệm # Đa hình có nghĩa là \u0026ldquo;nhiều hình thái\u0026rdquo;. Trong OOP, nó cho phép một đối tượng có thể được thể hiện dưới nhiều dạng khác nhau. Hành vi của một phương thức có thể thay đổi tùy thuộc vào đối tượng gọi nó.\nTính đa hình trong Java thường được thể hiện qua ghi đè phương thức (Method Overriding).\nVí dụ # Hãy mở rộng ví dụ về Animal. Cả Dog và Cat đều là Animal và đều có thể phát ra âm thanh, nhưng cách chúng phát ra âm thanh thì khác nhau.\npublic class Animal { public void makeSound() { System.out.println(\u0026#34;Animal makes a sound\u0026#34;); } } public class Dog extends Animal { @Override // Chú thích cho biết đây là phương thức ghi đè public void makeSound() { System.out.println(\u0026#34;Dog barks: Woof! Woof!\u0026#34;); } } public class Cat extends Animal { @Override public void makeSound() { System.out.println(\u0026#34;Cat meows: Meow! Meow!\u0026#34;); } } // Sử dụng tính đa hình public class Main { public static void main(String[] args) { Animal myAnimal = new Animal(); Animal myDog = new Dog(); // Một đối tượng Dog cũng là một Animal Animal myCat = new Cat(); // Một đối tượng Cat cũng là một Animal myAnimal.makeSound(); // Output: Animal makes a sound myDog.makeSound(); // Output: Dog barks: Woof! Woof! myCat.makeSound(); // Output: Cat meows: Meow! Meow! } } Cùng một lời gọi phương thức makeSound() nhưng kết quả lại khác nhau tùy thuộc vào đối tượng thực sự là gì (Dog hay Cat).\n🎨 4. Tính Trừu Tượng (Abstraction) # Khái niệm # Tính trừu tượng là quá trình che giấu các chi tiết triển khai phức tạp và chỉ hiển thị những thông tin cần thiết ra bên ngoài. Kế thừa cho thấy mối quan hệ \u0026ldquo;is-a\u0026rdquo;, còn trừu tượng định nghĩa ra một \u0026ldquo;bản thiết kế\u0026rdquo; chung.\nTrong Java, chúng ta đạt được tính trừu tượng thông qua lớp trừu tượng (abstract class) và giao diện (interface).\nAbstract Class vs Interface # Abstract Class: Là một lớp không thể khởi tạo đối tượng, nó hoạt động như một khuôn mẫu cho các lớp con. Nó có thể chứa cả phương thức trừu tượng (không có thân hàm) và phương thức thông thường. Interface: Là một bản thiết kế hoàn toàn trừu tượng. Nó chỉ chứa các phương thức trừu tượng và các hằng số. Một lớp có thể implements (triển khai) nhiều interface. Ví dụ với Interface # // Interface định nghĩa hành vi \u0026#34;có thể bay\u0026#34; interface Flyable { void fly(); // Mặc định là public abstract } // Lớp Airplane cũng triển khai interface Flyable class Airplane implements Flyable { @Override public void fly() { System.out.println(\u0026#34;Airplane is flying with engines.\u0026#34;); } } // Lớp Bird triển khai interface Flyable class Bird implements Flyable { @Override public void fly() { System.out.println(\u0026#34;Bird is flying with wings.\u0026#34;); } } Khi nào dùng? # Dùng abstract class khi bạn muốn chia sẻ mã nguồn chung giữa các lớp con có liên quan chặt chẽ. Dùng interface khi bạn muốn định nghĩa một \u0026ldquo;hợp đồng\u0026rdquo; về hành vi mà các lớp không liên quan có thể cùng triển khai. 🎯 Tổng kết # Để trở thành một lập trình viên Java giỏi, việc nắm vững 4 nguyên tắc OOP là điều bắt buộc.\n📦 Tính Đóng Gói: Che giấu dữ liệu, bảo vệ trạng thái của đối tượng. 👨‍👩‍👧‍👦 Tính Kế Thừa: Tái sử dụng code và tạo ra hệ thống phân cấp logic. 🎭 Tính Đa Hình: Cho phép hành vi của phương thức thay đổi tùy theo đối tượng. 🎨 Tính Trừu Tượng: Che giấu sự phức tạp, tập trung vào \u0026ldquo;cái gì\u0026rdquo; thay vì \u0026ldquo;như thế nào\u0026rdquo;. ","date":"14-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/java-beginner/02_lap-trinh-huong-doi-tuong/","section":"Tất cả bài viết","summary":"Tìm hiểu sâu về 4 trụ cột của OOP trong Java: Tính đóng gói, Kế thừa, Đa hình và Trừu tượng, với các ví dụ thực tế.","title":"Bài 2: Lập Trình Hướng Đối Tượng (OOP) Trong Java","type":"posts"},{"content":"","date":"14-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/l%E1%BA%ADp-tr%C3%ACnh-h%C6%B0%E1%BB%9Bng-%C4%91%E1%BB%91i-t%C6%B0%E1%BB%A3ng/","section":"Tags","summary":"","title":"Lập Trình Hướng Đối Tượng","type":"tags"},{"content":"","date":"14-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/oop/","section":"Tags","summary":"","title":"OOP","type":"tags"},{"content":" 🚀 Giới thiệu # ES6 (hay ECMAScript 2015) là bản cập nhật quan trọng nhất trong lịch sử JavaScript. Nó không chỉ thêm tính năng mới, mà còn thay đổi hoàn toàn cách chúng ta viết code.\n1️⃣ let và const # (Đã phân tích chi tiết ở Bài 2)\nBlock Scope thay vì Function Scope Giải quyết vấn đề hoisting Ngăn chặn rò rỉ biến 2️⃣ Arrow Functions (=\u0026gt;) # Cú pháp ngắn gọn: # // Kiểu cũ function add(a, b) { return a + b; } // Kiểu mới (Arrow Function) const add = (a, b) =\u0026gt; a + b; Xử lý this: # function Timer() { this.seconds = 0; setInterval(() =\u0026gt; { this.seconds++; // \u0026#39;this\u0026#39; là Timer console.log(this.seconds); }, 1000); } 3️⃣ Template Literals (``) # Ưu điểm: # Nhúng biến với ${\u0026hellip;} Hỗ trợ nhiều dòng const user = \u0026#34;Alice\u0026#34;; const score = 95; // Kiểu mới const greeting = `Xin chào ${user}, Điểm của bạn là ${score}.`; 4️⃣ Destructuring # Với Objects: # const user = { firstName: \u0026#34;John\u0026#34;, lastName: \u0026#34;Doe\u0026#34;, age: 30 }; // Destructuring object const { firstName, age } = user; const { lastName: ho } = user; // Đổi tên Với Arrays: # const colors = [\u0026#34;Đỏ\u0026#34;, \u0026#34;Xanh\u0026#34;, \u0026#34;Vàng\u0026#34;]; const [firstColor, secondColor] = colors; 5️⃣ Spread Operator (\u0026hellip;) # // Nối mảng const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const combined = [...arr1, ...arr2]; // Shallow copy const original = [\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;]; const copy = [...original]; // Với objects const user = { name: \u0026#34;Bot\u0026#34;, version: 1 }; const updatedUser = { ...user, version: 2 }; 6️⃣ Rest Parameters # function sum(first, second, ...allOtherNumbers) { let total = first + second; allOtherNumbers.forEach((num) =\u0026gt; (total += num)); return total; } sum(1, 2, 3, 4, 5); // 15 7️⃣ Default Parameters # // Kiểu mới function greet(name = \u0026#34;Guest\u0026#34;) { console.log(`Hello, ${name}`); } greet(); // Hello, Guest 8️⃣ ES Modules # // math.js export const PI = 3.14; export function add(a, b) { return a + b; } // app.js import { PI, add } from \u0026#34;./math.js\u0026#34;; 9️⃣ Promises # (Đã phân tích ở Bài 3)\n🔟 async/await # (Đã phân tích ở Bài 3)\n🎯 Tổng kết # let/const: Block scope, thay thế var Arrow Functions: Cú pháp ngắn gọn, xử lý this Template Literals: String interpolation đơn giản Destructuring: Trích xuất dữ liệu dễ dàng Spread/Rest: Thao tác với arrays/objects linh hoạt Modules: Code organization chuẩn mực Promises \u0026amp; async/await: Xử lý bất đồng bộ hiện đại ","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/js-beginner/05_tinh-nang-es6-moi/","section":"Tất cả bài viết","summary":"Khám phá 10 tính năng quan trọng nhất của ES6+ đã thay đổi cách chúng ta viết JavaScript hiện đại.","title":"Bài 5: 10 Tính năng ES6+ \"Thay đổi cuộc chơi\" Bạn Phải Biết","type":"posts"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/es6/","section":"Tags","summary":"","title":"ES6","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/javascript/","section":"Tags","summary":"","title":"JavaScript","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/series/javascript-cho-ng%C6%B0%E1%BB%9Di-m%E1%BB%9Bi-b%E1%BA%AFt-%C4%91%E1%BA%A7u/","section":"Series","summary":"","title":"JavaScript Cho Người Mới Bắt Đầu","type":"series"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/categories/l%E1%BA%ADp-tr%C3%ACnh-javascript/","section":"Categories","summary":"","title":"Lập Trình JavaScript","type":"categories"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/modern-javascript/","section":"Tags","summary":"","title":"Modern JavaScript","type":"tags"},{"content":" 🌳 DOM là gì? # DOM (Document Object Model) là một API cho phép JavaScript tương tác với HTML. Khi trình duyệt tải trang web, nó tạo ra một \u0026ldquo;cây đối tượng\u0026rdquo; từ HTML của bạn.\nVí dụ HTML: # \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Trang của tôi\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Chào bạn\u0026lt;/h1\u0026gt; \u0026lt;p id=\u0026#34;greeting\u0026#34;\u0026gt;Đây là một trang web.\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 🔍 Bước 1: Truy vấn (Query) # Các phương thức truy vấn: # Theo ID (Nhanh nhất): const element = document.getElementById(\u0026#34;greeting\u0026#34;); Theo CSS Selector (Linh hoạt nhất): const heading = document.querySelector(\u0026#34;h1\u0026#34;); const firstParagraph = document.querySelector(\u0026#34;.my-class\u0026#34;); const allParagraphs = document.querySelectorAll(\u0026#34;p\u0026#34;); ✨ Bước 2: Thao tác (Manipulation) # Thay đổi nội dung: # // Thay đổi text element.textContent = \u0026#34;Nội dung mới!\u0026#34;; // Thay đổi HTML (cẩn thận XSS) element.innerHTML = \u0026#34;\u0026lt;strong\u0026gt;Nội dung\u0026lt;/strong\u0026gt; mới!\u0026#34;; Thay đổi thuộc tính: # const img = document.querySelector(\u0026#34;img\u0026#34;); img.src = \u0026#34;new-image.jpg\u0026#34;; img.setAttribute(\u0026#34;alt\u0026#34;, \u0026#34;Mô tả mới\u0026#34;); Thao tác với classes: # element.classList.add(\u0026#34;active\u0026#34;); element.classList.remove(\u0026#34;inactive\u0026#34;); element.classList.toggle(\u0026#34;visible\u0026#34;); 👂 Bước 3: Lắng nghe (Listen) # Xử lý sự kiện: # const button = document.querySelector(\u0026#34;#myButton\u0026#34;); button.addEventListener(\u0026#34;click\u0026#34;, function (event) { // Ngăn hành vi mặc định event.preventDefault(); // Thực hiện thao tác const greeting = document.getElementById(\u0026#34;greeting\u0026#34;); greeting.textContent = \u0026#34;Bạn đã click!\u0026#34;; }); 🎯 Tổng kết # DOM: Cây đối tượng biểu diễn HTML Query: getElementById, querySelector để tìm phần tử Manipulate: Thay đổi nội dung, thuộc tính, style Listen: addEventListener để phản hồi tương tác ","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/js-beginner/04_javascript-va-dom/","section":"Tất cả bài viết","summary":"Tìm hiểu về DOM (Document Object Model) và cách JavaScript tương tác với trang web thông qua DOM API để tạo ra các trang web động.","title":"Bài 4: DOM là gì? \"Quyền năng\" Thay đổi Trang Web với JavaScript","type":"posts"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/dom/","section":"Tags","summary":"","title":"DOM","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/async/","section":"Tags","summary":"","title":"Async","type":"tags"},{"content":" 🧠 Giới thiệu về Bất đồng bộ # JavaScript về cơ bản là đơn luồng (single-threaded). Điều này có nghĩa là nó chỉ có thể làm một việc tại một thời điểm. Hãy tưởng tượng một nhân viên pha chế tại quán cà phê chỉ có thể phục vụ từng người một.\n😱 Vấn đề của Đồng bộ (Synchronous) # console.log(\u0026#34;1. Bắt đầu đặt hàng\u0026#34;); // Giả sử đây là một tác vụ nặng, mất 5 giây syncTask(5000); console.log(\u0026#34;3. Nhận được cà phê\u0026#34;); Trong mô hình đồng bộ, toàn bộ ứng dụng sẽ bị \u0026ldquo;đơ\u0026rdquo; (blocked) trong 5 giây đó.\n🔄 Event Loop - Giải pháp Bất đồng bộ # Quy trình xử lý: # Nhận tác vụ (đơn hàng) Giao cho Web API (máy pha cà phê) Tiếp tục công việc khác Nhận kết quả từ Callback Queue Event Loop điều phối 📞 Callbacks - Cấp độ 1 # Ví dụ cơ bản: # console.log(\u0026#34;1. Bắt đầu đặt hàng\u0026#34;); setTimeout(() =\u0026gt; { console.log(\u0026#34;3. Nhận được cà phê\u0026#34;); }, 2000); console.log(\u0026#34;2. Nhận hóa đơn và chờ\u0026#34;); Callback Hell: # step1(data, (result1) =\u0026gt; { step2(result1, (result2) =\u0026gt; { step3(result2, (result3) =\u0026gt; { // Code lồng nhau sâu 😱 }); }); }); 🤝 Promises - Cấp độ 2 # Ba trạng thái: # pending: Đang chờ fulfilled: Thành công rejected: Thất bại Ví dụ Promise: # fetch(\u0026#34;https://api.example.com/user/1\u0026#34;) .then((response) =\u0026gt; response.json()) .then((userData) =\u0026gt; { console.log(\u0026#34;Dữ liệu:\u0026#34;, userData); }) .catch((error) =\u0026gt; { console.error(\u0026#34;Lỗi:\u0026#34;, error); }); ⭐ Async/Await - Cấp độ 3 # Quy tắc sử dụng: # Đánh dấu hàm với async Dùng await với Promises Xử lý lỗi bằng try/catch Ví dụ Async/Await: # async function fetchUserData() { try { const response = await fetch(\u0026#34;https://api.example.com/user/1\u0026#34;); const userData = await response.json(); console.log(\u0026#34;Dữ liệu:\u0026#34;, userData); } catch (error) { console.error(\u0026#34;Lỗi:\u0026#34;, error); } } 🎯 Tổng kết # Đồng bộ: Blocking, đơn giản nhưng không hiệu quả Callbacks: Cách truyền thống, dễ rơi vào callback hell Promises: Chuỗi .then() rõ ràng, xử lý lỗi tập trung Async/Await: Cú pháp hiện đại, dễ đọc và maintain ","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/js-beginner/03_javascript-bat-dong-bo/","section":"Tất cả bài viết","summary":"Tìm hiểu về cách JavaScript xử lý các tác vụ bất đồng bộ, từ callbacks truyền thống đến promises và cú pháp hiện đại async/await.","title":"Bài 3: JavaScript Bất đồng bộ (Async) - Từ Callbacks, Promises đến Async/Await","type":"posts"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/callbacks/","section":"Tags","summary":"","title":"Callbacks","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/promises/","section":"Tags","summary":"","title":"Promises","type":"tags"},{"content":" 🔍 Giới thiệu # Trong JavaScript, bạn dùng var, let, và const để khai báo biến—những chiếc hộp có nhãn để lưu trữ dữ liệu. Trong hơn 20 năm, chúng ta chỉ có var. Kể từ ES6 (2015), chúng ta có thêm let và const.\nSự khác biệt cốt lõi nằm ở hai khái niệm: Scope (Phạm vi) và Hoisting (Kéo cờ).\n😱 Vấn đề với var và Function Scope # Function Scope là gì? # var tuân theo function scope Biến chỉ \u0026ldquo;thuộc về\u0026rdquo; hàm chứa nó Nếu không trong hàm nào, trở thành biến toàn cục Ví dụ về rò rỉ biến: # function runLoop() { for (var i = 0; i \u0026lt; 5; i++) { console.log(i); // 0, 1, 2, 3, 4 } console.log(\u0026#34;Giá trị cuối:\u0026#34;, i); // 5 - biến i rò rỉ! } runLoop(); 🤔 Hoisting với var # Hoisting là gì? # JavaScript \u0026ldquo;kéo\u0026rdquo; khai báo lên đầu scope Biến var được khởi tạo là undefined Ví dụ hoisting: # console.log(myVar); // undefined, không lỗi! var myVar = \u0026#34;Hello\u0026#34;; console.log(myVar); // \u0026#34;Hello\u0026#34; // Thực tế JS thực thi như sau: var myVar; // Hoisted console.log(myVar); myVar = \u0026#34;Hello\u0026#34;; console.log(myVar); 🎯 let và const: Giải pháp Modern # Block Scope # Biến chỉ tồn tại trong khối {} Không rò rỉ ra ngoài function runLoopFixed() { for (let i = 0; i \u0026lt; 5; i++) { console.log(i); } // console.log(i); // Lỗi: i is not defined } Temporal Dead Zone (TDZ) # console.log(myLetVar); // Lỗi: Cannot access before initialization let myLetVar = \u0026#34;Hello\u0026#34;; 💡 Khi nào dùng let vs const? # let - cho biến có thể thay đổi # let score = 0; score += 10; // OK const - cho tham chiếu không đổi # const PI = 3.14; // PI = 3.14159; // Lỗi! const user = { name: \u0026#34;Alice\u0026#34; }; user.name = \u0026#34;Bob\u0026#34;; // OK - thay đổi thuộc tính // user = {}; // Lỗi - không thể gán lại 🎯 Tổng kết # var: function scope, hoisting với undefined let/const: block scope, TDZ bảo vệ Dùng const mặc định, let khi cần thay đổi giá trị Tránh dùng var trong code hiện đại ","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/js-beginner/02_var-let-const/","section":"Tất cả bài viết","summary":"Tìm hiểu sự khác biệt giữa var, let và const trong JavaScript, cách hoạt động của scope và hoisting, và khi nào nên dùng mỗi loại.","title":"Bài 2: Hiểu Sâu về var, let, và const - Scope và Hoisting","type":"posts"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/hoisting/","section":"Tags","summary":"","title":"Hoisting","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/scope/","section":"Tags","summary":"","title":"Scope","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/variables/","section":"Tags","summary":"","title":"Variables","type":"tags"},{"content":" 🧠 JavaScript là gì? # Khi bạn nghĩ về \u0026ldquo;lập trình web\u0026rdquo;, có lẽ bạn sẽ nghĩ ngay đến HTML và CSS.\nNếu HTML là bộ khung xương (cấu trúc) và CSS là làn da, quần áo (thẩm mỹ), thì JavaScript (JS) chính là hệ thần kinh và bộ não — thứ mang lại sự sống, sự tương tác và hành vi cho trang web.\nMột trang web chỉ có HTML/CSS giống như một bức tranh tĩnh; JavaScript làm nó \u0026ldquo;sống\u0026rdquo; bằng cách xử lý sự kiện và thay đổi nội dung động.\n🎨 Bộ Ba Không Thể Tách Rời # HTML (HyperText Markup Language) # Vai trò: định nghĩa cấu trúc nội dung. Ví dụ: \u0026lt;h1\u0026gt;Chào mừng bạn\u0026lt;/h1\u0026gt; CSS (Cascading Style Sheets) # Vai trò: định nghĩa giao diện và bố cục. Ví dụ: h1 { color: red; text-align: center; } JavaScript (JS) # Vai trò: định nghĩa hành vi và tương tác. Ví dụ: button.addEventListener(\u0026#34;click\u0026#34;, () =\u0026gt; { h1.textContent = \u0026#34;Tạm biệt!\u0026#34;; }); 💪 \u0026ldquo;Quyền năng\u0026rdquo; của JavaScript ở Phía Trình Duyệt (Client-side) # DOM Manipulation # JS có thể đọc và thay đổi cấu trúc HTML (thêm/xóa/sửa phần tử) mà không cần tải lại trang. Event Handling # JS lắng nghe và phản hồi các hành động của người dùng: click, mouseover, keydown, submit,… Asynchronous Communication (AJAX / Fetch) # JS có thể gửi yêu cầu nền và cập nhật nội dung một phần trang mà không tải lại toàn bộ trang. Ví dụ fetch: fetch(\u0026#34;/api/posts\u0026#34;) .then((res) =\u0026gt; res.json()) .then((posts) =\u0026gt; console.log(posts)) .catch((err) =\u0026gt; console.error(err)); 🚀 Sự Bành Trướng: JavaScript \u0026ldquo;Ăn cả thế giới\u0026rdquo; # Node.js (Backend) # Chạy JS trên server, xây dựng API (Express, NestJS), xử lý DB và logic nghiệp vụ. Mobile # React Native, NativeScript giúp viết một lần chạy được trên iOS và Android. Desktop # Electron cho phép tạo ứng dụng desktop (ví dụ: VS Code, Slack) bằng HTML/CSS/JS. 🎯 Tổng kết (tùy chọn) # JavaScript: ngôn ngữ hành vi cho web, mở rộng ra server, mobile và desktop. Học JS là bước bắt buộc để trở thành developer hiện đại. \u0026hellip;existing code\u0026hellip;\n","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/js-beginner/01_javascript-la-gi/","section":"Tất cả bài viết","summary":"Giải thích vai trò của JavaScript trong web: DOM, event handling, AJAX, và sự phát triển sang backend, mobile, desktop.","title":"Bài 1: JavaScript là gì? Từ \"Bộ não\" của Web đến \"Kẻ thống trị\" Mọi Nền tảng","type":"posts"},{"content":"Java là một trong những ngôn ngữ lập trình phổ biến và có sức ảnh hưởng nhất trên thế giới. Từ các ứng dụng Android, hệ thống ngân hàng, cho đến các dịch vụ web khổng lồ, Java luôn có mặt ở khắp mọi nơi. Nếu bạn đang muốn bắt đầu hành trình lập trình của mình, Java chính là một lựa chọn tuyệt vời.\nBài viết này sẽ cung cấp cho bạn một lộ trình chi tiết, từ việc hiểu Java là gì cho đến việc viết chương trình đầu tiên của bạn.\n🤔 1. Java Là Gì và Tại Sao Nên Học? # Java là một ngôn ngữ lập trình hướng đối tượng, đa nền tảng được phát triển bởi Sun Microsystems (nay thuộc sở hữu của Oracle). Triết lý cốt lõi của nó là \u0026ldquo;Viết một lần, chạy mọi nơi\u0026rdquo; (Write Once, Run Anywhere - WORA).\nNhững lý do bạn nên học Java # Đa nền tảng: Mã Java được biên dịch thành \u0026ldquo;bytecode\u0026rdquo;, có thể chạy trên bất kỳ máy nào có cài đặt Máy ảo Java (JVM). Lập trình hướng đối tượng (OOP): Giúp tổ chức mã nguồn một cách logic, dễ quản lý, tái sử dụng và mở rộng. Hệ sinh thái và cộng đồng lớn: Có một cộng đồng phát triển khổng lồ cùng với hàng triệu thư viện và framework (như Spring, Hibernate). Nhu cầu tuyển dụng cao: Java liên tục nằm trong top các ngôn ngữ lập trình được yêu cầu nhiều nhất trên thị trường việc làm. 🛠️ 2. Chuẩn Bị Môi Trường Phát Triển # Để bắt đầu lập trình Java, bạn cần hai thứ chính:\nJDK (Java Development Kit): Bộ công cụ dành cho nhà phát triển, bao gồm Trình biên dịch (Compiler) và Máy ảo Java (JVM). Bạn có thể tải JDK từ trang web của Oracle hoặc các bản OpenJDK như Adoptium. IDE (Integrated Development Environment): Một trình soạn thảo code thông minh giúp bạn viết, biên dịch và chạy chương trình dễ dàng hơn. IntelliJ IDEA (Community Edition): Rất mạnh mẽ và thông minh. Visual Studio Code (với Java Extension Pack): Nhẹ nhàng và linh hoạt. Eclipse: Một IDE kỳ cựu và vẫn rất phổ biến. 🚀 3. Chương Trình \u0026ldquo;Hello, World!\u0026rdquo; Đầu Tiên # Sau khi cài đặt xong, hãy cùng viết chương trình Java đầu tiên. Mở IDE của bạn và tạo một tệp mới tên là HelloWorld.java.\n// Tên class phải trùng với tên tệp public class HelloWorld { // Đây là phương thức chính, nơi chương trình bắt đầu chạy public static void main(String[] args) { // In dòng chữ \u0026#34;Hello, World!\u0026#34; ra màn hình console System.out.println(\u0026#34;Hello, World!\u0026#34;); } } Giải thích # public class HelloWorld: Khai báo một lớp (class) có tên là HelloWorld. Trong Java, mọi thứ đều nằm trong các lớp. public static void main(String[] args): Đây là phương thức main, điểm khởi đầu của mọi chương trình Java. System.out.println(\u0026quot;Hello, World!\u0026quot;);: Lệnh này dùng để in một chuỗi ký tự ra console. Bây giờ, hãy nhấn nút \u0026ldquo;Run\u0026rdquo; trong IDE của bạn. Bạn sẽ thấy dòng chữ Hello, World! xuất hiện. Chúc mừng, bạn đã viết thành công chương trình Java đầu tiên!\n🧠 4. Các Khái Niệm Cú Pháp Cơ Bản # Biến (Variables) # Dùng để lưu trữ dữ liệu. Mỗi biến phải có một kiểu dữ liệu xác định.\nString name = \u0026#34;Alice\u0026#34;; // Chuỗi ký tự int age = 30; // Số nguyên double score = 9.5; // Số thực boolean isStudent = true; // Giá trị đúng/sai Toán tử (Operators) # Dùng để thực hiện các phép toán.\nint a = 10; int b = 5; int sum = a + b; // Phép cộng int product = a * b; // Phép nhân Cấu trúc điều khiển (Control Flow) # Dùng để quyết định luồng chạy của chương trình.\nif-else (Rẽ nhánh):\nint a = 10; if (a \u0026gt; 5) { System.out.println(\u0026#34;a lớn hơn 5\u0026#34;); } else { System.out.println(\u0026#34;a không lớn hơn 5\u0026#34;); } for (Vòng lặp):\n// In các số từ 0 đến 4 for (int i = 0; i \u0026lt; 5; i++) { System.out.println(\u0026#34;Số hiện tại là: \u0026#34; + i); } 🎯 Tổng kết # Bạn đã đi qua những bước đầu tiên quan trọng nhất trên con đường chinh phục Java.\n🤔 Hiểu khái niệm: Java là ngôn ngữ đa nền tảng, hướng đối tượng. 🛠️ Cài đặt môi trường: Cài đặt JDK và một IDE (như IntelliJ hoặc VS Code). 🚀 Viết code đầu tiên: Tạo lớp, viết phương thức main và dùng System.out.println() để in ra console. 🧠 Nắm cú pháp cơ bản: Làm quen với biến, toán tử và cấu trúc điều khiển như if-else, for. Nền tảng vững chắc hôm nay sẽ là bàn đạp để bạn tiến xa hơn với các chủ đề thú vị như OOP, Collections, và Spring Boot. Hãy kiên trì luyện tập nhé!\n","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/java-beginner/01_nhap-mon-java/","section":"Tất cả bài viết","summary":"Hướng dẫn toàn diện về Java cho người mới bắt đầu, từ khái niệm, cài đặt môi trường đến viết chương trình Hello World đầu tiên.","title":"Bài 1: Nhập Môn Java - Hướng Dẫn Toàn Diện","type":"posts"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/c%C6%A1-b%E1%BA%A3n/","section":"Tags","summary":"","title":"Cơ Bản","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/gi%E1%BB%9Bi-thi%E1%BB%87u/","section":"Tags","summary":"","title":"Giới Thiệu","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/hello-world/","section":"Tags","summary":"","title":"Hello World","type":"tags"},{"content":"","date":"13-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/web/","section":"Tags","summary":"","title":"Web","type":"tags"},{"content":" Chào mừng đến với Hannie\u0026rsquo;s Blog! # Đây là nơi mình ghi lại hành trình khám phá thế giới lập trình, với một niềm đam mê đặc biệt dành cho Lập trình Mạng. Tại đây, bạn sẽ tìm thấy các bài viết chuyên sâu, hướng dẫn thực tế và những chia sẻ kinh nghiệm xoay quanh hai ngôn ngữ mình yêu thích: Java và JavaScript.\nDù bạn đang tìm hiểu về những khái niệm nền tảng như Socket, cách xây dựng RESTful API hiệu quả, hay các chủ đề nâng cao khác, mình hy vọng rằng những nội dung trên blog sẽ là nguồn tài liệu hữu ích cho con đường học tập và phát triển của bạn.\nCùng nhau khám phá và chinh phục thế giới code nhé! 👇\n","date":"12-10-2025","externalUrl":null,"permalink":"/HannieBlog/posts/","section":"Tất cả bài viết","summary":"Khám phá tất cả các bài viết về lập trình mạng, Java, JavaScript và các chủ đề liên quan. Cập nhật kiến thức và chia sẻ kinh nghiệm cùng mình.","title":"Tất cả bài viết","type":"posts"},{"content":"","date":"12-10-2025","externalUrl":null,"permalink":"/HannieBlog/categories/about/","section":"Categories","summary":"","title":"About","type":"categories"},{"content":" 👋 Giới thiệu # Chào mừng bạn đã ghé thăm góc nhỏ trên Internet của mình. Mình là Ngô Bích Hằng (Hannie), một sinh viên năm cuối (2022-2026) chuyên ngành Công nghệ thông tin tại trường Đại học Công nghệ TP.HCM HUTECH, với kết quả học tập tốt (GPA 3.66/4).\nMình có một niềm đam mê đặc biệt với việc phát triển giao diện người dùng (Front-End) và mong muốn được áp dụng kiến thức để tạo ra những sản phẩm có trải nghiệm người dùng tốt.\n🚀 Hành trình đến với Lập trình Front-End # Mình bắt đầu với lập trình vì tò mò, nhưng lại thực sự say mê khi tham gia các cuộc thi như \u0026lsquo;My First Website\u0026rsquo; hay \u0026lsquo;IT Code War\u0026rsquo;. Khoảnh khắc \u0026ldquo;a-ha!\u0026rdquo; của mình là khi viết những dòng HTML/CSS và JavaScript đầu tiên, và nhìn thấy một trang web tĩnh \u0026ldquo;sống dậy\u0026rdquo; với các tương tác và hiệu ứng.\nCảm giác biến một thiết kế (UI) thành một sản phẩm mà người dùng có thể trực tiếp trải nghiệm (UX) thật sự rất tuyệt vời. Từ đó, mình đã không ngừng tìm tòi về React và các kỹ năng để tạo ra các ứng dụng web mượt mà, hiệu quả.\n🛠️ Kinh nghiệm \u0026amp; Hoạt động # Kinh nghiệm Giảng dạy # Ngoài thời gian học và làm dự án cá nhân, mình còn có kinh nghiệm làm việc tại Học viện Sáng tạo Công nghệ Teky với vị trí Giảng viên Part-time (từ 2025 đến nay). Tại đây, công việc của mình bao gồm:\nTrực tiếp giảng dạy và trợ giảng các khóa học lập trình Front-end (JavaScript, HTML/CSS) cho trẻ em. Hỗ trợ biên soạn tài liệu, giáo án và tạo môi trường học tập sáng tạo, truyền cảm hứng. Hướng dẫn học viên phát triển tư duy logic và kỹ năng giải quyết vấn đề. Hoạt động \u0026amp; Các cuộc thi # Mình rất thích tham gia các hoạt động và cuộc thi học thuật để trau dồi kỹ năng và thử thách bản thân. Một số hoạt động tiêu biểu mình đã tham gia:\nChung kết Cuộc thi \u0026ldquo;IT CODE WAR 2025\u0026rdquo; Giải Khuyến khích Cuộc thi \u0026ldquo;Gen Z\u0026rsquo;s thinking 2024\u0026rdquo; Giải Khuyến khích Cuộc thi \u0026ldquo;My First Website 2024\u0026rdquo; Tham gia Cuộc thi \u0026ldquo;Database Design Champion 2024\u0026rdquo; Tham gia Cuộc thi \u0026ldquo;HUTECH\u0026rsquo;S NET Cracking Challenges 2024\u0026rdquo; Tham gia các cuộc thi học thuật khác như \u0026ldquo;Tư duy Logic toán học\u0026rdquo; và \u0026ldquo;IQ Toán học\u0026rdquo; 🧠 Kỹ năng \u0026amp; Công nghệ # Dưới đây là một số công nghệ và công cụ mình yêu thích và thường xuyên sử dụng:\nNgôn ngữ chính: JavaScript, HTML5, CSS3 Frameworks \u0026amp; Thư viện: React Các ngôn ngữ đã học: C, C#, Java, Python Cơ sở dữ liệu: SQL Kỹ năng \u0026amp; Chứng chỉ: Kỹ năng làm việc nhóm, giao tiếp và quản lý thời gian Networking Basics Tiếng Anh (Chứng chỉ B1) Chứng chỉ đã đạt được # JavaScript Essentials 1 JavaScript Essentials 2 🎯 Mục tiêu của Blog # Mình tạo ra blog này với mong muốn:\nHệ thống hóa lại kiến thức của bản thân. Chia sẻ những gì mình học được về Front-End và các công nghệ mới với cộng đồng một cách dễ hiểu nhất. Kết nối với những người có cùng đam mê để cùng nhau học hỏi và phát triển. Hy vọng rằng những bài viết ở đây sẽ mang lại giá trị nào đó cho bạn. Đừng ngần ngại để lại bình luận hay góp ý nhé!\n🔗 Kết nối với mình # Bạn có thể tìm thấy mình tại:\nGitHub: https://github.com/Hannie1812 Facebook: https://www.facebook.com/hang.ngobichhang.3/ Email: hanniego182@gmail.com ","date":"12-10-2025","externalUrl":null,"permalink":"/HannieBlog/aboutme/","section":"Hannie's Blog","summary":"\u003cfigure\u003e\n        \u003cimg\n          class=\"my-0 rounded-md mx-auto\"\n          srcset=\"\n          /HannieBlog/img/about_me_hu_f53fc0987d2948cc.png 330w,\n          /HannieBlog/img/about_me_hu_2001002a0bbbb622.png 660w,\n          /HannieBlog/img/about_me_hu_59c4808ab633191.png 1024w,\n          /HannieBlog/img/about_me_hu_41ddb2efb3433a27.png 2x\"\n          src=\"/HannieBlog/img/about_me_hu_2001002a0bbbb622.png\"\n          data-zoom-src=\"/HannieBlog/img/about_me_hu_41ddb2efb3433a27.png\"\n          alt=\"Ảnh đại diện\"\n        /\u003e\n  \n  \n  \u003c/figure\u003e\n\n\u003ch2 class=\"relative group\"\u003e👋 Giới thiệu \n    \u003cdiv id=\"-giới-thiệu\" class=\"anchor\"\u003e\u003c/div\u003e\n    \n    \u003cspan\n        class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none\"\u003e\n        \u003ca class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline\" href=\"#-gi%e1%bb%9bi-thi%e1%bb%87u\" aria-label=\"Cọc dấu\"\u003e#\u003c/a\u003e\n    \u003c/span\u003e        \n    \n\u003c/h2\u003e\n\u003cp\u003eChào mừng bạn đã ghé thăm góc nhỏ trên Internet của mình. Mình là \u003cstrong\u003eNgô Bích Hằng (Hannie)\u003c/strong\u003e, một sinh viên năm cuối (2022-2026) chuyên ngành Công nghệ thông tin tại trường Đại học Công nghệ TP.HCM HUTECH, với kết quả học tập tốt (GPA 3.66/4).\u003c/p\u003e","title":"About Me","type":"page"},{"content":"","date":"12-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/c%C3%A1-nh%C3%A2n/","section":"Tags","summary":"","title":"Cá Nhân","type":"tags"},{"content":"","date":"12-10-2025","externalUrl":null,"permalink":"/HannieBlog/tags/kinh-nghi%E1%BB%87m/","section":"Tags","summary":"","title":"Kinh Nghiệm","type":"tags"},{"content":"","externalUrl":null,"permalink":"/HannieBlog/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"}]