有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java反射:如何获取变量的名称?

使用Java反射,是否可以获取局部变量的名称?例如,如果我有:

Foo b = new Foo();
Foo a = new Foo();
Foo r = new Foo();

是否可以实现一种方法来查找这些变量的名称,如下所示:

public void baz(Foo... foos)
{
    for (Foo foo: foos) {
        // Print the name of each foo - b, a, and r
        System.out.println(***); 
    }
}

EDIT:这个问题与Is there a way in Java to find the name of the variable that was passed to a function?不同,它更纯粹地询问是否可以使用反射来确定局部变量的名称,而另一个问题(包括公认的答案)更侧重于测试变量的值


共 (6) 个答案

  1. # 1 楼答案

    这根本不可能。变量名在Java中不通信(并且可能由于编译器优化而被删除)

    编辑(与评论相关):

    如果您不想将其用作函数参数,这里有一个替代方案(我不会使用它-见下文):

    public void printFieldNames(Object obj, Foo... foos) {
        List<Foo> fooList = Arrays.asList(foos);
        for(Field field : obj.getClass().getFields()) {
             if(fooList.contains(field.get()) {
                  System.out.println(field.getName());
             }
        }
    }
    

    如果a == b, a == r, or b == r或存在具有相同引用的其他字段,则会出现问题

    现在不需要编辑,因为问题已澄清

  2. # 2 楼答案

    编辑:删除之前的两个答案,一个用于回答编辑前的问题,另一个用于回答问题,如果不是绝对错误,至少是接近问题。

    如果使用(javac -g)上的调试信息进行编译,则局部变量的名称将保留在中。类文件。例如,以这个简单的类为例:

    class TestLocalVarNames {
        public String aMethod(int arg) {
            String local1 = "a string";
            StringBuilder local2 = new StringBuilder();
            return local2.append(local1).append(arg).toString();
        }
    }
    

    在使用javac -g:vars TestLocalVarNames.java编译之后,局部变量的名称现在位于。类文件javap-l标志(“打印行号和局部变量表”)可以显示它们

    javap -l -c TestLocalVarNames显示:

    class TestLocalVarNames extends java.lang.Object{
    TestLocalVarNames();
      Code:
       0:   aload_0
       1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
       4:   return
    
      LocalVariableTable:
       Start  Length  Slot  Name   Signature
       0      5      0    this       LTestLocalVarNames;
    
    public java.lang.String aMethod(int);
      Code:
       0:   ldc     #2; //String a string
       2:   astore_2
       3:   new     #3; //class java/lang/StringBuilder
       6:   dup
       7:   invokespecial   #4; //Method java/lang/StringBuilder."<init>":()V
       10:  astore_3
       11:  aload_3
       12:  aload_2
       13:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       16:  iload_1
       17:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
       20:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       23:  areturn
    
      LocalVariableTable:
       Start  Length  Slot  Name   Signature
       0      24      0    this       LTestLocalVarNames;
       0      24      1    arg       I
       3      21      2    local1       Ljava/lang/String;
       11      13      3    local2       Ljava/lang/StringBuilder;
    }
    

    {a1}解释了我们在这里看到的内容:

    §4.7.9 The ^{} Attribute

    The LocalVariableTable attribute is an optional variable-length attribute of a Code (§4.7.3) attribute. It may be used by debuggers to determine the value of a given local variable during the execution of a method.

    LocalVariableTable存储每个插槽中变量的名称和类型,因此可以将它们与字节码匹配。这就是调试器执行“计算表达式”的方式

    不过,正如erickson所说,无法通过正常反射访问此表。如果您仍然决心这样做,我相信Java Platform Debugger Architecture (JPDA)会有所帮助(但我自己从未使用过)

  3. # 3 楼答案

    从Java8开始,可以通过反射获得一些局部变量名信息。请参阅下面的“更新”部分

    完整的信息通常存储在类文件中。一个编译时优化是删除它,节省空间(并提供一些混淆)。但是,当存在时,每个方法都有一个局部变量表属性,该属性列出了局部变量的类型和名称,以及它们所在的指令范围

    也许像ASM这样的字节码工程库允许您在运行时检查这些信息。我认为需要这些信息的唯一合理的地方是在开发工具中,因此字节码工程对于其他目的也可能有用


    更新:对这个wasadded to Java 8.参数(一个特殊的局部变量类)名称的支持有限,现在可以通过反射获得。除其他目的外,这有助于替换依赖项注入容器使用的@ParameterName注释

  4. # 4 楼答案

    import java.lang.reflect.Field;
    
    
    public class test {
    
     public int i = 5;
    
     public Integer test = 5;
    
     public String omghi = "der";
    
     public static String testStatic = "THIS IS STATIC";
    
     public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
      test t = new test();
      for(Field f : t.getClass().getFields()) {
       System.out.println(f.getGenericType() +" "+f.getName() + " = " + f.get(t));
      }
     }
    
    }
    
  5. # 5 楼答案

    您可以这样做:

    Field[] fields = YourClass.class.getDeclaredFields();
    //gives no of fields
    System.out.println(fields.length);         
    for (Field field : fields) {
        //gives the names of the fields
        System.out.println(field.getName());   
    }
    
  6. # 6 楼答案

    您只需创建一个字段数组,然后将其设置为所需的类,如下所示

    Field fld[] = (class name).class.getDeclaredFields();   
    for(Field x : fld)
    {System.out.println(x);}
    

    例如,如果你这样做了

    Field fld[] = Integer.class.getDeclaredFields();
              for(Field x : fld)
              {System.out.println(x);}
    

    你会得到

    public static final int java.lang.Integer.MIN_VALUE
    public static final int java.lang.Integer.MAX_VALUE
    public static final java.lang.Class java.lang.Integer.TYPE
    static final char[] java.lang.Integer.digits
    static final char[] java.lang.Integer.DigitTens
    static final char[] java.lang.Integer.DigitOnes
    static final int[] java.lang.Integer.sizeTable
    private static java.lang.String java.lang.Integer.integerCacheHighPropValue
    private final int java.lang.Integer.value
    public static final int java.lang.Integer.SIZE
    private static final long java.lang.Integer.serialVersionUID