如何从Java中的静态方法调用getClass()?


350

我有一个必须具有一些静态方法的类。在这些静态方法中,我需要调用方法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

解决此编译时错误的适当方法是什么?


getResource()在存在用户定义的实例(例如非J2SE)类的实例之前使用有时会失败。问题在于,在此阶段,JRE将使用引导类加载器,而引导类加载器的类路径上将没有应用程序资源。
Andrew Thompson

Answers:


616

答案

只需使用TheClassName.class代替即可getClass()

声明记录器

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

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

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

现在,如果您键入,log<tab>它将自动扩展到

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

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


6
当不正确的代码在另一个类中运行时,这将导致复制/粘贴和令人悲伤的结果。
威廉·恩崔肯

1
@WilliamEntriken:使用匿名内部类并不是一个很好的解决方案,它只是使用额外的类文件来膨胀字节码,从而节省了开发人员的时间。我不确定人们在哪里必须在各处复制/粘贴内容,但是应该使用编辑器解决方案(而不是语言黑客)来解决。例如,如果使用IntelliJ,请使用可以插入类名的实时模板。
马克·彼得斯

1
我真的不知道为什么会得到4个downvotes
铝Mothafar

“我不确定人们在哪里需要在各处复制/粘贴的操作”,例如Log在Android中使用,这需要提供一个标签,通常是类名。并且可能还有其他示例。当然,您可以为此声明一个字段(这是通常的做法),但是仍然可以复制粘贴。
user149408

1
您在Live Template解决方案中忘记了一件事:单击“编辑变量”,然后在“表达式”中CLASS键入className()。这将确保$ CLASS $实际上被类名替换,否则它将变成空。
HerbCSO

122

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

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

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

至于标题中的确切问题,在相邻线程中有一个技巧:

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

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

在其他类继承的基类中使用该类时的警告:

还值得注意的是,如果此代码段的形状是某个基类的静态方法,则currentClass值将始终是对该基类的引用,而不是对可能正在使用该方法的任何子类的引用。


23
到目前为止,我最喜欢的答案。NameOfClass.class很明显,但是这要求您知道类的名称。getEnclosingClass技巧不仅对复制粘贴有用,对使用自省的静态基类方法也有用
Domingo Ignacio 2014年

1
附带的Class.getResource()行为可能与ClassLoader.getResource(); 参见stackoverflow.com/a/676273
augurar 2015年

68

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

MethodHandles.lookup().lookupClass()

如果您只需要getSimpleName()调用即可从方法打印帮助,那么这是一个不错的解决方案。
Dmitrii Bulashevich

6
我一直在寻找一种“复制粘贴”解决方案,以在每个位置添加记录器,而不必每次都更改类名。您把它给了我: private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
朱利安·费尼欧

在那里的支持最好的解决办法,退税之处在于Android不支持这一点,直到API 26,也就是Android的8
user149408

24

我自己为此搏斗。一个不错的技巧是在静态上下文中使用当前线程获取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;
}


11

getClass() 方法是在Object类中定义的,具有以下签名:

公共最终课程getClass()

由于未定义为static,因此无法在静态代码块内调用它。有关更多信息,请参见以下答案:Q1Q2Q3

如果您处于静态上下文中,则必须使用类文字表达式来获取Class,因此基本上必须这样做:

Foo类

这种类型的表达式称为类文字,在Java语言规范书中对它们进行了如下解释:

类文字是由类,接口,数组或原始类型的名称后跟“。”组成的表达式。和令牌类。类文字的类型是Class。它根据当前实例的类的定义类加载器所定义的命名类型(或为void)对Class对象求值。

您还可以在Class的API文档中找到有关此主题的信息。


9

尝试一下

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

要么

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

这种方法的优点在于,它也可以在8(API 26)之前的Android版本上使用。请注意,该索引[1]将导致所有日志消息Thread作为其在Android 4.4.4上的来源报告。[2]在Android和OpenJRE上似乎都能给出正确的结果。
user149408

0

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

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

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


-2

尝试这样的事情。这个对我有用。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");

1
不确定为什么要提供与该线程中相同的答案三遍:2011年两次,2013
。– Antares42 '16

如果类加载器可以访问“ resources \\ config”文件夹来加载Logg类,则此解决方案有效。在某些具有多个类加载器的服务器上(例如,一个类加载器可加载lib文件夹,而其他类可加载应用库和资源),这是不正确的。出于这个原因,这个答案上的解决方案有时只能巧合地起作用!
曼努埃尔·罗密罗
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.