代码之家  ›  专栏  ›  技术社区  ›  Miles D

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

  •  219
  • Miles D  · 技术社区  · 16 年前

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

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

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

    15 回复  |  直到 16 年前
        1
  •  241
  •   Community CDub    8 年前

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

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

    或者(多亏了 @James Van Huis ):

     MyClass.class.getSimpleName(); // class name and no more
    
        2
  •  122
  •   Rein    10 年前

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

    MethodHandles.lookup().lookupClass()
    
        3
  •  121
  •   Tom Hawtin - tackline    4 年前

    照@toolkit说的做。不要这样做:

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

    ( 或者,如果您使用的是一个Java版本,该版本在最初编写这个答案之后很好地发布,@Rein。)

        4
  •  84
  •   Artyom Krivolapov EdChum    7 年前

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

    上层函数(在本例中,kotlin创建了一个不能从kotlin代码访问的静态Java类)。

    我们有几种不同的变体来获取此信息:

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

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

    3. new Throwable().getStackTrace()[0].getClassName();
      通过 count ludwig

    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
    


    结论

    1. ,相当干净,速度惊人。
      仅适用于Java 7和Android API 26!
     MethodHandles.lookup().lookupClass();
    
    1. 如果Android或Java 6需要此功能,可以使用第二个最佳变体。也挺快的,, 但在每个使用位置创建一个匿名类
     new Object(){}.getClass().getEnclosingClass();
    
    1. 如果您在许多地方需要它,并且不希望由于大量匿名类而导致字节码膨胀 证券经理 是你的朋友(第三好选择)。

      但你不能只是打电话 getClassContext() –它在 证券经理 班您将需要这样的帮助器类:

     // 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<*>


    附笔。

    如果要为静态kotlin UTIL(如我:)创建记录器实例,可以使用以下帮助程序:

    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
    }
    
        5
  •  41
  •   bluish dmajkic    13 年前

    Thread.currentThread().getStackTrace()[1].getClassName();
    
        6
  •  35
  •   lifelongcoug    13 年前

    通过使用JNI,您可以做一些非常美妙的事情,如下所示:

    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!

    *轻笑

        7
  •  21
  •   bluish dmajkic    13 年前

    反对:关于这是否适用于所有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()); 
    
        8
  •  13
  •   Christoffer    16 年前

    滥用安全管理器

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

    或者,如果没有设置,使用一个扩展它的内部类(下面的例子是从 Real's HowTo

    public static class CurrentClassGetter extends SecurityManager {
        public String getClassName() {
            return getClassContext()[1].getName(); 
        }
    }
    
        9
  •  9
  •   James Van Huis    16 年前

    如果您想要完整的包名,请调用:

    String name = MyClass.class.getCanonicalName();
    

    String name = MyClass.class.getSimpleName();
    
        10
  •  6
  •   Community CDub    8 年前

    调用方类的逐字使用,如 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);
      }
    }
    
        11
  •  4
  •   avalori    11 年前

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

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

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

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

        12
  •  3
  •   Community CDub    8 年前

    既然问题 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 私有的 因为:
    本级 或生成错误消息
    2) 其他类的引用应按如下方式进行 ClassName.class ClassName.thisClass .

    具有 本级 定义后,对类名的访问变为:

    thisClass.getName()
    
        13
  •  1
  •   phaderer    10 年前

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

    希望这会有帮助!

        14
  •  0
  •   0xAliHn    7 年前

    static non 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());
    
        15
  •  -2
  •   Franzé Jr.    9 年前

    如果使用反射,则可以获取方法对象,然后:

    method.getDeclaringClass().getName()
    

    要获取方法本身,您可能可以使用:

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