从Java中的静态方法获取类名

Mil*_*s D 232 java static

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

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

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

too*_*kit 220

为了正确支持重构(重命名类),您应该使用以下任一方法:

 MyClass.class.getName(); // full name with package
Run Code Online (Sandbox Code Playgroud)

或者(感谢@James Van Huis):

 MyClass.class.getSimpleName(); // class name and no more
Run Code Online (Sandbox Code Playgroud)

  • 如果您要对MyClass的知识进行硬编码,那么您最好只做String name ="MyClass"; ! (133认同)
  • 但是,在IDE中重构类名将无法正常工作. (110认同)
  • 如果我在子类中调用静态方法,并且我想要子类名称怎么办? (51认同)
  • 我*希望*"this"在静态上下文中工作,表示Java中的当前类,即"class.xxx"在实例或静态代码中被允许表示此类!这个问题是MyClass在上下文中是冗长和冗余的.但是,就像我喜欢Java一样,它确实似乎倾向于冗长. (19认同)
  • 真正.虽然MyClass.class将确保这一行不会被"更改类名"重构所遗忘 (9认同)
  • @EdwardFalk这正是我来到这里的原因!非常伤心,至少没有解决. (5认同)
  • 不幸的是,它似乎无法做到.实质上,静态方法不了解类层次结构. (3认同)
  • 在 PHP 中,您可以在静态方法中使用“self”(而不是“this”)。我希望 Java 也能有同样的功能。 (2认同)

Tom*_*ine 118

做什么工具包说.不要做这样的事情:

return new Object() { }.getClass().getEnclosingClass();
Run Code Online (Sandbox Code Playgroud)

  • 这似乎比SecurityManager或Throwable解决方案更糟糕...... (14认同)
  • 我不明白为什么getClass()不能是静态的.这样的"成语"就不再需要了. (8认同)
  • 如果类扩展了另一个,则不会返回实际的类,只返回基类. (7认同)
  • 这才是真正的答案.因为您不需要自己在代码中编写类的名称. (5认同)
  • 如果我明白 @LuisSoeiro 的意思,那么这个方法只有在从定义它的类调用时才有效。多态性将会被破坏。调用同一方法的子类将不会获得自己的名称,只能获得父名称。哦,“new Throwable().getStackTrace()[0].getClassName()”方法很好,我宁愿使用它,但是当子类使用它时它也会失败。 (2认同)

Rei*_*ein 86

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

MethodHandles.lookup().lookupClass()
Run Code Online (Sandbox Code Playgroud)

  • 这很容易成为最佳解决方案.它避免了指定实际类名的需要,它不是模糊的,它不是黑客,根据Artyom Krivolapov的帖子,它也是迄今为止最快的方法. (6认同)
  • @Rein 如果在基类中调用它,有没有办法获取运行时类? (2认同)

Art*_*pov 45

因此,我们有一种情况需要静态获取类对象或类完整/简单名称而不需要明确使用MyClass.class语法.

在某些情况下它可以非常方便,例如记录器实例 上层函数(在这种情况下,kotlin创建一个无法从kotlin代码访问的静态Java类).

我们有一些不同的变体来获取此信息:

  1. new Object(){}.getClass().getEnclosingClass();
    Tom Hawtin 指出- 强调

  2. getClassContext()[0].getName();SecurityManager
    所指出克里斯托弗

  3. new Throwable().getStackTrace()[0].getClassName();
    通过计数路德维希

  4. Thread.currentThread().getStackTrace()[1].getClassName();
    来自Keksi

  5. 最后
    MethodHandles.lookup().lookupClass();
    来自Rein的精彩


我已为所有变体准备了基准,结果如下:

# 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
Run Code Online (Sandbox Code Playgroud)


结论

  1. 最好的变种,相当干净,怪异的快速.
    仅在Java 7和Android API 26之后可用!
 MethodHandles.lookup().lookupClass();
Run Code Online (Sandbox Code Playgroud)
  1. 如果您需要Android或Java 6的此功能,您可以使用第二个最佳变体.它也相当快,但在每个使用地点创建一个匿名类 :(
 new Object(){}.getClass().getEnclosingClass();
Run Code Online (Sandbox Code Playgroud)
  1. 如果你在很多地方需要它并且不希望你的字节码由于大量的匿名类而膨胀 - 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())
 }
Run Code Online (Sandbox Code Playgroud)
  1. 您可能根本不需要使用基于getStackTrace()from例外的最后两个变体Thread.currentThread().非常低效,只能返回类名String,而不是Class<*>实例.


PS

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

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())
Run Code Online (Sandbox Code Playgroud)

用法示例:

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
 * @throws IllegalArgumentException if least greater than or equal to bound
 * @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
}
Run Code Online (Sandbox Code Playgroud)


小智 38

这条指令工作正常:

Thread.currentThread().getStackTrace()[1].getClassName();
Run Code Online (Sandbox Code Playgroud)

  • 小心,它可能真的很慢.但是你可以复制粘贴它. (5认同)
  • @ErelSegalHalevi它在后台创建了很多[StackTraceElement](http://docs.oracle.com/javase/7/docs/api/java/lang/StackTraceElement.html):( (5认同)
  • 如果你看一下`Thread.getStackTrace()`的源代码,你会看到它除了`return(new Exception())之外什么都没做.getStackTrace();`在`currentThread上调用` )`.所以@count ludwig的解决方案是实现同样目标的更直接的方法. (4认同)

小智 34

你可以像这样使用JNI做一些非常甜蜜的事情:

MyObject.java:

public class MyObject
{
    static
    {
        System.loadLibrary( "classname" );
    }

    public static native String getClassName();

    public static void main( String[] args )
    {
        System.out.println( getClassName() );
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

javac MyObject.java
javah -jni MyObject
Run Code Online (Sandbox Code Playgroud)

然后:

MyObject.c:

#include "MyObject.h"

JNIEXPORT jstring JNICALL Java_MyObject_getClassName( JNIEnv *env, jclass cls )
{
    jclass javaLangClass = (*env)->FindClass( env, "java/lang/Class" );
    jmethodID getName = (*env)->GetMethodID( env, javaLangClass, "getName",
        "()Ljava/lang/String;" );
    return (*env)->CallObjectMethod( env, cls, getName );
}
Run Code Online (Sandbox Code Playgroud)

然后将C编译成一个名为的共享库libclassname.so并运行java!

*轻笑

  • @Renate,所以整个答案实际上是一个笑话?如果是这样,请说清楚,因为你知道,我们应该帮助其他人,所以我们不要把无辜者推入陷阱。 (2认同)

小智 20

我使用它来初始化我的类(或注释)顶部的Log4j Logger.

PRO:Throwable已经加载,您可以通过不使用"IO heavy"SecurityManager来节省资源.

CON:关于这是否适用于所有JVM的一些问题.

// Log4j . Logger --- Get class name in static context by creating an anonymous Throwable and 
// getting the top of its stack-trace. 
// NOTE you must use: getClassName() because getClass() just returns StackTraceElement.class 
static final Logger logger = Logger.getLogger(new Throwable() .getStackTrace()[0].getClassName()); 
Run Code Online (Sandbox Code Playgroud)


Chr*_*fer 13

滥用SecurityManager

System.getSecurityManager().getClassContext()[0].getName();
Run Code Online (Sandbox Code Playgroud)

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

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


Jam*_*uis 9

如果您想要整个包名,请致电:

String name = MyClass.class.getCanonicalName();
Run Code Online (Sandbox Code Playgroud)

如果您只想要最后一个元素,请致电:

String name = MyClass.class.getSimpleName();
Run Code Online (Sandbox Code Playgroud)


Ser*_*kov 5

逐字使用调用者的类MyClass.class.getName()实际上可以完成这项工作,但如果将此代码传播到需要此类名的许多类/子类,则很容易复制/粘贴错误.

汤姆Hawtin的食谱其实不差,一个只需要做饭正确的方式:)

如果你有一个带有可以从子类调用的静态方法的基类,并且这个静态方法需要知道实际调用者的类,这可以像下面这样实现:

class BaseClass {
  static sharedStaticMethod (String callerClassName, Object... otherArgs) {
    useCallerClassNameAsYouWish (callerClassName);
    // and direct use of 'new Object() { }.getClass().getEnclosingClass().getName()'
    // instead of 'callerClassName' is not going to help here,
    // as it returns "BaseClass"
  }
}

class SubClass1 extends BaseClass {
  static someSubclassStaticMethod () {
    // this call of the shared method is prone to copy/paste errors
    sharedStaticMethod (SubClass1.class.getName(),
                        other_arguments);
    // and this call is safe to copy/paste
    sharedStaticMethod (new Object() { }.getClass().getEnclosingClass().getName(),
                        other_arguments);
  }
}
Run Code Online (Sandbox Code Playgroud)