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

时间:2009-06-01 20:42:17

标签: java static

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

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

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

15 个答案:

答案 0 :(得分:215)

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

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

或(感谢@James Van Huis):

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

答案 1 :(得分:117)

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

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

答案 2 :(得分:76)

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

MethodHandles.lookup().lookupClass()

答案 3 :(得分:40)

因此,当我们需要静态获取类对象或类完整/简单名称而没有明确使用MyClass.class语法时,我们就会出现这种情况。

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

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

  1. new Object(){}.getClass().getEnclosingClass();
    Tom Hawtin - tackline

  2. 注明 来自getClassContext()[0].getName();
  3. SecurityManagerChristoffer

  4. 注明
  5. new Throwable().getStackTrace()[0].getClassName();
    count ludwig

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

  7. 最后很棒 MethodHandles.lookup().lookupClass();
    来自Rein


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

    # 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
    


    结论

    1. 最佳使用方式,相当干净且怪异 仅在Java 7和Android API 26之后可用!
    2.  
       MethodHandles.lookup().lookupClass();
      
      1. 如果您需要Android或Java 6的此功能,您可以使用第二个最佳变体。它也相当快,但在每个使用地点创建一个匿名类 :(
      2.  
         new Object(){}.getClass().getEnclosingClass();
        
        1. 如果您在很多地方需要它,并且不希望您的字节码由于大量的匿名类而膨胀 - SecurityManager是您的朋友(第三个最佳选择)。

          但您无法致电getClassContext() - 它已在SecurityManager课程中受到保护。你需要一些这样的帮助类:

        2.  
           // 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<*>个实例。

          2. P.S。

            如果你想为静态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())
            

            用法示例:

            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
            }
            

答案 4 :(得分:37)

此说明正常:

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

答案 5 :(得分: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() );
    }
}

然后:

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.so的共享库并运行java!

*笑声

答案 6 :(得分:21)

我使用它来启动我的类顶部的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()); 

答案 7 :(得分:14)

滥用SecurityManager

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

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

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

答案 8 :(得分:9)

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

String name = MyClass.class.getCanonicalName();

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

String name = MyClass.class.getSimpleName();

答案 9 :(得分:5)

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

Tom Hawtin's recipe实际上并不坏,只需要以正确的方式烹饪:)

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

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

答案 10 :(得分:4)

重构安全,剪切和粘贴安全的解决方案,可避免在下面定义ad-hoc类。

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

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

然后在静态方法中调用它:

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

通过避免使用字符串来提供重构安全性,授予剪切和粘贴安全性,因为如果您剪切和粘贴调用方法,则在目标“MyClass2”类中找不到getMyClassName(),因此您将成为被迫重新定义和更新它。

答案 11 :(得分:3)

由于问题Something like `this.class` instead of `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)其他类的引用应该以{{1​​}}而不是ClassName.class完成。

定义ClassName.thisClass后,对类名的访问将变为:

thisClass

答案 12 :(得分:1)

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

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

希望它会有所帮助!

答案 13 :(得分:0)

我已将这两种方法用于staticnon static方案:

主要课程:

//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());

答案 14 :(得分:-1)

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

method.getDeclaringClass().getName()

要获取Method本身,您可以使用:

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