当子类从超类继承main()时,是否可以确定在命令行上调用的实际类?例如,考虑以下两个类,其中主由A实现,由B继承
public class A {
public static void main(String[] args) throws Exception {
// Replace with <some magic here> to determine the class
// invoked on the command-line
final Class<? extends A> c = A.class;
System.out.println("Invoked class: " + c.getName());
final A instance = c.newInstance();
// Do something with instance here...
}
}
public class B extends A {
}我们可以成功地调用B (即,B确实“继承”main --至少在任何意义上可以继承静态方法),但我没有找到确定用户调用的实际类的方法:
$ java -cp . A
Invoked class: A
$ java -cp . B
Invoked class: A我最接近的是要求子类实现main(),并在超类中调用一个助手方法,然后该方法读取线程堆栈以确定调用类:
public class AByStack {
public static void run(String[] args) throws Exception {
// Read the thread stack to find the calling class
final Class<? extends AByStack> c = (Class<? extends AByStack>)
Class.forName(Thread.currentThread().getStackTrace()[2].getClassName());
System.out.println("Invoked class: " + c.getName());
final AByStack instance = c.newInstance();
// Do something with instance here...
}
public static void main(String[] args) throws Exception {
run(args);
}
}
public class BByStack extends AByStack {
public static void main(String[] args) throws Exception {
// Call the master 'run' method
run(args);
}
}这种方法有效:
$ java -cp . AByStack
Invoked class: AByStack
$ java -cp . BByStack
Invoked class: BByStack但是我真的很想消除子类实现main()的要求(是的,请叫我挑食.)。我不介意它是否需要一些难看的代码,因为它将被实现一次并隐藏在基类中,而且我对Sun/Oracle很感兴趣,所以我愿意考虑使用私有的sun.misc类或类似的东西。
但我确实想避免平台依赖。例如,在Linux上,我们可以查看/proc/self/cmdline,但这当然不能移植到Windows (我不确定Mac我现在还没有带着我的Mac来测试这个技巧)。我认为JNI和JVMTI也是出于同样的原因离开的。关于JVMTI,我可能错了,但在我看来,它需要一个C包装器。如果不是,也许我们可以以某种方式使用这个接口。
这个问题是几年前在http://www.coderanch.com/t/375326/java/java/Getting-command-line-class上提出的。最好的答案是在每个子类中都需要一个静态初始化器块--对子类作者的要求不同,但类似于我演示的main调用run()解决方案。但我没有看到最近的讨论;我希望当前的‘m可能允许访问在讨论时无法获得的信息。
发布于 2011-04-15 21:57:40
由于我浪费了更多的时间来调查这个问题,所以我会在这里发表我的结论。
第一,重申这个问题的理据:
我已经将参数处理、I/O和其他共享任务抽象为一个抽象超类,我希望其他类能够对其进行扩展。在执行参数解析和共享设置之后,超类中的静态方法实例化子类的一个实例,并调用其run()方法。
子类的作者被鼓励实现公共静态void (String[]),并调用超类的主入口点。但是,与所有子类实现run()的要求不同,我们不能在编译时静态地执行该需求(因为抽象静态方法没有概念)。
因此,我试图在超类中实现一个main(String[])方法,该方法可以确定命令行上请求的子类的名称,并实例化相应的类。
我找到了两种方法,都是针对Sun / Oracle JVM的。
第一个使用内部sun.jvmstat类:
import java.lang.management.ManagementFactory;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.VmIdentifier;
import sun.jvmstat.perfdata.monitor.protocol.local.LocalMonitoredVm;
...
public static String jvmstatMainClass() {
// Determine the VMID (on most platforms, this will be the PID)
final String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
// Connect to the virtual machine by VMID
final VmIdentifier vmId = new VmIdentifier(pid);
final LocalMonitoredVm lmVm = new LocalMonitoredVm(vmId, 1000);
// Find the requested main-class
String mainClass = MonitoredVmUtil.mainClass(lmVm, true);
// And detach from the VM
lmVm.detach();
return mainClass;
}第二种使用Sun的jps实用工具:
import java.io.BufferedReader;
import java.io.InputStreamReader;
...
public static String jpsMainClass() {
// Determine the VMID (on most platforms, this will be the PID)
final String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
// Execute the 'jps' utility
final Process jps = Runtime.getRuntime().exec(new String[] { "jps", "-l" });
final BufferedReader br = new BufferedReader(new InputStreamReader(jps.getInputStream()));
// Parse the output of jps to find the current VM by PID
for (String line = br.readLine(); line != null; line = br.readLine()) {
final String[] split = line.split(" ");
if (pid.equals(split[0])) {
return split[1];
}
}
}
return null;
}希望我浪费的时间能证明对别人有帮助。
发布于 2011-04-11 21:36:59
您可能希望这样做,以便可以使用不同的名称调用某些工具,但根据调用的名称,它们的行为大致相同吗?或者类似的魔法?
在这种情况下,您可以简单地在所有子类中拥有一个实际的main()方法,并将其委托给一个方法,该方法也使用被调用的类的名称:
public class Super {
protected void doMain(String invokee, String... args) {
System.out.println("I was invoked as: " + invokee);
}
}
public class ToolA {
public static void main(String... args) {
new Super().doMain("ToolA", args); // or ToolA.class.getName() to be refactor-proof
}
}发布于 2011-04-11 21:55:14
我想我应该修改run()方法的签名,而不是使用调用堆栈,而是在每个子类中实现main(),如下所示:
public class BByStack extends AByStack {
public static void main(String[] args) throws Exception {
// Call the master 'run' method
run(BByStack.class, args);
}
}如果您所做的只是使用默认构造函数实例化,我甚至可以传入
new BByStack()
我不知道要做什么;这个方法是静态的,所以您不能获得this.getClass()之类的句柄;如果该方法没有在子类上定义,您也不能从堆栈中获得该信息。
https://stackoverflow.com/questions/5627819
复制相似问题