【问题标题】:How to call getClass() from a static method in Java?如何从 Java 中的静态方法调用 getClass()?
【发布时间】:2012-01-06 17:04:13
【问题描述】:

我有一个必须有一些静态方法的类。在这些静态方法中,我需要调用方法 getClass() 来进行以下调用:

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

然而 Eclipse 告诉我:

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

修复此编译时错误的适当方法是什么?

【问题讨论】:

  • 在用户定义(例如非 J2SE)类的实例之前使用 getResource() 有时会失败。问题是 JRE 将在那个阶段使用引导类加载器,它不会在(引导加载器的)类路径上拥有应用程序资源。

标签: java static-methods


【解决方案1】:

答案

只需使用TheClassName.class 而不是getClass()

声明记录器

由于这在特定用例中引起了如此多的关注——提供一种插入日志声明的简单方法——我想我会在这方面添加我的想法。日志框架通常期望日志被限制在特定的上下文中,比如一个完全限定的类名。因此,如果不进行修改,它们就不能复制粘贴。其他答案中提供了有关粘贴安全日志声明的建议,但它们有缺点,例如膨胀字节码或添加运行时自省。我不推荐这些。复制粘贴是编辑器的问题,因此编辑器解决方案是最合适的。

在 IntelliJ 中,我建议添加一个实时模板:

  • 使用“log”作为缩写
  • 使用private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class); 作为模板文本。
  • 单击编辑变量并使用表达式className() 添加类
  • 选中复选框以重新格式化和缩短 FQ 名称。
  • 将上下文更改为 Java:声明。

现在如果你输入log<tab>,它会自动展开为

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

并自动为您重新格式化和优化导入。

【讨论】:

  • 当不正确的代码在不同的类中运行时,这将导致复制/粘贴和悲伤的结果。
  • @WilliamEntriken:使用匿名内部类并不是一个很好的解决方案,它只是用额外的类文件使你的字节码膨胀,以节省几秒钟的开发人员工作量。我不确定人们在必须在整个地方复制/粘贴的地方都在做什么,但应该使用编辑器解决方案而不是语言破解来处理它。例如如果使用 IntelliJ,请使用可以插入类名的实时模板。
  • “我不确定人们在到处复制/粘贴的地方做什么”——例如在 Android 中使用 Log,通常需要提供标签类名。可能还有其他例子。当然,您可以为此声明一个字段(这是常见做法),但这仍然是复制和粘贴。
  • @user149408:是的,这已经被讨论了很多。尽管此用例与原始问题实际上无关,但这是人们访问此问题的常见原因,因此我在答案中添加了一些想法。
  • 您在实时模板解决方案中忘记了一件事:单击“编辑变量”并在CLASS 的表达式中输入className()。这将确保 $CLASS$ 实际上被替换为类名,否则它将为空。
【解决方案2】:

对于问题中的代码示例,标准的解决方案是通过名称显式引用该类,甚至可以不使用getClassLoader()调用:

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

这种方法仍有一个缺点,即如果您需要将此代码复制到许多类似的类中,它对复制/粘贴错误不是很安全。

而至于标题中的确切问题,adjacent thread 贴出了一个技巧:

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

它使用嵌套的匿名Object 子类来获取执行上下文。这个技巧的好处是复制/粘贴安全......

在其他类继承自的基类中使用 this 时的注意事项:

同样值得注意的是,如果这个 sn-p 是某个基类的静态方法,那么currentClass 值将始终是对该基类的引用,而不是对可能使用该方法的任何子类的引用。

【讨论】:

  • 到目前为止我最喜欢的答案。 NameOfClass.class 很明显,但这需要您知道您的班级名称。 getEnclosureClass 技巧不仅对复制粘贴有用,对使用自省的静态基类方法也很有用
  • 顺便提一下,Class.getResource() 的行为可能与 ClassLoader.getResource() 略有不同;见stackoverflow.com/a/676273
【解决方案3】:

在 Java7+ 中,您可以在静态方法/字段中执行此操作:

MethodHandles.lookup().lookupClass()

【讨论】:

  • 如果您只需要 getSimpleName() 调用即可从 main 方法打印帮助,这是一个不错的解决方案。
  • 我正在寻找一种“复制粘贴”解决方案,用于在每个位置添加记录器,而无需每次都更改类名。你给了我:private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
  • 支持它的最佳解决方案,缺点是Android直到API 26,即Android 8才支持它。
【解决方案4】:

我自己也在为此苦苦挣扎。一个不错的技巧是在静态上下文中使用当前线程来获取 ClassLoader。这也适用于 Hadoop MapReduce。其他方法在本地运行时有效,但在 MapReduce 中使用时返回 null InputStream。

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

【讨论】:

    【解决方案5】:

    只需使用类文字,即NameOfClass.class

    【讨论】:

      【解决方案6】:

      试试看

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

      或者

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

      【讨论】:

      • 这种方法的美妙之处在于它也适用于 Android 8 (API 26) 之前的版本。请注意索引[1] 将导致所有日志消息将Thread 报告为其在Android 4.4.4 上的来源。 [2] 似乎在 Android 和 OpenJRE 上都给出了正确的结果。
      【解决方案7】:

      getClass() 方法在 Object 类中定义,签名如下:

      公共最终类 getClass()

      因为它没有被定义为static,所以你不能在静态代码块中调用它。有关更多信息,请参阅这些答案:Q1Q2Q3

      如果你在静态上下文中,那么你必须使用类文字表达式来获取 Class,所以你基本上必须这样做:

      Foo.class

      这种类型的表达式称为Class Literals,它们在Java Language Specification Book中解释如下:

      类字面量是由类、接口、数组或原始类型的名称后跟一个“.”组成的表达式。和令牌类。类文字的类型是 Class。它评估为由当前实例的类的定义类加载器定义的命名类型(或 void)的 Class 对象。

      您还可以在 API documentation 上找到有关此主题的信息,以获取 Class。

      【讨论】:

        【解决方案8】:

        我遇到了同样的问题! 但要解决它只需修改您的代码如下。

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

        这对我来说很好,希望它对你也很好。

        【讨论】:

        • 什么是“YourClassName”?应该是'startMusic'?无论如何,为了避免指定类名,我们不能只使用'this.class.getClassLoader()'吗?
        【解决方案9】:

        假设有一个 Utility 类,那么示例代码将是 -

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

        CustomLocation - 如果资源中的任何文件夹结构,否则删除此字符串文字。

        【讨论】:

          【解决方案10】:

          试试这样的。这个对我有用。 Logg(类名)

              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");
          

          【讨论】:

          • 不知道为什么您要提供与此线程中已有的相同答案三次:2011 年两次,2013 年一次。
          • 如果类 Logg 由具有“resources\\config”文件夹访问权限的类加载器加载,则此解决方案有效。在某些具有多个类加载器的服务器上(例如,一个类加载器用于加载 lib 文件夹,另一个用于加载应用程序库和资源),这是不正确的。出于这个原因,这个答案的解决方案有时只是巧合!
          猜你喜欢
          • 1970-01-01
          • 2011-11-14
          • 2013-12-19
          • 1970-01-01
          • 1970-01-01
          • 2014-07-28
          • 2020-12-17
          • 1970-01-01
          相关资源
          最近更新 更多