Java:如何从静态上下文中获取当前类的类对象?

时间:2009-08-20 16:45:42

标签: java reflection logging

我有一个记录函数,它将调用对象作为参数。然后我在其上调用getClass()。getSimpleName(),以便我可以轻松获取要添加到我的日志条目的类名以便于参考。问题是,当我从静态方法调用我的日志函数时,我无法传入“this”。我的日志功能如下所示:

public static void log(Object o, String msg){
  do_log(o.getClass().getSimpleName()+" "+msg);
}
public void do_something(){
  log(this, "Some message");
}

但是我要说我想从静态函数中记录:

public static void do_something_static(){
  log(this, "Some message from static");
}

显然do_something_static()不起作用,因为它是静态的,而“this”不在静态上下文中。我怎么能绕过这个?我是否可以在不使用反射的情况下完成它(因为我知道有很多开销涉及并且它可能会影响性能,因为我记录了大量数据)

我知道我可能会以某种方式将当前类硬编码到调用中,但我确信当我将函数移动到另一个类时,我将忘记更新硬编码的引用,它将不再是正确的。

谢谢!

8 个答案:

答案 0 :(得分:14)

您可以添加“Class”作为第一个参数并重载日志方法:

public class SomeClass {
    // Usage test:
    public static void main( String [] args ) {
        log( SomeClass.class, "Hola" );
        log( new java.util.Date(), "Hola" );
    }

    // Object version would call Class method... 
    public static void log( Object o , String msg ) {
        log( o.getClass(), msg );
    }
    public static void log( Class c ,  String message ) {
        System.out.println( c.getSimpleName() + " " + message );
    }
}

输出:

$ java SomeClass
SomeClass Hola
Date Hola

但是将调用类作为第一个参数传递感觉真的很糟糕。这里面向对象的模型与“程序”风格相反。

你可以使用堆栈跟踪来获取调用类,但正如你所提到的,如果你多次调用它会有一个开销。

但是如果你创建的是类变量,那么如果你碰巧有1000个类使用这个实用程序,那么只有一个实例,你最多可以有1000个调用。

这样的事情会更好(这个other答案的微妙变化):

public class LogUtility {

    private final String loggingFrom;

    public static LogUtility getLogger() {
        StackTraceElement [] s = new RuntimeException().getStackTrace();
        return new LogUtility( s[1].getClassName() );
    }

    private LogUtility( String loggingClassName ) {
        this.loggingFrom = "("+loggingClassName+") ";
    }

    public void log( String message ) {
        System.out.println( loggingFrom + message );
    }
}

使用测试:

class UsageClass {
    private static final LogUtility logger = LogUtility.getLogger();

    public static void main( String [] args ) {

        UsageClass usageClass = new UsageClass();

        usageClass.methodOne();
        usageClass.methodTwo();
        usageClass.methodThree();

    }
    private void methodOne() {
        logger.log("One");
    }
    private void methodTwo() {
        logger.log("Two");
    }
    private void methodThree() {
        logger.log("Three");
    }
}

输出

$ java UsageClass
(UsageClass) One
(UsageClass) Two
(UsageClass) Three

注意声明:

....
class UsageClass {
    // This is invoked only once. When the class is first loaded.
    private static final LogUtility logger = LogUtility.getLogger();
....

这样,如果您使用来自objectA,objectB,objectC的“logger”或类方法(如main),它们都将拥有一个记录器实例并不重要。

答案 1 :(得分:7)

查询堆栈跟踪可能对您的问题很不利。你有3种可能的方法。

异常#的getStackTrace()

只需创建一个异常实例并获取第一帧:

static String className() {
    return new RuntimeException().getStackTrace()[0].getClassName();
}

使用Thread更加容易:

static String className2() {
    return Thread.currentThread().getStackTrace()[1].getClassName();
}

这两种方法的缺点是您无法重复使用它们。所以你可能想为它定义一个Helper类:

class Helper {

    public static String getClassName() {
        return Thread.currentThread().getStackTrace()[2].getClassName();
    }
} 

现在您可以通过编程方式获取您的类名,而无需对类名进行硬编码:

public static void log(Object o, String msg){
    do_log(Helper.getCClassName() + "  " + msg);
}

答案 2 :(得分:2)

将方法log改为:

public static void log(Class c, String msg){
  do_log(c.getSimpleName()+" "+msg);
}

如果do_something_static在课程MyClassWithStatics中,那么do_something_static将成为:

public static void do_something_static(){
  log(MyClassWithStatics.class, "Some message from static");
}

答案 3 :(得分:1)

而不是“this”使用“MyClass.class”并让你的log方法处理没有getClass()的类对象。

但是不要自己这样做,而是考虑让日志框架去做。

答案 4 :(得分:0)

您应该在短期内将课程硬编码到电话中。如果您认为在项目中的各个位置需要它,因为它是static,您应该封装它并让它将该类作为参数。

public static void do_something_static(Class callingClass){
  log(callingClass, "Some message from static");
}

答案 5 :(得分:0)

好吧,如果你真的不想硬编码像ClassName.class这样的东西,那么你可以尝试通过遍历堆栈跟踪来推断该类。幸运的是,某人already做到了这一点:)。另外,请考虑使用一个记录器,它允许您在不指定调用对象的情况下记录某些内容。

答案 6 :(得分:0)

不会实现某种Singleton做你需要的吗?

public class LogUtility {

    private final String loggingFrom;
    private static LogUtility instance;

    public static LogUtility getLogger() {
        if(instance == null)
            this.instance = new LogUtility();
        StackTraceElement [] s = new RuntimeException().getStackTrace();
        this.instance.setLoggingFrom(s[1].getClassName())
        return this.instance;
    }

    private LogUtility() {}

    private void setLoggingFrom(String loggingClassName){
        this.loggingFrom = loggingClassName;
    }

    public void log( String message ) {
        System.out.println( loggingFrom + message );
    }
}

用法(项目中的任何位置):

LogUtility.getLogger().log("Message");

答案 7 :(得分:-1)

用非静态函数替换静态函数。而不是

Utils.do_something(...)

DO

new Utils().do_something(...)