Với các lớp ẩn danh, bạn thực sự đang khai báo một lớp lồng "không tên". Đối với các lớp lồng nhau, trình biên dịch tạo ra một lớp công khai độc lập mới với hàm tạo sẽ lấy tất cả các biến mà nó sử dụng làm đối số (đối với các lớp lồng nhau "có tên", đây luôn là một thể hiện của lớp gốc / lớp kèm theo). Điều này được thực hiện bởi vì môi trường thời gian chạy không có khái niệm về các lớp lồng nhau, do đó cần phải có một chuyển đổi (tự động) từ một lớp lồng vào một lớp độc lập.
Lấy mã này làm ví dụ:
public class EnclosingClass {
public void someMethod() {
String shared = "hello";
new Thread() {
public void run() {
// this is not valid, won't compile
System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
Điều đó sẽ không hoạt động, bởi vì đây là những gì trình biên dịch thực hiện dưới mui xe:
public void someMethod() {
String shared = "hello";
new EnclosingClass$1(shared).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
Lớp ẩn danh ban đầu được thay thế bằng một số lớp độc lập mà trình biên dịch tạo ra (mã không chính xác, nhưng sẽ cho bạn một ý tưởng hay):
public class EnclosingClass$1 extends Thread {
String shared;
public EnclosingClass$1(String shared) {
this.shared = shared;
}
public void run() {
System.out.println(shared);
}
}
Như bạn có thể thấy, lớp độc lập giữ một tham chiếu đến đối tượng được chia sẻ, hãy nhớ rằng mọi thứ trong java là giá trị truyền qua, vì vậy ngay cả khi biến tham chiếu 'được chia sẻ' trong EnclosesClass bị thay đổi, thì thể hiện của nó không bị sửa đổi và tất cả các biến tham chiếu khác trỏ đến nó (như biến trong lớp ẩn danh: Đóng $ 1), sẽ không nhận thức được điều này. Đây là lý do chính mà trình biên dịch buộc bạn phải khai báo các biến 'được chia sẻ' này là cuối cùng, để loại hành vi này sẽ không biến nó thành mã đang chạy của bạn.
Bây giờ, đây là những gì xảy ra khi bạn sử dụng một biến đối tượng bên trong một lớp ẩn danh (đây là điều bạn nên làm để giải quyết vấn đề của mình, chuyển logic của bạn sang một phương thức "thể hiện" hoặc một hàm tạo của một lớp):
public class EnclosingClass {
String shared = "hello";
public void someMethod() {
new Thread() {
public void run() {
System.out.println(shared); // this is perfectly valid
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
Điều này biên dịch tốt, bởi vì trình biên dịch sẽ sửa đổi mã, do đó, lớp được tạo mới Bao gồm $ 1 sẽ giữ một tham chiếu đến thể hiện của EnclosesClass nơi nó được khởi tạo (đây chỉ là một đại diện, nhưng sẽ giúp bạn đi):
public void someMethod() {
new EnclosingClass$1(this).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
public class EnclosingClass$1 extends Thread {
EnclosingClass enclosing;
public EnclosingClass$1(EnclosingClass enclosing) {
this.enclosing = enclosing;
}
public void run() {
System.out.println(enclosing.shared);
}
}
Giống như thế này, khi biến tham chiếu 'được chia sẻ' trong EnclosesClass được gán lại và điều này xảy ra trước khi gọi đến Thread # run (), bạn sẽ thấy "lời chào khác" được in hai lần, vì bây giờ biến EnclosesClass $ 1 # sẽ giữ tham chiếu đối tượng của lớp nơi nó được khai báo, vì vậy những thay đổi đối với bất kỳ thuộc tính nào trên đối tượng đó sẽ hiển thị với các thể hiện của EnclosesClass $ 1.
Để biết thêm thông tin về chủ đề này, bạn có thể xem bài đăng blog xuất sắc này (không phải do tôi viết): http://kevinboone.net/java_inner.html