How do I find what an object refers to?

427 Views Asked by At

I'm trying to find a way to list which objects a run-time object is referring to. I know there is a way to enquire the jvm using oql but what I'd like to do is to query it from inside a program. Is there any API I could use?

2

There are 2 best solutions below

5
On BEST ANSWER

You can do it via Reflection (java.lang.reflect).

How is described in this article. Basically, given this class that has private members:

public class Secret {

    private String secretCode = "It's a secret";

    private String getSecretCode(){
        return secretCode;     
    }
}

With Reflection, you can access all of its members (including the private ones), including their values. And so you look at all of its data members to see what they refer to (and of course, you can repeat the process if they also refer to other objects). Here's how to access their members (this code shows methods as well, which you probably won't need if you're just interested in data, but I didn't see any good reason to pull that part out):

import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import java.lang.reflect.InvocationTargetException; 

public class Hacker {

    private static final Object[] EMPTY = {};

    public void reflect(Object instance)
    throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Class secretClass = instance.getClass();

        // Print all the method names & execution result
        Method methods[] = secretClass.getDeclaredMethods(); 
        System.out.println("Access all the methods"); 
        for (int i = 0; i < methods.length; i++) { 
            System.out.println("Method Name: " + methods[i].getName());
            System.out.println("Return type: " + methods[i].getReturnType());
            methods[i].setAccessible(true);
            System.out.println(methods[i].invoke(instance, EMPTY) + "\n");
        }

        //  Print all the field names & values
        Field fields[] = secretClass.getDeclaredFields();
        System.out.println("Access all the fields");
        for (int i = 0; i < fields.length; i++){ 
            System.out.println("Field Name: " + fields[i].getName()); 
            fields[i].setAccessible(true); 
            System.out.println(fields[i].get(instance) + "\n"); 
        }
    }

    public static void main(String[] args){

        Hacker newHacker = new Hacker();

        try { 
            newHacker.reflect(new Secret());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

I've fixed a bug in their original code and made a small change to make it more clear that Hacker is not in any way tied to Secret (other than in main).

Update: Re your question below about the fields from base classes, here's an updated Hacker that does that (I've assumed you don't want to try to enumerate the fields on Object, so I've stopped there):

import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import java.lang.reflect.InvocationTargetException; 

public class Hacker {

    private static final Object[] EMPTY = {};

    public void reflect(Object instance)
    throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Class cls = instance.getClass();

        while (cls != null && cls != Object.class) {
            System.out.println("From class: " + cls.getName());

            // Print all the method names & execution result
            Method methods[] = cls.getDeclaredMethods(); 
            System.out.println("Access all the methods"); 
            for (int i = 0; i < methods.length; i++) { 
                System.out.println("Method Name: " + methods[i].getName());
                System.out.println("Return type: " + methods[i].getReturnType());
                methods[i].setAccessible(true);
                System.out.println(methods[i].invoke(instance, EMPTY) + "\n");
            }

            //  Print all the field names & values
            Field fields[] = cls.getDeclaredFields();
            System.out.println("Access all the fields");
            for (int i = 0; i < fields.length; i++){ 
                System.out.println("Field Name: " + fields[i].getName()); 
                fields[i].setAccessible(true); 
                System.out.println(fields[i].get(instance) + "\n"); 
            }

            // Go to the base class
            cls = cls.getSuperclass();
        }
    }

    public static void main(String[] args){

        Hacker newHacker = new Hacker();

        try { 
            newHacker.reflect(new Secret());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

When combined with

public class BaseSecret {

  private String baseSecretCode = "It's a base secret";

}

and

public class Secret extends BaseSecret {

    private String secretCode = "It's a secret";

    private String getSecretCode(){
        return secretCode;     
    }
}

you get:

$ java Hacker 
From class: Secret
Access all the methods
Method Name: getSecretCode
Return type: class java.lang.String
It's a secret

Access all the fields
Field Name: secretCode
It's a secret

From class: BaseSecret
Access all the methods
Access all the fields
Field Name: baseSecretCode
It's a base secret
0
On

You can use getClass() method of Object class to get the runtime class of an object.