ChatGPT解决这个技术问题 Extra ChatGPT

How to call getClass() from a static method in Java?

I have a class that must have some static methods. Inside these static methods I need to call the method getClass() to make the following call:

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

However Eclipse tells me:

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

What is the appropriate way to fix this compile time error?

Using getResource() before there is an instance of a user defined (e.g. non-J2SE) class will sometimes fail. The problem is that the JRE will be using the bootstrap class-loader at that stage, which will not have application resources on the class-path (of the bootstrap loader).

M
Mark Peters

The Answer

Just use TheClassName.class instead of getClass().

Declaring Loggers

Since this gets so much attention for a specific usecase--to provide an easy way to insert log declarations--I thought I'd add my thoughts on that. Log frameworks often expect the log to be constrained to a certain context, say a fully-qualified class name. So they are not copy-pastable without modification. Suggestions for paste-safe log declarations are provided in other answers, but they have downsides such as inflating bytecode or adding runtime introspection. I don't recommend these. Copy-paste is an editor concern, so an editor solution is most appropriate.

In IntelliJ, I recommend adding a Live Template:

Use "log" as the abbreviation

Use private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class); as the template text.

Click Edit Variables and add CLASS using the expression className()

Check the boxes to reformat and shorten FQ names.

Change the context to Java: declaration.

Now if you type log<tab> it'll automatically expand to

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

And automatically reformat and optimize the imports for you.


This will result in a copy/paste and sad sad results when the incorrect code is running in a different class.
@WilliamEntriken: Using anonymous inner classes isn't a great solution to that, it just bloats your bytecode with extra classfiles to save a couple seconds of developer effort. I'm not sure what people are doing where they have to copy/paste all over the place, but it should be treated with an editor solution, not a language hack. e.g. if using IntelliJ, use a Live Template which can insert the classname.
“I'm not sure what people are doing where they have to copy/paste all over the place”—something such as using Log in Android, which requires a tag to be supplied, typically the class name. And there are probably other examples. Of course you can declare a field for that (which is common practice), but that is still copy and paste.
@user149408: Yes this has been talked about a lot. Though this usecase had really nothing to do with the original question, it's a common reason people visit this question so I've added some thoughts to the answer.
You forgot one thing in your Live Template solution: Click "Edit variables" and in the Expression for CLASS type in className(). That will make sure that $CLASS$ is actually replaced with the class name, otherwise it will just come out empty.
J
Joshua Pinter

As for the code example in the question, the standard solution is to reference the class explicitly by its name, and it is even possible to do without getClassLoader() call:

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

This approach still has a back side that it is not very safe against copy/paste errors in case you need to replicate this code to a number of similar classes.

And as for the exact question in the headline, there is a trick posted in the adjacent thread:

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

It uses a nested anonymous Object subclass to get hold of the execution context. This trick has a benefit of being copy/paste safe...

Caution when using this in a Base Class that other classes inherit from:

It is also worth noting that if this snippet is shaped as a static method of some base class then currentClass value will always be a reference to that base class rather than to any subclass that may be using that method.


By far my favorite answer. NameOfClass.class is obvious, but that requires you to know your class's name. The getEnclosingClass trick is not just useful for copy-pasting, also useful for static base class methods that make use of introspection
Incidentally Class.getResource() may behave slightly differently from ClassLoader.getResource(); see stackoverflow.com/a/676273
R
Rein

In Java7+ you can do this in static methods/fields:

MethodHandles.lookup().lookupClass()

Nice solution if you only need getSimpleName() call to print help from main method.
I was looking for a 'copy paste' solution for adding logger every where, without changing the class name each time. You gave it to me : private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
The best solution where it’s supported, drawback being that Android does not support this until API 26, i.e. Android 8.
s
starkadder

I wrestled with this myself. A nice trick is to use use the current thread to get a ClassLoader when in a static context. This will work in a Hadoop MapReduce as well. Other methods work when running locally, but return a null InputStream when used in a MapReduce.

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

M
Michael Borgwardt

Simply use a class literal, i.e. NameOfClass.class


I
Ishfaq

Try it

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

Or

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

The beauty of this approach is that it will also work on Android versions prior to 8 (API 26). Beware that index [1] will cause all log messages to report Thread as their source on Android 4.4.4. [2] seems to give the correct result on both Android and OpenJRE.
C
Community

getClass() method is defined in Object class with the following signature:

public final Class getClass()

Since it is not defined as static, you can not call it within a static code block. See these answers for more information: Q1, Q2, Q3.

If you're in a static context, then you have to use the class literal expression to get the Class, so you basically have to do like:

Foo.class

This type of expression is called Class Literals and they are explained in Java Language Specification Book as follows:

A class literal is an expression consisting of the name of a class, interface, array, or primitive type followed by a `.' and the token class. The type of a class literal is Class. It evaluates to the Class object for the named type (or for void) as defined by the defining class loader of the class of the current instance.

You can also find information about this subject on API documentation for Class.


u
user14203853

I had the same problem ! but to solve it just modify your code as following.

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

this worked fine with me hope it will also work fine with you.


what is "YourClassName"? should be 'startMusic"? Anyway to avoid specifying class name, can't we use just 'this.class.getClassLoader()'?
P
Pravind Kumar

Suppose there is a Utility class, then sample code would be -

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

CustomLocation - if any folder structure within resources otherwise remove this string literal.


O
Oliver Lagerbäck Westblom

Try something like this. It works for me. Logg (Class name)

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

Not sure why you're providing the same answer that's already in this thread three times: Twice from 2011, once from 2013.
This solution works if the class Logg is loadded by the classloader with access to "resources\\config" folder. On some servers that have multiple classloaders (by example one classloader to load lib folder and other to load app libs and resources), that is not true. For that reason, the solution poupose on this answer only works sometimes by coincidence!