如何从该类的静态方法中获取该类的名称。例如

public class MyClass {
    public static String getClassName() {
        String name = ????; // what goes here so the string "MyClass" is returned
        return name;
    }
}

为了把它放在上下文中,我实际上想在异常中返回类名作为消息的一部分。


当前回答

在Java 7+中,你可以在静态方法/字段中做到这一点:

MethodHandles.lookup().lookupClass()

其他回答

我在静态和非静态场景中都使用了这两种方法:

主要课程:

//For non static approach
public AndroidLogger(Object classObject) {
    mClassName = classObject.getClass().getSimpleName();
}

//For static approach
public AndroidLogger(String className) {
    mClassName = className;
}

如何提供类名:

非静态方式:

private AndroidLogger mLogger = new AndroidLogger(this);

静态方法:

private static AndroidLogger mLogger = new AndroidLogger(Myclass.class.getSimpleName());

按照@toolkit说的去做。不要做这样的事情:

return new Object() { }.getClass().getEnclosingClass();

(编辑:或者如果你使用的Java版本是在这个答案最初写出来之后才出现的,使用@Rein的答案。)

因此,当我们需要静态地获取类对象或类的完整/简单名称而不显式地使用MyClass.class语法时,我们会遇到这种情况。

在某些情况下,它真的很方便,例如kotlin高级函数的记录器实例(在这种情况下,kotlin创建了一个静态Java类,不能从kotlin代码中访问)。

我们有几个不同的变量来获得这个信息:

新对象(){}.getClass () .getEnclosingClass (); 汤姆·霍廷·塔克兰指出 getClassContext () [0] . getname ();来自SecurityManager 克里斯托弗注意到 ()新Throwable .getStackTrace () [0] .getClassName (); 路德维希伯爵 .getStackTrace Thread.currentThread () () [1] .getClassName (); 从Keksi 最后非常棒 .lookupClass MethodHandles.lookup () (); 从控制


I've prepared a benchmark for all variants and results are:
# Run complete. Total time: 00:04:18

Benchmark                                                      Mode  Cnt      Score     Error  Units
StaticClassLookup.MethodHandles_lookup_lookupClass             avgt   30      3.630 ±   0.024  ns/op
StaticClassLookup.AnonymousObject_getClass_enclosingClass      avgt   30    282.486 ±   1.980  ns/op
StaticClassLookup.SecurityManager_classContext_1               avgt   30    680.385 ±  21.665  ns/op
StaticClassLookup.Thread_currentThread_stackTrace_1_className  avgt   30  11179.460 ± 286.293  ns/op
StaticClassLookup.Throwable_stackTrace_0_className             avgt   30  10221.209 ± 176.847  ns/op

结论

最好的变种使用,相当干净和惊人的速度。 仅从Java 7和Android API 26起可用!

 MethodHandles.lookup().lookupClass();

如果你在Android或Java 6上需要这个功能,你可以使用第二好的变体。它也相当快,但是在每个使用的地方都创建了一个匿名类:(

 new Object(){}.getClass().getEnclosingClass();

如果你在很多地方都需要它,并且不希望你的字节码因为大量的匿名类而膨胀——SecurityManager是你的朋友(第三个最好的选择)。

但是你不能只调用getClassContext()——它在SecurityManager类中受到保护。你需要一些像这样的辅助类:

 // Helper class
 public final class CallerClassGetter extends SecurityManager
 {
    private static final CallerClassGetter INSTANCE = new CallerClassGetter();
    private CallerClassGetter() {}
 
    public static Class<?> getCallerClass() {
        return INSTANCE.getClassContext()[1];
    }
 }
 
 // Usage example:
 class FooBar
 {
    static final Logger LOGGER = LoggerFactory.getLogger(CallerClassGetter.getCallerClass())
 }

您可能永远不需要使用基于getStackTrace() from异常或Thread.currentThread()的最后两个变体。非常低效,只能返回String形式的类名,而不能返回class <*>实例。


P.S.

如果你想为静态kotlin utils创建一个记录器实例(就像我一样:),你可以使用这个帮助器:

import org.slf4j.Logger
import org.slf4j.LoggerFactory

// Should be inlined to get an actual class instead of the one where this helper declared
// Will work only since Java 7 and Android API 26!
@Suppress("NOTHING_TO_INLINE")
inline fun loggerFactoryStatic(): Logger
    = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass())

使用的例子:

private val LOGGER = loggerFactoryStatic()

/**
 * Returns a pseudo-random, uniformly distributed value between the
 * given least value (inclusive) and bound (exclusive).
 *
 * @param min the least value returned
 * @param max the upper bound (exclusive)
 *
 * @return the next value
 * @see java.util.concurrent.ThreadLocalRandom.nextDouble(double, double)
 */
fun Random.nextDouble(min: Double = .0, max: Double = 1.0): Double {
    if (min >= max) {
        if (min == max) return max
        LOGGER.warn("nextDouble: min $min > max $max")
        return min
    }
    return nextDouble() * (max - min) + min
}

滥用SecurityManager

System.getSecurityManager().getClassContext()[0].getName();

或者,如果没有设置,使用一个内部类来扩展它(下面的例子可耻地复制自Real的HowTo):

public static class CurrentClassGetter extends SecurityManager {
    public String getClassName() {
        return getClassContext()[1].getName(); 
    }
}

因为问题是' this.class '而不是' ClassName.class ' ?被标记为这个的重复(这是有争议的,因为这个问题是关于类而不是类名),我在这里发布答案:

class MyService {
    private static Class thisClass = MyService.class;
    // or:
    //private static Class thisClass = new Object() { }.getClass().getEnclosingClass();
    ...
    static void startService(Context context) {
        Intent i = new Intent(context, thisClass);
        context.startService(i);
    }
}

将thisClass定义为private很重要,因为: 1)它不能被继承:派生类必须要么定义自己的thisClass,要么产生错误消息 2)其他类的引用应该使用ClassName.class而不是ClassName.thisClass。

定义了thisClass后,对类名的访问变为:

thisClass.getName()