关于反射机制的一个小问题,如何输出变量名,有兴趣的进来坐坐!

lufing 2010-10-18 08:34:41
是这样的 我想写一个函数 例如 void func(String param)

String a = "hello";
String b = "world";

我把变量a 传进去 它能打印出变量名 "a"
我把变量b 传进去 它能打印出变量名 "b"

以此类推


不知道大家有没有什么办法 我网上查了一下 只找到一个用反射机制 输出对象中属性变量名的方法

但是这种输出 传进来参数的变量名 能不能实现 怎么实现 望高手赐教

能解决此问题者 立马送上50分
...全文
406 36 打赏 收藏 转发到动态 举报
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
tlqtangok 2011-11-27
  • 打赏
  • 举报
回复
可以用保存在磁盘上的临时文件来实现的。
ChDw 2010-10-20
  • 打赏
  • 举报
回复
以下是一段代码参考,我考虑不是很周全的。肯定在某些情况下不能正确判断出变量名的。
我还是那句话,建议你放弃这个想法。实在不是很有意思的东西,而且涉及到JVM底层的指令了。

引用proguard.jar(我用4.2版本的,其它版本应该差不多)

package chdw;

import java.io.DataInputStream;

import proguard.classfile.ProgramClass;
import proguard.classfile.ProgramMethod;
import proguard.classfile.attribute.Attribute;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.attribute.LineNumberTableAttribute;
import proguard.classfile.attribute.LocalVariableInfo;
import proguard.classfile.attribute.LocalVariableTableAttribute;
import proguard.classfile.constant.MethodrefConstant;
import proguard.classfile.constant.Utf8Constant;
import proguard.classfile.instruction.ConstantInstruction;
import proguard.classfile.instruction.Instruction;
import proguard.classfile.instruction.InstructionFactory;
import proguard.classfile.instruction.VariableInstruction;
import proguard.classfile.io.ProgramClassReader;

public class TestName {
public static String getName(String name) throws Exception {
String callerClzName = new Exception().getStackTrace()[1].getClassName();
String callerMethodName = new Exception().getStackTrace()[1].getMethodName();
int callerLineNumber = new Exception().getStackTrace()[1].getLineNumber();
Class<?> callerClz = Class.forName(callerClzName);
ProgramClassReader r = new ProgramClassReader(new DataInputStream(callerClz.getResourceAsStream("/" + callerClzName.replace('.', '/') + ".class")));
ProgramClass clz = new ProgramClass();
r.visitProgramClass(clz);
for(ProgramMethod m : clz.methods) {
if(m == null)
continue;
if(callerMethodName.equals(m.getName(clz))) {
String lineNumRange = m.getLineNumberRange(clz);
int index = lineNumRange.indexOf(':');
int start = Integer.parseInt(lineNumRange.substring(0, index));
int end = Integer.parseInt(lineNumRange.substring(index + 1));
if(callerLineNumber >= start && callerLineNumber <= end) {
for(Attribute attr : m.attributes) {
if(attr instanceof CodeAttribute) {
CodeAttribute code = (CodeAttribute) attr;
LineNumberTableAttribute lineNumberTable = (LineNumberTableAttribute) code.getAttribute(clz, "LineNumberTable");
for(int i = 0; i < lineNumberTable.u2lineNumberTableLength; i++) {
if(lineNumberTable.lineNumberTable[i].u2lineNumber == callerLineNumber) {
int codeEnd = (i < lineNumberTable.u2lineNumberTableLength - 1) ? lineNumberTable.lineNumberTable[i + 1].u2startPC : code.code.length;
Instruction prev = null;
for(int z = 0; z < codeEnd;) {
Instruction instruction = InstructionFactory.create(code.code, z);
if(lineNumberTable.getLineNumber(z) == callerLineNumber
&& instruction.getName().equals("invokestatic")) {
ConstantInstruction invokeStatic = (ConstantInstruction) instruction;
MethodrefConstant method = (MethodrefConstant) clz.constantPool[invokeStatic.constantIndex];
if(method.getName(clz).equals("getName") && method.getClassName(clz).replace('/', '.').equals(TestName.class.getName())) {
VariableInstruction var = (VariableInstruction) prev;
for(Attribute at : code.attributes) {
if(at instanceof LocalVariableTableAttribute) {
LocalVariableTableAttribute varTable = (LocalVariableTableAttribute) at;
LocalVariableInfo varInfo = varTable.localVariableTable[var.variableIndex];
Utf8Constant varName = (Utf8Constant) clz.constantPool[varInfo.u2nameIndex];
return varName.getString();
}
}
}
}
z += instruction.length(z);
prev = instruction;
}
}
}
}
}
}
}
}

return null;
}
}



package chdw;


public class Test {
public static void main(String[] args) throws Exception {
String xyz = "aa";
String zyx = "aa";
System.out.println(TestName.getName(xyz));
System.out.println(TestName.getName(zyx));
}
}



最后可以输出
xyz
zyx

ChDw 2010-10-20
  • 打赏
  • 举报
回复
我的回复已经说明了吧。这个信息在保留了调试信息的编译方式下,其实是可以获得的。只是非常复杂而且意义不是很大,建议你不要做。


如果你真的非常需要这个功能,那么也并非没有办法(前提是这个类保留了调试信息编译的)使用proguard这个项目中的一些类可以帮助你读取相关的信息
whut0802 2010-10-20
  • 打赏
  • 举报
回复
来看高人
hanRivergo 2010-10-20
  • 打赏
  • 举报
回复
31L高,学习。
「已注销」 2010-10-20
  • 打赏
  • 举报
回复
这样的代码应该在测试里可能会有大的用处

平时写代码估计用不上
lingdushanke 2010-10-19
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 hjjk123 的回复:]
引用 24 楼 lingdushanke 的回复:
18楼、21楼正解

某些人不要觉得编译后就没有名称只有地址了,先去理解一下什么是反射机制吧

楼主看一下java.lang.Class类的API,它所提供的方法可以帮助你获得你想要的信息

自己好好看看

类变量的字节码
// 3 5:ldc1 #2 <String "ddddd">
// 4 7:putfield ……
[/Quote]

不好意思,只考虑成员变量了,没想局部变量
thegodofwar 2010-10-19
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 chdw 的回复:]
不明白。你的意思是
String a = "hello";
String b = "world";

func(a);
func(b);

这样会输出a和b?这个基本不太可能实现,因为a和b这个名字都可能在编译后没有了

如果你非常非常需要这个东西,那么在保证这个类保持LocalVariable表的时候,再在你的实现中根据new Exception().getStackTrace……
[/Quote]
职业...
hjjk123 2010-10-19
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 lingdushanke 的回复:]
18楼、21楼正解

某些人不要觉得编译后就没有名称只有地址了,先去理解一下什么是反射机制吧

楼主看一下java.lang.Class类的API,它所提供的方法可以帮助你获得你想要的信息
[/Quote]
自己好好看看

类变量的字节码

// 3 5:ldc1 #2 <String "ddddd">
// 4 7:putfield #3 <Field String oj>
上面的oj是类变量的名称,他是给用户保留的,你再去看看局域变量,根本无法获得
hjjk123 2010-10-19
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 lingdushanke 的回复:]
18楼、21楼正解

某些人不要觉得编译后就没有名称只有地址了,先去理解一下什么是反射机制吧

楼主看一下java.lang.Class类的API,它所提供的方法可以帮助你获得你想要的信息
[/Quote]

局域变量 你试试 可以 反射吗????

当然 类变量 是可以的。。。。
铑枪--突廆孒 2010-10-19
  • 打赏
  • 举报
回复
XXX.getDeclaredField("a").getName
lingdushanke 2010-10-19
  • 打赏
  • 举报
回复
18楼、21楼正解

某些人不要觉得编译后就没有名称只有地址了,先去理解一下什么是反射机制吧

楼主看一下java.lang.Class类的API,它所提供的方法可以帮助你获得你想要的信息
zhuzeitou 2010-10-19
  • 打赏
  • 举报
回复
好吧,发现我又看错了……
唐玲玲 2010-10-19
  • 打赏
  • 举报
回复
先赋值为空
zhuzeitou 2010-10-19
  • 打赏
  • 举报
回复
public class Test {
String a;
String b;

Test(String a, String b) {
this.a = a;
this.b = b;
};

void func(String param) {
Field[] fields = getClass().getDeclaredFields();
for (Field f : fields) {
try {
if (f.get(this).equals(param)) {
System.out.println(f.getName());
return;
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("null");
}

public static void main(String[] args) {
Test t = new Test("Hello", "World");
t.func("World");
}
}


修改了下,封装成lz说的func接口了
龙四 2010-10-19
  • 打赏
  • 举报
回复
可能性较小,字节码中不需要变量名称,已经给你去掉了
zhuzeitou 2010-10-19
  • 打赏
  • 举报
回复
public class Test {
String a;
String b;
Test(String a, String b) {
this.a = a;
this.b = b;
};

public static void main(String[] args) {
Test t = new Test("Hello", "World");
Field[] fields = t.getClass().getDeclaredFields();
for (Field f : fields) {
try {
if (f.get(t).equals("Hello")) {
System.out.println(f.getName());
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (f.get(t).equals("World")) {
System.out.println(f.getName());
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

输出
a
b

这个意思?
huang5491276 2010-10-19
  • 打赏
  • 举报
回复
不明白啥意思
Miracle1216 2010-10-19
  • 打赏
  • 举报
回复
都是什么跟什么啊。。。。
树成 2010-10-19
  • 打赏
  • 举报
回复
变量名称在编译以后就没有了,成员变量会变成指向堆内存的指针的偏移量,虚拟机通过对象指针偏移量获得成员变量数据。局部变量会变成方法栈空间中,方法的局部变量索引,虚拟机根据索引下标获得局部变量数据。因此变量名称只对开发者有意义,对虚拟机变量名称是没有意义的,所以编译以后就没有这些名称了。自然通过反射就无法获得,不错成员变量的变量名还是能够得到的,因为解析class文件的时候会用到成员变量名,但是局部变量是肯定没有的。
加载更多回复(14)

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧