如何访问JShell中代码片段创建的对象及其引用关系?
Great question! Building a GUI to map out JShell's internal object graph is such a neat project—let's break down exactly how to pull this off.
1. Grabbing Named Variables (like allObj or tst)
JShell's API gives you direct access to variables created by user code via VarSnippet instances. Every time you call eval(), you get back a list of SnippetEvents that track what changed in the JShell session. Here's how to extract variable values:
Step-by-Step Code Example
JShell jShell = JShell.create(); Map<String, Object> jshellVariables = new HashMap<>(); // Process each line of user code List<String> userCode = Arrays.asList( "ArrayList<TestObject> allObj = new ArrayList<TestObject>();", "TestObject tst = new TestObject();", "TestObject tst2 = new TestObject();", "allObj.add(tst);", "allObj.add(tst2);" ); for (String line : userCode) { List<SnippetEvent> events = jShell.eval(line); for (SnippetEvent event : events) { // Only process valid, variable-related snippets if (event.status() == SnippetStatus.VALID && event.snippet() instanceof VarSnippet) { VarSnippet varSnippet = (VarSnippet) event.snippet(); Object varValue = jShell.varValue(varSnippet); jshellVariables.put(varSnippet.name(), varValue); // Now you have the actual object (e.g., the ArrayList or TestObject instance) System.out.printf("Found variable: %s -> %s%n", varSnippet.name(), varValue); } } }
Key Notes:
VarSnippet.name()gives you the variable's name (likeallObj).jShell.varValue(varSnippet)returns the live object instance created by the user's code.- For method calls (like
allObj.add(tst)), theSnippetEventwill be anExpressionSnippetinstead. To get the updated state ofallObj, just re-fetch its value from theVarSnippet(you can get all active variables viajShell.variables()).
2. Mapping Object Reference Relationships
JShell doesn't have a built-in API to directly get object reference graphs, but you can use Java reflection to dig into an object's fields and build this graph yourself.
Building the Reference Graph
Here's a helper method to scan an object's fields (including private ones) and track its references:
// Stores object identity hash -> list of referenced objects Map<Integer, List<Object>> referenceGraph = new HashMap<>(); private void scanObjectReferences(Object obj) { int objHash = System.identityHashCode(obj); if (obj == null || referenceGraph.containsKey(objHash)) { return; // Avoid duplicates and infinite loops from circular references } List<Object> referencedObjects = new ArrayList<>(); Class<?> currentClass = obj.getClass(); // Scan all fields in the class and its superclasses while (currentClass != null) { for (Field field : currentClass.getDeclaredFields()) { field.setAccessible(true); // Bypass access modifiers for private fields try { Object fieldValue = field.get(obj); // Skip primitives, wrappers, and strings (adjust this list as needed) if (fieldValue != null && !isSimpleType(fieldValue.getClass())) { referencedObjects.add(fieldValue); scanObjectReferences(fieldValue); // Recursively scan referenced objects } } catch (IllegalAccessException e) { // Handle exceptions (log, skip, etc.) e.printStackTrace(); } } currentClass = currentClass.getSuperclass(); } referenceGraph.put(objHash, referencedObjects); } // Helper to filter out simple types we don't need to track private boolean isSimpleType(Class<?> clazz) { return clazz.isPrimitive() || clazz.equals(Integer.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Boolean.class) || clazz.equals(String.class); }
Using This for Your Example
Once you have allObj from the variable map, call scanObjectReferences(allObj)—this will:
- Scan the
ArrayList's internal fields (like its element array). - Find the
TestObjectinstances (tstandtst2) stored in the list. - Recursively scan those
TestObjects for their own references.
3. Tips for Your GUI Visualization
- Track Object Identity: Use
System.identityHashCode(obj)as a unique identifier for objects—this avoids confusion when multiple variables point to the same instance. - Update on Every Eval: After each user code line is executed, refresh your variable list and re-scan references to keep the visualization up-to-date.
- Handle Circular References: The
scanObjectReferencesmethod above skips already scanned objects to prevent infinite loops (common in things like linked lists or parent-child object structures). - Display Context: For each object, show its class type, variable name(s) that point to it, and the objects it references—this makes the graph meaningful to users.
内容的提问来源于stack exchange,提问作者Raphael S

