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

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

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

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

210969 次浏览

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

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

or(感谢@James Van Huis):

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

如果你想要整个包的名称,调用:

String name = MyClass.class.getCanonicalName();

如果你只想要最后一个元素,调用:

String name = MyClass.class.getSimpleName();

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

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

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

滥用SecurityManager

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

或者,如果没有设置,则使用扩展它的内部类(下面的示例可耻地从真正的基本知识的复制):

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

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

优点:Throwable已经加载,你可能会节省资源,不使用“IO重型”安全管理器。

反:有些问题是,这是否适用于所有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());

这条指令工作得很好:

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

*笑

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

这是一种重构安全、剪切粘贴安全的解决方案,可以避免定义下面的临时类。

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

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

然后在你的静态方法中召回它:

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

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

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

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

希望对大家有所帮助!

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

MethodHandles.lookup().lookupClass()

由于问题比如' 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()

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

method.getDeclaringClass().getName()

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

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

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

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

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

    <李> < p > new Object(){}.getClass().getEnclosingClass(); < br > 汤姆·霍丁-打线

  1. getClassContext()[0].getName();SecurityManager
    创造者

  2. <李> < p > new Throwable().getStackTrace()[0].getClassName(); < br > 由计数路德维希 < / p > <李> < p > Thread.currentThread().getStackTrace()[1].getClassName(); < br > 从Keksi < / p >
  3. 和最后棒极了

    < p > MethodHandles.lookup().lookupClass(); < br > 从控制 < / p >

我已经为所有变量准备了基准测试,结果如下:
# 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. 如果你在很多地方都需要它,并且不希望你的字节码因为大量的匿名类而膨胀——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() from异常或Thread.currentThread()的最后两个变体。非常无效,只能返回类名作为String,而不是Class<*>实例。

注:

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

我在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());