Hello World — cấu trúc một class Java
Viết và hiểu chương trình Java đầu tiên từng dòng: class, main method, System.out.println, package, command-line args và lỗi thường gặp khi chạy.
TL;DR: Mọi chương trình Java tối thiểu cần một class và một main method với signature cố định public static void main(String[] args). main phải static để JVM gọi trực tiếp mà không cần tạo object trước. Tên file phải khớp chính xác tên public class (bao gồm hoa/thường). System.out.println in ra console và xuống dòng, System.out.print in mà không xuống dòng. Hiểu từng keyword trong 5 dòng Hello World là nền tảng để đọc được mọi chương trình Java sau này.
Mọi ngôn ngữ lập trình đều có nghi lễ khởi đầu: Hello World. Không phải vì chương trình này làm được gì đặc biệt — mà vì nó kiểm chứng rằng toàn bộ chuỗi công cụ (JDK + IDE) hoạt động đúng, và bạn hiểu cấu trúc tối thiểu để Java chạy được.
Bài này không chỉ dạy bạn viết Hello World — mà giải thích từng keyword, từng dòng và cơ chế đằng sau, để bạn không bị lạc khi gặp lỗi đầu tiên.
1. Analogy — "Thư gửi đi"
Hình dung bạn gửi một lá thư:
- Phong bì — có địa chỉ người gửi, người nhận, tem. Bưu điện cần thông tin này để biết xử lý ra sao.
- Nội dung thư — bên trong phong bì, đây là thứ người nhận thực sự đọc.
Mọi chương trình Java có cấu trúc tương tự. Bảng ánh xạ từng phần của lá thư sang phần tương ứng trong chương trình:
| Lá thư | Chương trình Java |
|---|---|
| Phong bì (địa chỉ) | class skeleton public class HelloWorld |
| Trang đầu / lời mở | main method (điểm JVM bắt đầu) |
| Nội dung thư | code trong main (thứ thực thi) |
Thiếu phong bì thì bưu điện không biết làm gì. Tương tự, thiếu class skeleton thì JVM không biết chạy gì.
Class = phong bì (bắt buộc, cấu trúc cố định). main method = trang đầu của thư (JVM đọc trang này trước tiên). Code bên trong main = nội dung thư (thứ thực sự xảy ra).
2. Chương trình Hello World
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Chạy chương trình:
# Compile source sang bytecode
javac HelloWorld.java
# Chay bytecode tren JVM
java HelloWorld
# Output:
# Hello, World!
Chỉ 5 dòng. Nhưng mỗi từ có lý do tồn tại.
3. Phân tích từng dòng
3.1 public class HelloWorld
public class HelloWorld {
Ba thành phần:
| Từ khoá | Ý nghĩa |
|---|---|
public | Access modifier — ai cũng có thể dùng class này (JVM, code khác) |
class | Khai báo đây là một class — đơn vị tổ chức code cơ bản nhất của Java |
HelloWorld | Tên class — phải khớp chính xác với tên file (bao gồm hoa/thường) |
Quy tắc tên file: Nếu class là public class HelloWorld, file phải tên HelloWorld.java. Không phải helloworld.java, không phải Hello_World.java. Sai tên file → javac từ chối compile.
3.2 main method — phải viết chính xác như thế nào?
public static void main(String[] args) {
Đây là entry point — điểm JVM bắt đầu thực thi. Signature này cố định hoàn toàn:
| Phần | Giải thích |
|---|---|
public | JVM phải gọi được method này từ bên ngoài class |
static | JVM gọi trực tiếp mà không cần tạo object từ class trước |
void | Method không trả về giá trị gì (JVM không cần giá trị trả về) |
main | Tên mà JVM tìm kiếm khi khởi động — tên này cố định, không đổi được |
String[] args | Mảng chuỗi nhận từ command line (xem phần 5) |
Nếu main không phải static, JVM phải tạo object từ class trước khi gọi — nhưng class có thể có nhiều constructor với tham số khác nhau, JVM không có quy tắc chung để đoán. Thiết kế public static void main(String[]) là signature cố định, JVM tìm chính xác và gọi không cần khởi tạo — đơn giản, không mơ hồ. Sai bất kỳ phần nào (bỏ static, đổi tên thành Main) → JVM không tìm thấy → chương trình không chạy.
3.3 System.out.println("Hello, World!")
System.out.println("Hello, World!");
Đây là lời gọi method theo chuỗi:
System— class trong thư viện chuẩnjava.lang, đại diện cho hệ thốngout— field static củaSystem, là một objectPrintStreamđại diện cho console outputprintln— method củaPrintStream, in chuỗi ra console và xuống dòng
Nói ngắn: "Lấy object out từ class System, gọi method println trên nó với chuỗi "Hello, World!"".
Sự khác biệt giữa print và println:
Nhìn đoạn code dưới đây. Trước khi đọc comment output, hãy đoán: nếu gọi print("A") rồi print("B"), output trông thế nào? Còn println("A") rồi println("B") thì khác gì?
System.out.print("A"); // In "A", KHONG xuong dong
System.out.print("B"); // In "B" ngay sau A
// Output: AB
System.out.println("A"); // In "A" ROI xuong dong
System.out.println("B"); // In "B" dong moi
// Output:
// A
// B
4. Sơ đồ từ source đến thực thi
flowchart LR src["HelloWorld.java<br/>(source code)"] javac["javac<br/>(compiler)"] cls["HelloWorld.class<br/>(bytecode)"] jvm["java<br/>(JVM launcher)"] out["Hello World!<br/>(console output)"] src --> javac --> cls --> jvm --> out
HelloWorld.java— source code bạn viết, con người đọc đượcjavac HelloWorld.java— compiler đọc source, tạo raHelloWorld.classHelloWorld.class— bytecode, JVM đọc được, không phụ thuộc OSjava HelloWorld— JVM launcher tải.class, tìm methodmain, bắt đầu chạy- Output — kết quả in ra console
5. Command-line arguments — args
String[] args không phải syntax vô nghĩa. Đó là mảng chuỗi nhận từ dòng lệnh khi bạn chạy chương trình.
// Chao.java
public class Chao {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Xin chao, ban!");
} else {
// args[0] la tham so dau tien
System.out.println("Xin chao, " + args[0] + "!");
}
}
}
Chạy với tham số:
java Chao
# Output: Xin chao, ban!
java Chao An
# Output: Xin chao, An!
java Chao "Nguyen Van An"
# Output: Xin chao, Nguyen Van An!
Khi bạn chạy java Chao An Binh, args sẽ là ["An", "Binh"]:
args[0]="An"args[1]="Binh"args.length=2
6. Package — tổ chức class theo namespace
Khi dự án lớn, bạn có hàng chục class. Package là cách nhóm class liên quan lại — giống thư mục trong filesystem.
// com/example/HelloWorld.java
package com.example;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello from package!");
}
}
Convention đặt tên package: reverse domain name — tên miền đọc ngược:
- Công ty
example.com→ package prefixcom.example - Công ty
olhub.org→ package prefixorg.olhub - Nhóm học tập → có thể dùng
com.yourname.projectname
Nếu không khai báo package:
Class thuộc default (unnamed) package — chạy được, nhưng không thể import từ package khác. Dùng được cho file học tập nhỏ, không nên dùng cho dự án thực tế.
# Compile class co package
javac com/example/HelloWorld.java
# Chay (phai chi ro fully qualified name)
java com.example.HelloWorld
Trong IntelliJ IDEA, package được tự động tạo khi bạn chọn thư mục khi tạo class mới — IDE lo phần này cho bạn.
7. Lỗi nào hay gặp khi mới chạy Java?
Lỗi 1: Tên class không khớp tên file
HelloWorld.java:1: error: class HiWorld is public, should be declared in a file named HiWorld.java
public class HiWorld {
Sửa: đổi tên file thành HiWorld.java hoặc đổi tên class thành HelloWorld.
Lỗi 2: Signature main sai
Error: Main method not found in class HelloWorld, please define the main method as:
public static void main(String[] args)
Nguyên nhân thường gặp:
- Viết
void main()(thiếuString[] args) - Viết
public void main(thiếustatic) - Viết
Mainthay vìmain(Java case-sensitive)
Sửa: copy chính xác public static void main(String[] args).
Lỗi 3: Chạy trong thư mục sai
Error: Could not find or load main class HelloWorld
Caused by: java.lang.ClassNotFoundException: HelloWorld
Sửa: phải chạy java HelloWorld từ thư mục chứa HelloWorld.class, không phải thư mục khác.
Lỗi 4: Quên compile trước khi chạy
Error: Could not find or load main class HelloWorld
Sửa: chạy javac HelloWorld.java trước, sau đó mới java HelloWorld.
8. Java 11+ — single-file execution
Từ Java 11, bạn có thể chạy file .java trực tiếp mà không cần compile thủ công bằng lệnh java HelloWorld.java. Chi tiết cơ chế, giới hạn, và cách đọc bytecode được trình bày đầy đủ ở Compile & Run.
9. Access modifiers — primer nhanh
Bài này dùng public nhiều lần. Đây là preview — chi tiết sẽ có ở module OOP:
| Modifier | Ai thấy được? | Thường dùng cho |
|---|---|---|
public | Tất cả — mọi class, mọi package | Class chính, method API |
protected | Cùng package + subclass | Method kế thừa |
| (không ghi gì) | Chỉ cùng package | Utility class nội bộ |
private | Chỉ trong class đó | Field, method nội bộ |
Quy tắc đơn giản cho giai đoạn này: class public, main public static — đây là pattern bắt buộc để JVM chạy được.
10. Deep Dive Oracle
Spec chính thức:
- JLS §12.1 — Java Virtual Machine Startup — mô tả chính xác JVM tìm và gọi method
mainnhư thế nào, bao gồm điều kiện về signature - JLS §8.1 — Class Declarations — cú pháp khai báo class, access modifiers, và quy tắc về tên
- JEP 330 — Launch Single-File Source-Code Programs — spec cho tính năng
java HelloWorld.javatừ Java 11
Ghi chú đọc spec: JLS §12.1 giải thích tại sao signature main phải chính xác là public static void main(String[]) — JVM lookup method theo descriptor, sai một chút sẽ không match. Đây cũng là lý do Java 21 thử nghiệm "unnamed main method" (JEP 445) để đơn giản hóa việc học.
11. Liên hệ các bài khác
- Cài đặt môi trường — JDK, IntelliJ IDEA, và lệnh
javac/javađược thiết lập ở bài đó; bài này dùng trực tiếp các công cụ đó để chạy Hello World. - Tư duy lập trình — Input, Xử lý, Output — sau khi viết được Hello World, bài tiếp theo dạy cách tư duy bài toán theo mô hình I-P-O trước khi code.
- Compile & Run — đào sâu toàn bộ quy trình
javac→ bytecode → JVM, single-file execution Java 11+, và cách đọc.classfile.
12. Tóm tắt
- Mọi chương trình Java cần ít nhất 1 class và 1
mainmethod với signature cố định. public class Foo— tên class phải khớp tên fileFoo.java.public static void main(String[] args)— JVM tìm chính xác signature này; sai là không chạy.System.out.println— in ra console và xuống dòng;System.out.printkhông xuống dòng.args— mảng chuỗi nhận từ command line, truy cập quaargs[0],args[1]...- Package — namespace tổ chức class; convention
reverse domain name(com.company.project). - Từ Java 11:
java HelloWorld.javachạy trực tiếp không cần compile thủ công.
13. Tự kiểm tra
Q1main phải là static? Nếu bỏ static điều gì xảy ra và tại sao JVM không tự tạo object của class?▸
main phải là static? Nếu bỏ static điều gì xảy ra và tại sao JVM không tự tạo object của class?static cho phép JVM gọi method không cần tạo instance trước. Nếu không static, JVM phải new Foo() — nhưng class có thể có nhiều constructor nhận tham số khác nhau, JVM không có quy tắc chung để đoán tham số nào. Thiết kế public static void main(String[]) là signature cố định, JVM tìm chính xác và gọi không cần khởi tạo — đơn giản, không mơ hồ.Q2Chạy java HelloWorld báo ClassNotFoundException. Nguyên nhân có thể là gì? Liệt kê ít nhất 3 nguyên nhân.▸
java HelloWorld báo ClassNotFoundException. Nguyên nhân có thể là gì? Liệt kê ít nhất 3 nguyên nhân.- Chưa chạy
javac HelloWorld.java→ không có fileHelloWorld.class. - Gõ sai cú pháp:
java HelloWorld.class(thừa đuôi) thay vìjava HelloWorld. - File
.classở thư mục khác, cầnjava -cp path/to/classes HelloWorld. - File có
package com.foo;nhưng chạy không đúng vị trí: phải từ thư mục cha gọijava com.foo.HelloWorld. - Tên class trong file không khớp tên file (
public class BartrongHelloWorld.java).
Q3System.out.println(10 + 20 + "hello") in gì? Còn System.out.println("hello" + 10 + 20) in gì? Giải thích sự khác nhau.▸
System.out.println(10 + 20 + "hello") in gì? Còn System.out.println("hello" + 10 + 20) in gì? Giải thích sự khác nhau.10 + 20 + "hello"→"30hello". Operator+left-associative:(10 + 20)làm trước =30(int), rồi30 + "hello"= String concat."hello" + 10 + 20→"hello1020".("hello" + 10)="hello10"(concat), rồi"hello10" + 20="hello1020".
Quy tắc: gặp một String bên trái, mọi + phía sau đều là concat.
Q4Bạn có file Foo.java chứa public class Bar. Khi javac Foo.java xảy ra điều gì?▸
Foo.java chứa public class Bar. Khi javac Foo.java xảy ra điều gì?public, và tên class public phải khớp tên file. Trường hợp ngoại lệ: nếu class Bar không có modifier public (package-private), javac vẫn OK và sinh ra Bar.class.Q5Vì sao Java 11 có thể chạy java HelloWorld.java mà không cần javac? Cơ chế đằng sau là gì?▸
java HelloWorld.java mà không cần javac? Cơ chế đằng sau là gì?java thấy argument có đuôi .java, launcher gọi compiler internally, giữ bytecode trong RAM (không ghi .class ra disk), rồi chạy ngay. Giới hạn: chỉ 1 file, không classpath phức tạp. Mục đích: viết script nhỏ, code learning, shebang #!/usr/bin/java --source 21. Với project nhiều file vẫn phải javac → java truyền thống.Bài tiếp theo: Tư duy lập trình — input, xử lý, output
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên