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

Posted

技术标签:

【中文标题】如何从 Java 中的静态方法调用 getClass()?【英文标题】:How to call getClass() from a static method in Java? 【发布时间】: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 将在那个阶段使用引导类加载器,它不会在(引导加载器的)类路径上拥有应用程序资源。 【参考方案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() 略有不同;见***.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,所以你不能在静态代码块中调用它。有关更多信息,请参阅这些答案:Q1、Q2、Q3。

如果你在静态上下文中,那么你必须使用类文字表达式来获取 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 文件夹,另一个用于加载应用程序库和资源),这是不正确的。出于这个原因,这个答案的解决方案有时只是巧合!

以上是关于如何从 Java 中的静态方法调用 getClass()?的主要内容,如果未能解决你的问题,请参考以下文章

QT creator 如何调用VC写的静态库

Java面试题14 super.getClass()方法调用

静态方法 - 如何从另一个方法调用一个方法?

java调用同一个类中的方法为啥要将方法申明成静态?

在java中调用非静态方法之前创建一个实例

java静态资源(静态方法,静态属性)是程序一运行就加载到jvm中,还是当被调用的时候才进行加载呢?