Làm thế nào để gọi getClass () từ một phương thức tĩnh trong Java?


350

Tôi có một lớp phải có một số phương thức tĩnh. Bên trong các phương thức tĩnh này, tôi cần gọi phương thức getClass () để thực hiện cuộc gọi sau:

public static void startMusic() {
  URL songPath = getClass().getClassLoader().getResource("background.midi");
}

Tuy nhiên Eclipse nói với tôi:

Cannot make a static reference to the non-static method getClass() 
from the type Object

Cách thích hợp để sửa lỗi thời gian biên dịch này là gì?


Việc sử dụng getResource()trước khi có một thể hiện của lớp do người dùng định nghĩa (ví dụ: không phải J2SE) đôi khi sẽ thất bại. Vấn đề là JRE sẽ sử dụng trình nạp lớp bootstrap ở giai đoạn đó, sẽ không có tài nguyên ứng dụng trên đường dẫn lớp (của trình tải bootstrap).
Andrew Thompson

Câu trả lời:


616

Câu trả lời

Chỉ cần sử dụng TheClassName.classthay vì getClass().

Khai báo logger

Vì điều này nhận được rất nhiều sự chú ý cho một usecase cụ thể - để cung cấp một cách dễ dàng để chèn khai báo nhật ký - tôi nghĩ tôi đã thêm suy nghĩ của mình vào đó. Các khung nhật ký thường hy vọng nhật ký sẽ bị hạn chế trong một bối cảnh nhất định, giả sử tên lớp đủ điều kiện. Vì vậy, chúng không thể sao chép mà không sửa đổi. Các đề xuất cho khai báo nhật ký an toàn dán được cung cấp trong các câu trả lời khác, nhưng chúng có nhược điểm như lạm phát mã byte hoặc thêm phần hướng nội thời gian chạy. Tôi không khuyên bạn nên những điều này. Copy-paste là một mối quan tâm của biên tập viên , vì vậy một giải pháp biên tập là phù hợp nhất.

Trong IntelliJ, tôi khuyên bạn nên thêm Mẫu trực tiếp:

  • Sử dụng "log" làm tên viết tắt
  • Sử dụng private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);làm văn bản mẫu.
  • Nhấp vào Chỉnh sửa biến và thêm LỚP bằng cách sử dụng biểu thức className()
  • Kiểm tra các hộp để định dạng lại và rút ngắn tên FQ.
  • Thay đổi bối cảnh thành Java: khai báo.

Bây giờ nếu bạn gõ log<tab>nó sẽ tự động mở rộng sang

private static final Logger logger = LoggerFactory.getLogger(ClassName.class);

Và tự động định dạng lại và tối ưu hóa nhập khẩu cho bạn.


6
Điều này sẽ dẫn đến một bản sao / dán và kết quả buồn đáng buồn khi mã không chính xác đang chạy trong một lớp khác.
William Entriken

1
@WilliamEntriken: Sử dụng các lớp bên trong ẩn danh không phải là một giải pháp tuyệt vời cho điều đó, nó chỉ làm mờ mã byte của bạn với các tệp lớp bổ sung để tiết kiệm một vài giây nỗ lực của nhà phát triển. Tôi không chắc chắn những gì mọi người đang làm ở nơi họ phải sao chép / dán ở khắp mọi nơi, nhưng nó nên được xử lý bằng một giải pháp biên tập, không phải là hack ngôn ngữ. ví dụ: nếu sử dụng IntelliJ, hãy sử dụng Mẫu trực tiếp có thể chèn tên lớp.
Mark Peters

1
Tôi thực sự tự hỏi tại sao bạn có 4 lượt tải xuống
Al-Mothafar

Tôi không chắc chắn những gì mọi người đang làm ở nơi họ phải sao chép / dán khắp nơi, đó là những thứ gì đó như sử dụng Logtrong Android, yêu cầu phải cung cấp thẻ, thường là tên lớp. Và có lẽ có những ví dụ khác. Tất nhiên bạn có thể khai báo một trường cho điều đó (đó là thông lệ), nhưng đó vẫn là sao chép và dán.
user149408

1
Bạn đã quên một điều trong giải pháp Mẫu trực tiếp của mình: Nhấp vào "Chỉnh sửa biến" và trong Biểu thức để CLASSnhập className(). Điều đó sẽ đảm bảo rằng $ Class $ thực sự được thay thế bằng tên lớp, nếu không nó sẽ bị bỏ trống.
HerbCSO

122

Đối với ví dụ mã trong câu hỏi, giải pháp chuẩn là tham chiếu lớp rõ ràng bằng tên của nó và thậm chí có thể thực hiện mà không cần getClassLoader()gọi:

class MyClass {
  public static void startMusic() {
    URL songPath = MyClass.class.getResource("background.midi");
  }
}

Cách tiếp cận này vẫn có một mặt trái là nó không an toàn trước các lỗi sao chép / dán trong trường hợp bạn cần sao chép mã này sang một số lớp tương tự.

Và đối với câu hỏi chính xác trong tiêu đề, có một mẹo được đăng trong chuỗi liền kề :

Class currentClass = new Object() { }.getClass().getEnclosingClass();

Nó sử dụng một Objectlớp con ẩn danh lồng nhau để nắm giữ bối cảnh thực thi. Thủ thuật này có lợi ích là sao chép / dán an toàn ...

Thận trọng khi sử dụng điều này trong Lớp cơ sở mà các lớp khác kế thừa từ:

Cũng cần lưu ý rằng nếu đoạn mã này được định hình là một phương thức tĩnh của một số lớp cơ sở thì currentClassgiá trị sẽ luôn là một tham chiếu đến lớp cơ sở đó thay vì bất kỳ lớp con nào có thể đang sử dụng phương thức đó.


23
Cho đến nay câu trả lời yêu thích của tôi. NameOfClass. Class là hiển nhiên, nhưng điều đó đòi hỏi bạn phải biết tên lớp của mình. Thủ thuật getEnclosesClass không chỉ hữu ích cho việc sao chép, mà còn hữu ích cho các phương thức lớp cơ sở tĩnh sử dụng tính năng hướng nội
Domingo Ignacio

1
Ngẫu nhiên Class.getResource()có thể cư xử hơi khác với ClassLoader.getResource(); xem stackoverflow.com/a/676273
augurar

68

Trong Java7 + bạn có thể làm điều này trong các phương thức / trường tĩnh:

MethodHandles.lookup().lookupClass()

Giải pháp tuyệt vời nếu bạn chỉ cần lệnh gọi getSimpleName () để in trợ giúp từ phương thức chính .
Dmitrii Bulashevich

6
Tôi đang tìm kiếm một giải pháp 'sao chép dán' để thêm logger mọi lúc, mà không thay đổi tên lớp mỗi lần. Bạn đưa nó cho tôi: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou

Giải pháp tốt nhất được hỗ trợ, nhược điểm là Android không hỗ trợ điều này cho đến API 26, tức là Android 8.
user149408

24

Tôi vật lộn với điều này bản thân mình. Một mẹo hay là sử dụng luồng hiện tại để nhận ClassLoader khi ở trong bối cảnh tĩnh. Điều này cũng sẽ hoạt động trong MapReduce của Hadoop. Các phương thức khác hoạt động khi chạy cục bộ, nhưng trả về InputStream null khi được sử dụng trong MapReduce.

public static InputStream getResource(String resource) throws Exception {
   ClassLoader cl = Thread.currentThread().getContextClassLoader();
   InputStream is = cl.getResourceAsStream(resource);
   return is;
}


11

getClass() phương thức được định nghĩa trong lớp Object với chữ ký sau:

lớp cuối cùng công khai getClass ()

Vì nó không được định nghĩa là static, bạn không thể gọi nó trong một khối mã tĩnh. Xem những câu trả lời này để biết thêm thông tin: Q1 , Q2 , Q3 .

Nếu bạn đang ở trong một bối cảnh tĩnh, thì bạn phải sử dụng biểu thức nghĩa đen của lớp để lấy Class, vì vậy về cơ bản bạn phải làm như sau:

Foo. Class

Loại biểu thức này được gọi là Văn học lớp và chúng được giải thích trong Sách đặc tả ngôn ngữ Java như sau:

Một nghĩa đen của lớp là một biểu thức bao gồm tên của một lớp, giao diện, mảng hoặc kiểu nguyên thủy theo sau là một `. ' và lớp mã thông báo. Loại của một nghĩa đen là Class. Nó ước tính cho đối tượng Class cho kiểu được đặt tên (hoặc khoảng trống) như được định nghĩa bởi trình nạp lớp định nghĩa của lớp của thể hiện hiện tại.

Bạn cũng có thể tìm thấy thông tin về chủ đề này trên tài liệu API cho Class.


9

Thử nó

Thread.currentThread().getStackTrace()[1].getClassName()

Hoặc là

Thread.currentThread().getStackTrace()[2].getClassName()

Cái hay của phương pháp này là nó cũng sẽ hoạt động trên các phiên bản Android trước 8 (API 26). Coi chừng chỉ mục đó [1]sẽ khiến tất cả các thông điệp tường trình báo cáo Threadlà nguồn của chúng trên Android 4.4.4. [2]dường như đưa ra kết quả chính xác trên cả Android và OpenJRE.
user149408

0

Giả sử có một lớp Utility, thì mã mẫu sẽ là -

    URL url = Utility.class.getClassLoader().getResource("customLocation/".concat("abc.txt"));

CustomLocation - nếu có bất kỳ cấu trúc thư mục nào trong các tài nguyên khác, hãy xóa chuỗi ký tự này.


-2

Hãy thử một cái gì đó như thế này. Nó làm việc cho tôi. Logg (Tên lớp)

    String level= "";

    Properties prop = new Properties();

    InputStream in =
            Logg.class.getResourceAsStream("resources\\config");

    if (in != null) {
        prop.load(in);
    } else {
        throw new FileNotFoundException("property file '" + in + "' not found in the classpath");
    }

    level = prop.getProperty("Level");

1
Không chắc chắn lý do tại sao bạn cung cấp cùng một câu trả lời đã có trong chủ đề này ba lần: Hai lần từ năm 2011, một lần từ năm 2013.
Antares42

Giải pháp này hoạt động nếu lớp Logg được tải bởi trình nạp lớp với quyền truy cập vào thư mục "resource \\ config". Trên một số máy chủ có nhiều trình nạp lớp (ví dụ: một trình nạp lớp để tải thư mục lib và khác để tải các tài nguyên và ứng dụng lib), điều đó không đúng. Vì lý do đó, giải pháp cho câu trả lời này đôi khi chỉ hoạt động bởi sự trùng hợp!
Manuel Romeiro
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.