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

如何从这个类的静态方法获得类的名字。 例如

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

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

为了正确支持重构(重命名类),那么你应该使用:

  MyClass.class.getName(); // full name with package 

或者(感谢@James Van Huis ):

  MyClass.class.getSimpleName(); // class name and no more 

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

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

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

 MethodHandles.lookup().lookupClass() 

这个指令工作正常:

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

你可以像这样使用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() ); } } 

然后:

 javac MyObject.java javah -jni MyObject 

然后:

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

然后将C编译libclassname.solibclassname.so的共享库并运行java!

*轻笑

我用它来初始化我的类(或注释)顶部的Log4j记录器。

PRO:Throwable已经加载,你可以通过不使用“IO重”的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()); 

滥用SecurityManager

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

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

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

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

 String name = MyClass.class.getCanonicalName(); 

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

 String name = MyClass.class.getSimpleName(); 

MyClass.class.getName()这样的调用者类的逐字使用实际上是做这个工作的,但是如果你把这个代码传播给你需要这个类名的许多类/子类,就容易发生复制/粘贴错误。

而汤姆霍金的食谱其实不错,只需要用正确的方法做饭:)

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

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

所以,当我们需要静态地获取类对象或类的全名/简单名称而没有显式使用MyClass.class语法时,我们遇到了这种情况。

在某些情况下它可以非常方便,比如记录器实例 kotlin高级函数(在这种情况下,kotlin创建一个静态java类,不能从kotlin代码访问)。

我们有几个不同的变种获取这个信息:

  1. new Object(){}.getClass().getEnclosingClass();
    汤姆·霍金 ( Tom Hawtin)指出

  2. getClassContext()[0].getName();SecurityManager
    Christoffer指出

  3. new Throwable().getStackTrace()[0].getClassName();
    由路德维格

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

  5. 最后真棒
    MethodHandles.lookup().lookupClass();
    来自Rein

我已经准备好了所有变体的jmh基准,结果是:

 # 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_javaClass_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 

结论

  1. 最好的变种使用 ,而不是干净和怪异的快速。
    仅适用于Java 7和Android API 26!
  MethodHandles.lookup().lookupClass(); 
  1. 如果你需要Android或Java 6的这个功能,你可以使用第二个最好的变种。 它也相当快, 但在每个使用地点创建一个一致的类 🙁
  new Object(){}.getClass().getEnclosingClass(); 
  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()) } 
  1. 您可能不需要使用基于异常getStackTrace()Thread.currentThread()最后两个变体。 非常低效,只能返回类名作为String ,而不是Class<*>实例。

PS

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

 import org.slf4j.Logger import org.slf4j.LoggerFactory // Should be inlined to get an actual class instead of the one when 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 * @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 } 

避免定义下面特定类的重构安全,剪切和粘贴安全的解决方案。

编写一个静态方法来恢复类名,注意在方法名中包含类名:

 private static String getMyClassName(){ return MyClass.class.getName(); } 

然后在你的静态方法中回想一下:

 public static void myMethod(){ Tracer.debug(getMyClassName(), "message"); } 

通过避免使用字符串来实现重构安全性,因为如果剪切并粘贴调用方法,就不会在目标“MyClass2”类中找到getMyClassName(),所以您将被迫重新定义和更新它。

由于这个问题像`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定义为私有是很重要的,因为:
1)它不能被继承:派生类必须定义自己的thisClass或产生一个错误信息
2)来自其他类的引用应该作为ClassName.class而不是ClassName.thisClass来完成。

在定义thisClass ,访问类名变为:

 thisClass.getName() 

我需要在多个类的静态方法中的类名,所以我用下面的方法实现了一个JavaUtil类:

 public static String getClassName() { String className = Thread.currentThread().getStackTrace()[2].getClassName(); int lastIndex = className.lastIndexOf('.'); return className.substring(lastIndex + 1); } 

希望它会帮助!

如果您正在使用反射,您可以获得Method对象,然后:

 method.getDeclaringClass().getName() 

为了获得方法本身,你可以使用:

 Class<?> c = Class.forName("class name"); Method method = c.getDeclaredMethod ("method name", parameterTypes)