2010-05-27 50 views
5

xem xét bạn có lớp sauLớp thành viên tĩnh - khai báo lớp riêng tư và lớp thành viên riêng tư?

public class OuterClass { 
    ... 

    private static class InnerClass { 
     int foo; 
     int bar; 
    } 
} 

Tôi nghĩ rằng tôi đã đọc ở đâu đó (nhưng không phải là chính thức Java Tutorial) rằng nếu tôi sẽ khai báo các lớp học viên tĩnh thuộc tính riêng, trình biên dịch phải tạo ra một số loại các phương thức accessor để lớp bên ngoài thực sự có thể truy cập các thuộc tính của lớp thành viên tĩnh (có hiệu quả là một lớp cấp cao nhất của gói riêng).

Mọi ý tưởng về điều đó?

Trả lời

4

Đúng vậy. Ít nhất là cho Sun javac. Hãy nhìn vào ví dụ sau:

public class OuterClass { 

    public static void main(String... args) { 
     InnerClass.foo = 7; 
     System.out.println(InnerClass.foo); 
    } 

    private static class InnerClass { 
     private static int foo; 
     private static int bar; 
    } 
} 

$ javap -c OuterClass\$InnerClass 
Compiled from "OuterClass.java" 
class OuterClass$InnerClass extends java.lang.Object{ 
static int access$002(int); 
    Code: 
    0: iload_0 
    1: dup 
    2: putstatiC#1; //Field foo:I 
    5: ireturn 

static int access$000(); 
    Code: 
    0: getstatiC#1; //Field foo:I 
    3: ireturn 

} 

Nó định nghĩa một static int access$002(int) cho thiết lập các giá trị, và một static int access$000() để nhận được giá trị. Setter cũng trả về giá trị, có lẽ để dễ dàng biên dịch someVariable = InnerClass.foo = 5.


$ javap -c OuterClass 
Compiled from "OuterClass.java" 
public class OuterClass extends java.lang.Object{ 
public OuterClass(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public static void main(java.lang.String[]); 
    Code: 
    0: bipush 7 
    2: invokestatiC#2; //Method OuterClass$InnerClass.access$002:(I)I 
    5: pop 
    6: getstatiC#3; //Field java/lang/System.out:Ljava/io/PrintStream; 
    9: invokestatiC#4; //Method OuterClass$InnerClass.access$000:()I 
    12: invokevirtual #5; //Method java/io/PrintStream.println:(I)V 
    15: return 

} 

Tại dòng 2 và tại dòng 9 nó gọi setter (access$002) và getter (access$000) tương ứng.


Cũng lưu ý rằng nó chỉ giới thiệu các phương thức truy cập này nếu cần. Ví dụ, trường bar không bao giờ được truy cập từ bên ngoài lớp, do đó trình biên dịch chỉ tạo ra trình lấy/đặt cho trường foo.

+0

+1 Câu trả lời hay. Vì vậy, bạn nên luôn luôn khai báo lớp thành viên tĩnh private/package-private/protected và để các trường thành viên là gói riêng tư? – helpermethod

+1

@Helper Method - Không - xem câu trả lời của tôi. –

+0

Bạn nên làm những gì thích hợp cho lĩnh vực được đề cập. Nếu có ý nghĩa riêng tư, hãy đặt ở chế độ riêng tư. Đừng lo lắng về vấn đề hiệu suất, v.v. cho đến khi nó quan trọng, và sau đó lược tả toàn bộ chương trình và xem nơi bạn tận dụng tối đa việc tối ưu hóa. (Nó chắc chắn sẽ không nằm trong những trường hợp như thế này.) – aioobe

3

Mọi ý tưởng về điều đó?

Câu trả lời của @ aioobe cho thấy rằng bạn đã chính xác.

Tuy nhiên, có thể không có sự khác biệt. Các cơ hội là trình biên dịch JIT sẽ nội tuyến cuộc gọi đến phương thức accessor và mã nguồn gốc kết quả sẽ giống hệt với một lần tìm nạp đơn giản. Ngay cả khi trình biên dịch JIT không làm điều đó, hình phạt hiệu suất có thể sẽ không đáng kể trong bối cảnh của một ứng dụng realworld.

Điều này có nghĩa là có không có điểm sớm "tối ưu hóa" mã bằng cách sử dụng công cụ sửa đổi truy cập cho biết điều gì đó khác với những gì bạn thực sự muốn thể hiện.

Các vấn đề liên quan