- 1 1. Mối quan hệ giữa Kế thừa và super
- 2 2. Cách sử dụng super (3 mẫu cốt lõi)
- 3 3. Các ví dụ thực tế để làm chủ super
- 3.1 3.1 Ví dụ 1: Thứ tự gọi hàm tạo (Cha → Con)
- 3.2 3.2 Ví dụ 2: Cha chỉ có hàm tạo có tham số
- 3.3 3.3 Example 3: Calling a parent method after overriding (super.method())
- 3.4 3.4 Example 4: Accessing a hidden parent field (super.field)
- 3.5 3.5 Example 5: Calling both parent and child versions clearly
- 3.6 3.6 A realistic example: shared logic in the parent class
- 3.7 3.7 Section summary
- 4 4. Difference Between this and super
- 4.1 4.1 this chỉ tới đối tượng hiện tại (phiên bản con)
- 4.2 4.2 super chỉ tới phần lớp cha
- 4.3 4.3 Cách nhớ sự khác nhau một cách đơn giản
- 4.4 4.4 Sử dụng this và super với các trường
- 4.5 4.5 Sử dụng this và super với các phương thức
- 4.6 4.6 Hàm khởi tạo: this() vs super()
- 4.7 4.7 Bạn không thể dùng this() và super() cùng lúc
- 4.8 4.8 Nhắc nhở quan trọng: super không tạo ra một đối tượng mới
- 4.9 4.9 Tóm tắt phần
- 5 5. Các lỗi thường gặp và cạm bẫy (Cách tránh lỗi)
- 5.1 5.1 Viết super(...) ở vị trí sai (quy tắc hàm khởi tạo)
- 5.2 5.2 Quên super(args...) khi lớp cha không có hàm khởi tạo không đối số
- 5.3 5.3 Cố gắng sử dụng cả this() và super() trong cùng một hàm khởi tạo
- 5.4 5.4 Cố gắng sử dụng super trong một phương thức tĩnh
- 5.5 5.5 Hiểu sai super là “tạo một đối tượng cha”
- 5.6 5.6 Sử dụng super.field quá thường xuyên (ẩn trường thường là ý tưởng tồi)
- 5.7 5.7 “Nếu tôi gọi super.method(), chỉ logic của lớp cha chạy” (không phải lúc nào cũng vậy)
- 5.8 5.8 Danh sách kiểm tra nhanh (để tránh lỗi người mới)
- 6 6. Kết luận (Những điểm chính)
- 7 7. Câu hỏi thường gặp (FAQ)
- 7.1 7.1 Khi nào tôi nên sử dụng super trong Java?
- 7.2 7.2 Điều gì xảy ra nếu tôi không viết super()?
- 7.3 7.3 Sự khác biệt giữa super() và this() là gì?
- 7.4 7.4 Tôi có thể sử dụng cả this() và super() trong cùng một constructor không?
- 7.5 7.5 Tôi có thể sử dụng super bên trong phương thức static không?
- 7.6 7.6 super có tạo ra một đối tượng lớp cha mới không?
- 7.7 7.7 Nếu tôi gọi super.method(), nó có luôn chỉ chạy logic của lớp cha không?
- 7.8 7.8 Tôi có nên sử dụng super.field thường xuyên không?
- 7.9 7.9 super có thể được sử dụng với interfaces không?
- 7.10 7.10 Cách nhanh nhất để làm chủ super là gì?
1. Mối quan hệ giữa Kế thừa và super
Để hiểu từ khóa super của Java, bạn nên hiểu trước kế thừa.
super không phải là thứ bạn ghi nhớ riêng lẻ—ý nghĩa của nó sẽ rõ ràng khi bạn thấy một lớp con và một lớp cha hoạt động cùng nhau.
Trong phần này, bạn sẽ học về khái niệm kế thừa là gì và tại sao super tồn tại, thông qua các giải thích thân thiện với người mới bắt đầu và các ví dụ mã đơn giản.
1.1 Kế thừa trong Java là gì? (Cơ bản về extends)
Trong Java, kế thừa cho phép bạn tạo một lớp mới dựa trên một lớp đã tồn tại.
- Lớp cha (superclass) : lớp cơ sở
- Lớp con (subclass) : lớp kế thừa từ lớp cha
Khi một lớp con mở rộng một lớp cha, nó có thể tái sử dụng các trường và phương thức của lớp cha mà không cần viết lại chúng.
Cú pháp cơ bản của kế thừa (extends)
class Parent {
void hello() {
System.out.println("Hello from Parent");
}
}
class Child extends Parent {
}
Ở đây, Child kế thừa phương thức hello() từ Parent.
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.hello(); // calling a method inherited from Parent
}
}
Kết quả:
Hello from Parent
Vì vậy mặc dù Child không định nghĩa hello(), nó vẫn có thể gọi nó vì đã kế thừa.
1.2 Tại sao chúng ta cần super?
Ở thời điểm này, bạn có thể nghĩ:
“Nếu lớp con đã kế thừa mọi thứ từ lớp cha, tại sao chúng ta lại cần
super?”
Trong nhiều trường hợp, bạn không cần viết super một cách rõ ràng.
Tuy nhiên, super trở nên cần thiết khi bạn muốn truy cập rõ ràng phiên bản của lớp cha của một thứ gì đó.
Các tình huống phổ biến bao gồm:
- Lớp con ghi đè một phương thức, nhưng bạn vẫn muốn gọi phiên bản của lớp cha
- Lớp con và lớp cha có các trường cùng tên
- Bạn cần gọi một hàm khởi tạo của lớp cha cụ thể bằng các đối số
Tóm lại:
superđược sử dụng khi bạn muốn tham chiếu một cách rõ ràng tới phía lớp cha.
1.3 Ghi đè và super (Giữ lại hành vi của lớp cha)
Một trong những lý do phổ biến nhất để sử dụng super là ghi đè phương thức.
Ghi đè có nghĩa là lớp con cung cấp phiên bản riêng của một phương thức đã tồn tại trong lớp cha.
Ví dụ: ghi đè một phương thức
class Parent {
void greet() {
System.out.println("Parent: Hello!");
}
}
class Child extends Parent {
@Override
void greet() {
System.out.println("Child: Hi!");
}
}
Bây giờ nếu bạn gọi greet() trên một đối tượng Child:
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.greet();
}
}
Kết quả:
Child: Hi!
Phiên bản của lớp con chạy, vì ghi đè cho lớp con ưu tiên.
Gọi phiên bản của lớp cha bằng super.greet()
Đôi khi bạn không muốn thay thế hoàn toàn hành vi của lớp cha—bạn chỉ muốn thêm logic bổ sung.
Đó là nơi super.method() hữu ích:
class Parent {
void greet() {
System.out.println("Parent: Hello!");
}
}
class Child extends Parent {
@Override
void greet() {
super.greet(); // call parent version
System.out.println("Child: Hi!");
}
}
Kết quả:
Parent: Hello!
Child: Hi!
Đây là một mẫu rất thực tế:
- Lớp cha xử lý hành vi chung
- Lớp con thêm hành vi tùy chỉnh
1.4 Khi lớp cha và lớp con có cùng tên trường
Một tình huống khác mà super quan trọng là khi lớp cha và lớp con có các trường cùng tên.
Ví dụ:
class Parent {
int value = 100;
}
class Child extends Parent {
int value = 200;
void printValue() {
System.out.println(value);
}
}
Ở đây, value đề cập đến trường của lớp con, vì vậy kết quả sẽ là:
200
Nếu bạn muốn truy cập trường của lớp cha, hãy sử dụng super.value:
class Child extends Parent {
int value = 200;
void printValue() {
System.out.println(value); // child value
System.out.println(super.value); // parent value
}
}
Kết quả:
200
100
Điểm cần nhớ:
value→ trường của lớp consuper.value→ trường của lớp cha
1.5 super thực sự có nghĩa là gì (Định nghĩa đơn giản)
Bây giờ bạn có thể tóm tắt super như sau:
superlà một từ khóa cho phép bạn truy cập một cách rõ ràng phiên bản lớp cha của các trường, phương thức và hàm khởi tạo.
Nếu bạn chỉ nhớ một câu, hãy nhớ câu này:
supercó nghĩa là “sử dụng phiên bản lớp cha.”
Trong phần tiếp theo, bạn sẽ học 3 cách quan trọng nhất để sử dụng super, với các ví dụ rõ ràng cho mỗi cách.
2. Cách sử dụng super (3 mẫu cốt lõi)
Bây giờ bạn đã hiểu super có nghĩa gì, hãy tập trung vào cách nó thực sự được sử dụng trong Java.
Trong mã thực tế, super xuất hiện trong ba mẫu chính:
- Gọi hàm khởi tạo của lớp cha (
super()/super(args...)) - Gọi phương thức của lớp cha (
super.method()) - Truy cập trường của lớp cha (
super.field)
Khi bạn nắm vững ba cách này, super sẽ rất dễ nhận biết và sử dụng đúng.
2.1 Gọi hàm khởi tạo của lớp cha (super() / super(args...))
2.1.1 Nhắc lại nhanh: Hàm khởi tạo là gì?
Hàm khởi tạo là một phương thức đặc biệt chạy khi bạn tạo một đối tượng.
class Person {
Person() {
System.out.println("Person constructor");
}
}
Hàm khởi tạo được dùng để khởi tạo, chẳng hạn thiết lập các trường.
2.1.2 Trong kế thừa, hàm khởi tạo của lớp cha chạy trước
Trong kế thừa Java, việc tạo một đối tượng con luôn bắt đầu bằng việc khởi tạo phần của lớp cha trước.
Điều đó có nghĩa là:
Hàm khởi tạo lớp cha → Hàm khởi tạo lớp con
Ví dụ:
class Parent {
Parent() {
System.out.println("Parent constructor");
}
}
class Child extends Parent {
Child() {
System.out.println("Child constructor");
}
}
public class Main {
public static void main(String[] args) {
new Child();
}
}
Kết quả:
Parent constructor
Child constructor
Mặc dù chúng ta không viết super(), hàm khởi tạo của lớp cha vẫn được chạy.
2.1.3 super() được chèn tự động (khi có thể)
Nếu lớp cha có hàm khởi tạo không đối số, Java sẽ tự động chèn dòng này vào đầu hàm khởi tạo của lớp con:
super();
Vì vậy hai hàm khởi tạo này về cơ bản là giống nhau:
Child() {
System.out.println("Child constructor");
}
Child() {
super(); // implicitly inserted by the compiler
System.out.println("Child constructor");
}
2.1.4 Khi lớp cha chỉ có hàm khởi tạo có tham số
Đây là một trong những lỗi phổ biến nhất của người mới bắt đầu.
Nếu lớp cha không có hàm khởi tạo không đối số, Java không thể tự động chèn super().
Ví dụ (điều này sẽ gây lỗi biên dịch):
class Parent {
Parent(int x) {
System.out.println("Parent: " + x);
}
}
class Child extends Parent {
Child() {
System.out.println("Child created");
}
}
Lý do lỗi:
- Trình biên dịch cố gắng chèn
super(); - Nhưng
Parent()không tồn tại
✅ Sửa: gọi rõ ràng hàm khởi tạo của lớp cha đúng
class Child extends Parent {
Child() {
super(10);
System.out.println("Child created");
}
}
Kết quả:
Parent: 10
Child created
2.1.5 super(...) phải là dòng đầu tiên trong hàm khởi tạo
Java yêu cầu super(...) là câu lệnh đầu tiên trong một hàm khởi tạo.
❌ Sai:
Child() {
System.out.println("Something first");
super(10); // compile error
}
✅ Đúng:
Child() {
super(10);
System.out.println("Child logic");
}
Lý do:
- Phần cha phải được khởi tạo trước khi phần con có thể chạy an toàn logic của riêng nó.
2.2 Gọi phương thức cha (super.method())
Một trường hợp sử dụng rất phổ biến khác là gọi phương thức cha sau khi ghi đè.
2.2.1 Ví dụ cơ bản
class Parent {
void show() {
System.out.println("Parent show()");
}
}
class Child extends Parent {
@Override
void show() {
System.out.println("Child show()");
}
void test() {
super.show(); // parent version
show(); // child version
}
}
public class Main {
public static void main(String[] args) {
new Child().test();
}
}
Kết quả:
Parent show()
Child show()
2.2.2 Khi nào điều này hữu ích?
super.method() hữu ích khi:
- Phương thức cha chứa logic chung
- Phương thức con cần mở rộng hành vi thay vì thay thế nó
- Bạn muốn tránh sao chép-dán logic cha vào con
Ví dụ:
class Parent {
void process() {
System.out.println("Common processing");
}
}
class Child extends Parent {
@Override
void process() {
super.process();
System.out.println("Child-specific processing");
}
}
Mẫu này giữ cho mã của bạn sạch sẽ và dễ bảo trì.
2.3 Truy cập trường cha (super.field)
Các trường hoạt động tương tự. Nếu cả cha và con đều có trường với cùng tên, trường con sẽ che giấu trường cha.
Ví dụ:
class Parent {
int value = 100;
}
class Child extends Parent {
int value = 200;
void print() {
System.out.println(value); // child field
System.out.println(super.value); // parent field
}
}
public class Main {
public static void main(String[] args) {
new Child().print();
}
}
Kết quả:
200
100
2.3.1 Bạn có nên làm điều này trong các dự án thực tế không?
Thường thì việc có cùng tên trường trong cha và con không phải là ý tưởng hay vì nó có thể gây nhầm lẫn cho người đọc.
Tuy nhiên, nó có thể xảy ra khi:
- Bạn đang bảo trì mã di sản
- Bạn bị buộc phải khớp với thiết kế lớp cha hiện có
- Bạn cần phân biệt rõ ràng giá trị cha so với con
Trong những trường hợp đó, super.field là công cụ đúng đắn.
2.4 Tóm tắt 3 mẫu
Bạn có thể nhớ super như thế này:
super()/super(args...)→ gọi hàm tạo chasuper.method()→ gọi phương thức chasuper.field→ truy cập trường cha
Trong phần tiếp theo, chúng ta sẽ đi sâu hơn với các ví dụ mã thực tế để bạn có thể thấy rõ cách các mẫu này hoạt động khi thực thi.
3. Các ví dụ thực tế để làm chủ super
Lúc này, bạn đã biết ba cách cốt lõi để sử dụng super.
Bây giờ hãy làm cho kiến thức đó dính lại bằng cách chạy qua các ví dụ thực tế.
Phần này tập trung vào:
- Thứ tự thực thi
- Điều gì được gọi và tại sao
- Cách
superthay đổi hành vi trong mã thực tế
3.1 Ví dụ 1: Thứ tự gọi hàm tạo (Cha → Con)
Quy tắc quan trọng nhất cần nhớ:
Khi tạo đối tượng con, Java luôn khởi tạo phần cha trước.
class Parent {
Parent() {
System.out.println("Parent constructor");
}
}
class Child extends Parent {
Child() {
super(); // optional here, but allowed
System.out.println("Child constructor");
}
}
public class Main {
public static void main(String[] args) {
new Child();
}
}
Kết quả:
Parent constructor
Child constructor
Điểm chính:
Ngay cả nếu bạn loại bỏ super(), kết quả vẫn giống nhau (miễn là cha có hàm tạo không đối số).
3.2 Ví dụ 2: Cha chỉ có hàm tạo có tham số
Nếu lớp cha không có hàm tạo không đối số, con phải gọi rõ ràng hàm tạo đúng.
class Parent {
Parent(String name) {
System.out.println("Parent: " + name);
}
}
class Child extends Parent {
Child() {
super("Taro");
System.out.println("Child đã được tạo");
}
}
public class Main {
public static void main(String[] args) {
new Child();
}
}
Output:
Parent: Taro
Child đã được tạo
Key point:
If you forget super("Taro"), Java tries to insert super() automatically and fails.
3.3 Example 3: Calling a parent method after overriding (super.method())
This is one of the most useful patterns in real projects.
class Parent {
void greet() {
System.out.println("Xin chào từ Parent");
}
}
class Child extends Parent {
@Override
void greet() {
super.greet(); // gọi hành vi của lớp cha
System.out.println("Xin chào từ Child");
}
}
public class Main {
public static void main(String[] args) {
new Child().greet();
}
}
Output:
Xin chào từ Parent
Xin chào từ Child
Key point:
You can extend parent behavior without rewriting it.
3.4 Example 4: Accessing a hidden parent field (super.field)
If parent and child both define a field with the same name, the child version hides the parent version.
class Parent {
int value = 100;
}
class Child extends Parent {
int value = 200;
void printValues() {
System.out.println("Giá trị Child: " + value);
System.out.println("Giá trị Parent: " + super.value);
}
}
public class Main {
public static void main(String[] args) {
new Child().printValues();
}
}
Output:
Giá trị Child: 200
Giá trị Parent: 100
Key point:
valuerefers to the child’s fieldsuper.valuerefers to the parent’s field
3.5 Example 5: Calling both parent and child versions clearly
This is a great example to remove confusion.
class Parent {
void show() {
System.out.println("show() của Parent");
}
}
class Child extends Parent {
@Override
void show() {
System.out.println("show() của Child");
}
void test() {
super.show(); // phiên bản của lớp cha
this.show(); // phiên bản của con (giống như chỉ gọi show())
}
}
public class Main {
public static void main(String[] args) {
new Child().test();
}
}
Output:
show() của Parent
show() của Child
Key point:
super.show()= parentthis.show()= child
3.6 A realistic example: shared logic in the parent class
In real applications, parent classes often contain common logic such as logging, validation, or setup steps.
class BaseService {
void execute() {
System.out.println("[LOG] Bắt đầu thực thi");
}
}
class UserService extends BaseService {
@Override
void execute() {
super.execute(); // tái sử dụng hành vi được chia sẻ
System.out.println("Logic của UserService đang chạy...");
}
}
public class Main {
public static void main(String[] args) {
new UserService().execute();
}
}
Output:
[LOG] Bắt đầu thực thi
Logic của UserService đang chạy...
Why this is useful:
- You avoid duplicating common behavior
- The child focuses only on what’s unique
- Your code becomes easier to maintain
3.7 Section summary
From these examples, you should remember:
- Parent constructors run before child constructors
super(args...)is required if the parent has no no-arg constructorsuper.method()is perfect for reusing and extending behaviorsuper.fieldhelps when parent and child fields share the same name
Next, we’ll clarify one of the most confusing topics for beginners:
What’s the difference between
thisandsuper?
4. Difference Between this and super
When learning Java’s super, you will almost always see this at the same time.
Both are reference keywords, so it’s easy for beginners to mix them up.
In this section, you’ll learn:
thiscó nghĩa là gìsupercó nghĩa là gì- Cách sử dụng chúng một cách chính xác với các trường, phương thức và hàm khởi tạo
4.1 this chỉ tới đối tượng hiện tại (phiên bản con)
this có nghĩa là:
“đối tượng hiện tại”
Nó thường được dùng khi tên trường và tên tham số của hàm khởi tạo trùng nhau.
class User {
String name;
User(String name) {
this.name = name; // field = parameter
}
}
Ở đây:
name(bên phải) là tham số của hàm khởi tạothis.name(bên trái) là trường của đối tượng
Vì vậy this giúp bạn tránh nhầm lẫn. 
4.2 super chỉ tới phần lớp cha
super chỉ tới phần lớp cha (siêu lớp) của đối tượng hiện tại.
Nó có nghĩa là:
“sử dụng phiên bản của lớp cha”
Bạn thường dùng nó khi muốn truy cập các thành viên của lớp cha bị ẩn hoặc bị ghi đè.
4.3 Cách nhớ sự khác nhau một cách đơn giản
Một cách thân thiện với người mới bắt đầu để ghi nhớ:
this→ phần lớp consuper→ phần lớp cha
Chúng không phải là hai đối tượng khác nhau.
Chúng là hai “góc nhìn” khác nhau của cùng một đối tượng.
4.4 Sử dụng this và super với các trường
Nếu cả lớp cha và lớp con đều định nghĩa một trường có cùng tên, bạn có thể rõ ràng chọn trường nào muốn dùng.
class Parent {
int value = 100;
}
class Child extends Parent {
int value = 200;
void print() {
System.out.println(this.value); // child field
System.out.println(super.value); // parent field
}
}
public class Main {
public static void main(String[] args) {
new Child().print();
}
}
Kết quả:
200
100
Điểm quan trọng:
this.value= trường của lớp consuper.value= trường của lớp cha
Ngoài ra, trong Java, this thường là tùy chọn:
System.out.println(value);
cũng tương đương với:
System.out.println(this.value);
4.5 Sử dụng this và super với các phương thức
Các phương thức là nơi thường gặp nhất sự khác biệt giữa this và super—đặc biệt khi có việc ghi đè.
class Parent {
void show() {
System.out.println("Parent show()");
}
}
class Child extends Parent {
@Override
void show() {
System.out.println("Child show()");
}
void test() {
this.show(); // child method (same as show())
super.show(); // parent method
}
}
public class Main {
public static void main(String[] args) {
new Child().test();
}
}
Kết quả:
Child show()
Parent show()
Điểm quan trọng:
this.show()→ phiên bản của lớp consuper.show()→ phiên bản của lớp cha
4.6 Hàm khởi tạo: this() vs super()
Đây là một trong những phần gây nhầm lẫn nhất cho người mới bắt đầu, nhưng rất quan trọng.
this()gọi một hàm khởi tạo khác trong cùng lớpsuper()gọi một hàm khởi tạo trong lớp cha
4.6.1 Ví dụ về this() (chuỗi gọi hàm khởi tạo trong cùng lớp)
class User {
String name;
int age;
User() {
this("NoName", 0); // call another constructor
}
User(String name, int age) {
this.name = name;
this.age = age;
}
}
Điều này hữu ích vì nó tránh việc lặp lại logic khởi tạo.
4.6.2 Ví dụ về super() (gọi hàm khởi tạo của lớp cha)
class Parent {
Parent() {
System.out.println("Parent constructor");
}
}
class Child extends Parent {
Child() {
super(); // initialize parent
System.out.println("Child constructor");
}
}
Kết quả:
Parent constructor
Child constructor
4.7 Bạn không thể dùng this() và super() cùng lúc
Nhiều người mới bắt đầu thử làm như sau:
Child() {
this(1);
super(); // error
}
Điều này không được phép.
Lý do:
- Cả
this()vàsuper()đều phải là dòng đầu tiên trong hàm khởi tạo - Chỉ một trong hai có thể là dòng đầu tiên
Vì vậy bạn phải chọn một trong hai.
4.8 Nhắc nhở quan trọng: super không tạo ra một đối tượng mới
.super không tạo ra một đối tượng cha riêng biệt.
Thay vào đó:
this= đối tượng hiện tại được xem như một đối tượng consuper= cùng một đối tượng được xem như một đối tượng cha
Đây là lý do tại sao super nên được nghĩ như một từ khóa tham chiếu, không phải là một trình tạo đối tượng.
4.9 Tóm tắt phần
thisđề cập đến đối tượng hiện tại (phía con)superđề cập đến phía lớp chathis()gọi một hàm khởi tạo khác trong cùng một lớpsuper()gọi một hàm khởi tạo trong lớp chathis()vàsuper()không thể được sử dụng đồng thời
Tiếp theo, chúng ta sẽ đề cập đến các lỗi thường gặp và các mẫu lỗi để bạn có thể tránh những bẫy phổ biến nhất cho người mới bắt đầu.
5. Các lỗi thường gặp và cạm bẫy (Cách tránh lỗi)
Từ khóa super của Java rất mạnh, nhưng cũng dễ bị lạm dụng—đặc biệt khi bạn còn đang học về kế thừa và các hàm khởi tạo.
Trong phần này, bạn sẽ học các lỗi phổ biến nhất mà người mới bắt đầu mắc phải và cách khắc phục nhanh chóng.
5.1 Viết super(...) ở vị trí sai (quy tắc hàm khởi tạo)
Một trong những quy tắc quan trọng nhất:
super(...)phải là câu lệnh đầu tiên bên trong một hàm khởi tạo.
❌ Sai (lỗi biên dịch):
class Parent {
Parent(int x) {}
}
class Child extends Parent {
Child() {
System.out.println("Do something first");
super(10); // ERROR: must be first line
}
}
✅ Đúng:
class Child extends Parent {
Child() {
super(10);
System.out.println("Child logic");
}
}
Tại sao?
Bởi vì phần cha của đối tượng phải được khởi tạo trước khi logic của con chạy.
5.2 Quên super(args...) khi lớp cha không có hàm khởi tạo không đối số
Nếu lớp cha chỉ có một hàm khởi tạo có tham số, Java không thể tự động chèn super().
❌ Sai:
class Parent {
Parent(String name) {}
}
class Child extends Parent {
Child() {
// compiler tries to insert super() here -> ERROR
}
}
✅ Đúng:
class Child extends Parent {
Child() {
super("Taro");
}
}
Đây là lỗi biên dịch rất phổ biến đối với người mới bắt đầu, vì vậy luôn kiểm tra các hàm khởi tạo của lớp cha.
5.3 Cố gắng sử dụng cả this() và super() trong cùng một hàm khởi tạo
Người mới thường nghĩ:
“Tôi muốn gọi một hàm khởi tạo khác VÀ hàm khởi tạo của lớp cha.”
Nhưng Java không cho phép điều này.
❌ Sai:
class Parent {
Parent() {}
}
class Child extends Parent {
Child() {
this(1);
super(); // ERROR
}
Child(int x) {}
}
Lý do:
this()vàsuper()phải là dòng đầu tiên- Chỉ một trong hai có thể là dòng đầu tiên
Vì vậy bạn phải thiết kế các hàm khởi tạo sao cho chỉ sử dụng một trong chúng trực tiếp.
5.4 Cố gắng sử dụng super trong một phương thức tĩnh
super gắn liền với thể hiện hiện tại của đối tượng, vì vậy nó không hoạt động trong các phương thức tĩnh.
❌ Sai (về mặt khái niệm):
class Parent {
void hello() {}
}
class Child extends Parent {
static void test() {
super.hello(); // ERROR
}
}
Điểm quan trọng:
- Phương thức tĩnh thuộc về lớp, không phải một thể hiện
superđề cập đến phần cha của một thể hiện
Do đó Java ngăn chặn việc này.
5.5 Hiểu sai super là “tạo một đối tượng cha”
Một số người mới cho rằng:
“Sử dụng
supertạo ra một đối tượng cha riêng biệt.”
Điều đó không đúng.
super không tạo ra một đối tượng mới.
Nó chỉ truy cập phía lớp cha của cùng một đối tượng.
Hãy nghĩ như sau:
this= cùng một đối tượng, góc nhìn của consuper= cùng một đối tượng, góc nhìn của cha
5.6 Sử dụng super.field quá thường xuyên (ẩn trường thường là ý tưởng tồi)
Đúng, super.field tồn tại—nhưng trong hầu hết các dự án thực tế, bạn nên tránh tạo các trường cha/con cùng tên.
Ví dụ (thiết kế gây nhầm lẫn):
class Parent {
int value = 100;
}
class Child extends Parent {
int value = 200; // hides parent field
}
Mặc dù super.value hoạt động, nhưng thiết kế này làm cho mã khó đọc và bảo trì hơn.
Thực hành tốt hơn:
.* Sử dụng các tên trường khác nhau * Giữ các trường ở mức private và cung cấp chúng qua các phương thức (getter) * Tránh ẩn các trường trừ khi thực sự cần thiết
5.7 “Nếu tôi gọi super.method(), chỉ logic của lớp cha chạy” (không phải lúc nào cũng vậy)
Đây là một chút nâng cao hơn, nhưng hữu ích để biết.
Khi bạn gọi super.method(), bạn chắc chắn gọi phương thức của lớp cha.
Tuy nhiên, bên trong phương thức cha đó, nếu nó gọi một phương thức khác mà lớp con đã ghi đè, phiên bản của lớp con có thể được thực thi.
Ví dụ:
class Parent {
void a() {
b(); // calls b()
}
void b() {
System.out.println("Parent b()");
}
}
class Child extends Parent {
@Override
void b() {
System.out.println("Child b()");
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.a();
}
}
Kết quả:
Child b()
Mặc dù a() nằm trong lớp cha, nó gọi b(), và b() đã được ghi đè trong lớp con.
Bài học cho người mới:
super.method() gọi phiên bản của lớp cha cho phương thức đó, nhưng các lời gọi phương thức bên trong nó vẫn có thể tuân theo quy tắc ghi đè.
5.8 Danh sách kiểm tra nhanh (để tránh lỗi người mới)
Trước khi bạn sử dụng super, hãy kiểm tra các mục sau:
super(...)có phải là dòng đầu tiên trong constructor không?- Lớp cha có constructor không có tham số không?
- Bạn có đang cố gắng sử dụng cả
this()vàsuper()không? - Bạn có đang sử dụng
supertrong một phương thức static không? - Bạn có đang ẩn các trường có cùng tên một cách không cần thiết không?
6. Kết luận (Những điểm chính)
Trong bài viết này, bạn đã học được super trong Java là gì, tại sao nó tồn tại, và cách sử dụng nó một cách đúng đắn trong mã thực tế.
Hãy tổng kết lại tất cả các điểm quan trọng nhất mà bạn nên nhớ.
6.1 super có nghĩa là gì trong Java?
super là một từ khóa cho phép bạn truy cập một cách rõ ràng phần lớp cha (superclass) của một đối tượng.
Nó chủ yếu được sử dụng trong kế thừa (extends) khi bạn muốn tham chiếu đến phiên bản của lớp cha cho:
- các hàm khởi tạo
- các phương thức
- các trường
Trong cách diễn đạt đơn giản:
supercó nghĩa là “sử dụng phiên bản của lớp cha.”
6.2 Ba cách quan trọng nhất để sử dụng super
Bạn có thể ghi nhớ super qua ba mẫu sau:
1) Gọi hàm khởi tạo của lớp cha
super()super(args...)
Điều này được dùng khi bạn cần khởi tạo lớp cha một cách đúng đắn, đặc biệt nếu lớp cha yêu cầu các đối số.
2) Gọi phương thức của lớp cha
super.method()
Điều này hữu ích khi bạn ghi đè một phương thức nhưng vẫn muốn tái sử dụng hành vi của lớp cha.
3) Truy cập trường của lớp cha
super.field
Điều này được dùng khi cả lớp cha và lớp con đều có một trường cùng tên và bạn muốn chỉ rõ trường của lớp cha.
6.3 this vs super (cách dễ nhất để hiểu)
Nhiều người mới bắt đầu nhầm lẫn hai từ khóa này, nhưng sự khác nhau thực sự rất đơn giản:
this→ đối tượng hiện tại (cái nhìn từ phía lớp con)super→ cái nhìn từ phía lớp cha của cùng một đối tượng
Chúng không đại diện cho hai đối tượng khác nhau.
6.4 Hầu hết các lỗi xảy ra quanh hàm khởi tạo
Nếu bạn muốn tránh những sai lầm phổ biến nhất, hãy nhớ các quy tắc sau:
super(...)phải là dòng đầu tiên trong một constructor- Nếu lớp cha không có constructor không tham số, bạn phải viết
super(args...) - Bạn không thể sử dụng
this()vàsuper()cùng nhau trong cùng một constructor superkhông hoạt động trong các phương thức static
6.5 Kết luận cuối cùng: super giúp bạn tái sử dụng và mở rộng logic của lớp cha một cách an toàn
Sức mạnh thực sự của super là nó cho phép bạn:
- giữ logic chung trong lớp cha
- mở rộng hành vi trong lớp con
- tránh sao chép mã
- làm cho kế thừa sạch sẽ và dễ bảo trì hơn
Nếu bạn chỉ nhớ một câu từ toàn bộ bài viết này, hãy nhớ:
superlà từ khóa bạn dùng khi muốn phiên bản của lớp cha.
7. Câu hỏi thường gặp (FAQ)
Đây là những câu hỏi phổ biến nhất mà người mới bắt đầu hỏi khi học về từ khóa super trong Java.
Nếu bạn vẫn còn cảm thấy không chắc chắn về một số phần, phần này sẽ giúp làm rõ nhanh chóng.
7.1 Khi nào tôi nên sử dụng super trong Java?
Bạn thường sử dụng super trong những tình huống sau:
- Khi bạn muốn gọi constructor của lớp cha (
super()/super(args...)) - Khi bạn ghi đè một phương thức nhưng vẫn muốn gọi phương thức của lớp cha (
super.method()) - Khi lớp cha và lớp con có cùng tên trường và bạn muốn trường của lớp cha (
super.field)
Đối với người mới bắt đầu, trường hợp phổ biến và hữu ích nhất là:
ghi đè một phương thức và gọi logic của lớp cha bằng
super.method().
7.2 Điều gì xảy ra nếu tôi không viết super()?
Nếu lớp cha có constructor không tham số, Java sẽ tự động chèn:
super();
vào đầu constructor của lớp con.
Vì vậy, trong nhiều trường hợp, bạn không cần viết nó thủ công.
Tuy nhiên, nếu lớp cha không có constructor không tham số, mã sẽ thất bại trừ khi bạn gọi rõ ràng constructor đúng với các tham số.
7.3 Sự khác biệt giữa super() và this() là gì?
Chúng gọi các constructor khác nhau:
super()→ gọi constructor trong lớp chathis()→ gọi constructor trong cùng lớp
Ý tưởng ví dụ:
- Sử dụng
this()để giảm mã constructor bị trùng lặp - Sử dụng
super()để khởi tạo đúng phần lớp cha của đối tượng
7.4 Tôi có thể sử dụng cả this() và super() trong cùng một constructor không?
Không, bạn không thể.
Lý do:
- Cả
this()vàsuper()phải là câu lệnh đầu tiên trong constructor - Chỉ có một câu lệnh có thể là đầu tiên
Vì vậy, Java buộc bạn phải chọn một trong hai.
7.5 Tôi có thể sử dụng super bên trong phương thức static không?
Không (nói chung).
super liên quan đến instance đối tượng hiện tại, nhưng các phương thức static thuộc về chính lớp và không có tham chiếu instance.
Đó là lý do tại sao sử dụng super trong phương thức static dẫn đến lỗi biên dịch.
7.6 super có tạo ra một đối tượng lớp cha mới không?
Không.
super không tạo ra bất kỳ đối tượng mới nào.
Nó chỉ truy cập góc nhìn của lớp cha đối với cùng một đối tượng.
Một cách suy nghĩ hữu ích:
this= cùng một đối tượng được xem như lớp consuper= cùng một đối tượng được xem như lớp cha
7.7 Nếu tôi gọi super.method(), nó có luôn chỉ chạy logic của lớp cha không?
super.method() luôn gọi phiên bản phương thức của lớp cha.
Tuy nhiên, nếu phương thức lớp cha nội bộ gọi một phương thức khác mà lớp con ghi đè, thì phiên bản lớp con có thể chạy do quy tắc ghi đè của Java.
Bài học cho người mới bắt đầu:
super.method()gọi phiên bản của lớp cha, nhưng các lời gọi phương thức bên trong nó vẫn có thể bị ảnh hưởng bởi ghi đè.
7.8 Tôi có nên sử dụng super.field thường xuyên không?
Thường thì không.
Nếu bạn thường xuyên cần super.field, có thể thiết kế của bạn đang gây nhầm lẫn vì lớp cha và lớp con chia sẻ cùng tên trường.
Trong các dự án thực tế, tốt hơn là tránh ẩn trường trừ khi bạn có lý do mạnh mẽ (như mã legacy hoặc ràng buộc bên ngoài).
7.9 super có thể được sử dụng với interfaces không?
Trong kế thừa thông thường (extends), super đề cập đến lớp cha.
Tuy nhiên, Java cũng cho phép cú pháp đặc biệt để gọi phương thức mặc định của interface:
InterfaceName.super.method();
Điều này nâng cao hơn, vì vậy nếu bạn là người mới bắt đầu, hãy tập trung trước vào:
- kế thừa lớp
super()/super.method()/super.field
7.10 Cách nhanh nhất để làm chủ super là gì?
Một lộ trình học đơn giản:
- Viết một lớp cha/con cơ bản và xác nhận thứ tự constructor
- Ghi đè một phương thức và gọi
super.method() - So sánh
thisvàsupervới trường và phương thức - Thực hành sửa lỗi liên quan đến constructor
Thực hành tốt nhất là viết các ví dụ nhỏ và chạy chúng.

