2014-07-16 26 views
7

Tôi đã tìm thấy một tình huống mà chương trình java hoạt động khác sau khi đổi tên một biến. Tôi hiểu rằng đây không phải là mã mà mọi người sẽ sử dụng nhưng nếu ai đó biết điều gì đang xảy ra thì sẽ tốt hơn nếu có lời giải thích. Tôi đã thử điều này với java 1.6 trên Eclipse Kepler.Thay đổi tên biến đổi hành vi của chương trình Java

package _test; 

public class TestClass{ 
    public static void main(String...args){ 
     Object testClazz$1 = new Object(){ 
      public String toString() { 
       return "hello"; 
      } 
     }; 
     TestClass$1 test = new TestClass$1(); 
     System.out.println(testClazz$1.toString()); 
     test.doStuff(); 
    } 
} 

class TestClass$1{ 
    public void doStuff(){ 
     System.out.println("hello2"); 
    } 
} 

đầu ra này:

hello

Exception in thread "main" java.lang.NoSuchMethodError: _test.TestClass$1.doStuff()V at _test.TestClass.main(TestClass.java:13)

Theo như tôi hiểu trình biên dịch tạo ra một tập tin TestClass $ 1.class cho đối tượng testClazz $ 1 và điều này gây ra một vụ va chạm đặt tên.

Nhưng sau khi đổi tên đối tượng để TestClass $ 1:

package _test; 

public class TestClass{ 
    public static void main(String...args){ 
     Object testClass$1 = new Object(){ 
      public String toString() { 
       return "hello"; 
      } 
     }; 

     TestClass$1 test = new TestClass$1(); 
     System.out.println(testClass$1.toString()); 
     test.doStuff(); 
    } 
} 

class TestClass$1{ 
    public void doStuff(){ 
     System.out.println("hello2"); 
    } 
} 

Đầu ra là:

[email protected]

hello2

Bất kỳ ý tưởng những gì đang xảy ra ở đây?

+0

Không chắc chắn lý do tại sao điều này xảy ra nhưng cần lưu ý về cách đặt tên: Thực tế là đặt tên biến của bạn bằng ký hiệu $ vì điều này thường được dành riêng cho biến do hệ thống tạo. Tôi muốn sẵn sàng đặt cược hành vi kỳ quặc này xảy ra bởi vì bạn đang vấp phải một số TestClass được tạo ra từ hệ thống. – DanK

+0

Hệ thống tạo ra một lớp học vô danh TestClass $ 1, nó vẫn còn lạ rằng điều này đang xảy ra. – user3139461

Trả lời

0

Khi trình nạp lớp gặp lớp Object() {...} ẩn danh, nó tải nó dưới tên TestClass$1. Điều này tạo ra xung đột với class TestClass$1 {...} được xác định rõ ràng.

Tuy nhiên, xung đột tên lớp được xử lý thay vì không đúng.This bit of documentation cho chúng tôi biết rằng

If the class c has already been linked, then this method simply returns.

Đó là những gì xảy ra trong trường hợp của bạn. Bạn chỉ bao giờ tải một trong hai lớp học TestClass$1.

"Tên biến khác nhau" là không chịu trách nhiệm cho bất kỳ điều gì ngoài việc biên dịch lại và liên kết lại trong trình biên dịch của bạn. Tại thời điểm này, trình nạp lớp được tự do chọn bất kỳ một trong hai số TestClass$1 thích hơn và sử dụng ở mọi nơi.


Nếu bạn đang sử dụng một cái gì đó giống như nhật thực toàn phần (như tôi) sau đó bytecode của bạn sẽ được lưu trữ, cho đến khi một touch hoạt động mới trên các tập tin nguồn (và cập nhật timestamps ...). Đây là những gì tôi đã làm để tái tạo (chạy openjdk 1,7, Eclipse Kepler theo RedHat):

Đặt này bên trong một tập tin nguồn TestClass.java:

package javaclasses.classes; 

public class TestClass{ 
    public static void main(String...args){ 
     Object o = new Object(){ 
      public String toString() { 
       return "hello"; 
      } 
     }; 

     TestClass$1 test = new TestClass$1(); 
     System.out.println(o.toString()); 
     test.doStuff(); 
    } 
} 

class TestClass$1{ 
    public void doStuff(){ 
     System.out.println("hello2"); 
    } 
} 

ctrl + F11 kết quả đầu ra:

[email protected] 
hello2 

Mở bảng điều khiển này trong bảng điều khiển và touch TestClass.java

Trở lại trong nhật thực và ctrl + F11 tại đầu ra:

hello 
Exception in thread "main" java.lang.NoSuchMethodError: javaclasses.classes.TestClass$1.doStuff()V 
    at javaclasses.classes.TestClass.main(TestClass.java:13) 

Conlusion: Tất cả những gì có thể nói dứt khoát là ClassLoader mặc định là không đáng tin cậy cho tay giải quyết các lớp học với cùng đủ điều kiện tên. Thay đổi tên biến không quan trọng, dấu thời gian được cập nhật trên tệp nguồn của bạn.

3

Các lớp ẩn danh được đặt tên tự động bằng cách gắn thêm một dấu hiệu $ và số ngày càng tăng vào tên của lớp kèm theo.

Trong ví dụ đầu tiên của bạn, lớp anoymous sẽ có tên TestClass$1 không có phương pháp doStuff(), bạn chỉ ghi đè toString() đó là lý do bạn nhận được lỗi NoSuchMethodError.

Trong ví dụ thứ 2, bạn đã có biến cục bộ có tên TestClass$1, do đó tên được tạo tự động do trình biên dịch chọn sẽ là tên khác, rất có thể là TestClass$2. Vì bạn khởi tạo TestClass$1 không phải là lớp ẩn danh mà lớp được xác định rõ ràng bởi bạn, sẽ được khởi tạo có phương thức doStuff() in chính xác "hello2" và không ghi đè Object.toString() để in giá trị trả về theo phương pháp toString() của nó sẽ in mặc định giá trị như được chỉ định trong java.lang.Ojbect (là tên lớp được gắn thêm một dấu hiệu @ theo sau là mã băm mặc định theo định dạng thập lục phân).

Kết luận: Mặc dù đây là một ví dụ thú vị, bạn không bao giờ nên sử dụng ký hiệu $ trong tên lớp và trong tên nhận dạng.

+3

"Ký tự $ chỉ nên được sử dụng trong mã nguồn được tạo bằng máy móc hoặc hiếm khi truy cập tên đã tồn tại trên các hệ thống cũ." trích dẫn từ [đặc tả ngôn ngữ Java SE 7] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.8) – Flo

0

tôi đổi mã của bạn để loại bỏ các "$" từ tên lớp và đổi tên TestClass $ 1 tới t và thay đổi println hơi như sau:

public class TestClass{ 

    public static void main(String...args){ 
     Object t = new Object(){ 
      public String toString() { 
       return "t.toString()"; 
      } 
     }; 
     TestClass1 tc1 = new TestClass1(); 
     System.out.println(t.toString()); 
     tc1.doStuff(); 
    } 
} 

class TestClass1{ 
    public void doStuff(){ 
     System.out.println("TestClass1.doStuff()"); 
    } 
} 

Output bây giờ là:

t.toString() 
TestClass1.doStuff() 

Đây có phải là những gì bạn mong đợi?

+1

Tôi chỉ quan tâm đến cách xử lý jvm đặt tên va chạm, không có lý do lành mạnh nào khi sử dụng $ sign in class và tên biến. – user3139461

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