ChatGPT解决这个技术问题 Extra ChatGPT

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

我有一个必须有一些静态方法的类。在这些静态方法中,我需要调用方法 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 将在那个阶段使用引导类加载器,它不会在(引导加载器的)类路径上拥有应用程序资源。

M
Mark Peters

答案

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

声明记录器

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

在 IntelliJ 中,我建议添加一个 Live Template:

使用“日志”作为缩写

使用私有静态最终 org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);作为模板文本。

单击编辑变量并使用表达式 className() 添加 CLASS

选中复选框以重新格式化和缩短 FQ 名称。

将上下文更改为 Java: 声明。

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

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

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


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

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

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

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

至于标题中的确切问题,adjacent thread中发布了一个技巧:

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

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

在其他类继承自的基类中使用它时要小心:

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


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

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

MethodHandles.lookup().lookupClass()

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

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

M
Michael Borgwardt

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


I
Ishfaq

试试看

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

或者

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

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

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

公共最终类 getClass()

由于它未定义为 static,因此您不能在静态代码块中调用它。有关详细信息,请参阅以下答案:Q1Q2Q3

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

Foo.class

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

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

您还可以在 API documentation 上找到有关此主题的信息,以供课堂使用。


u
user14203853

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

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

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


什么是“你的班级名称”?应该是'startMusic'?无论如何,为了避免指定类名,我们不能只使用'this.class.getClassLoader()'吗?
P
Pravind Kumar

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

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

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


O
Oliver Lagerbäck Westblom

尝试这样的事情。这个对我有用。 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 文件夹,另一个用于加载应用程序库和资源),这是不正确的。出于这个原因,这个答案的解决方案有时只是巧合!