How the Python Tutor visualizer can help students in your Java programming courses

Summary: This article is meant for instructors who teach Java programming. Despite its name, Python Tutor is a widely-used web-based visualizer for Java that helps students to understand and debug their code. It visualizes the majority of object-oriented programming concepts taught in introductory college courses (e.g., CS1 and CS2), high school AP Computer Science, and intermediate-level Java programming. The Java visualizer will always remain free to use.

Python Tutor is a free tool that has been used by tens of millions of people since 2010 to visualize and debug code step-by-step. Despite its name, it also visualizes Java code (in addition to C, C++, and JavaScript) to help students understand critical concepts and debug homework assignments.

These Java visualizations are well-aligned with the curricula of university-level introductory programming courses (e.g., CS1 and CS2), high school AP Computer Science A, and object-oriented programming courses taught in Java. (If you teach AP CS A, also see my newer companion article on how this visualizer covers the AP CS A course framework.)

This article shows instructors how Python Tutor can automatically illustrate key concepts from a wide range of Java-based courses. If you think this tool may be helpful for your staff or students, please share this direct link in relevant course materials, chat groups, mailing lists, discussion forums, or social media:

(Also, if you teach in C or C++, check out what the C/C++ visualizer can do as well.)

Credits: The original Java visualizer was created in 2013 by David Pritchard and Will Gwozdz. It has since been enhanced in various ways over the years. Most notably, in 2023 the server infrastructure was upgraded so it now runs faster and more reliably. And in 2026, it gained support for visualizing Java collections (e.g., ArrayList, HashMap), 2-D array grids, wrapped primitive objects, and keyboard input via Scanner – all shown below.

Object-Oriented Programming

The Java visualizer can illustrate most object-oriented programming concepts that are taught in introductory and intermediate-level classes (no pun intended), including:

The tool generates visual representations of classes, objects, methods, and fields (a.k.a. attributes), helping students to grasp the relationships and interactions among these elements. Students can observe the instantiation of objects, the calling of methods, and the changing states of object fields, thus making these abstract concepts more concrete.

Here's an example showing inheritance, which I derived (no pun intended) from code in the CSAwesome e-textbook:

This program instantiates three instances of the Student class, assigns them to local variables within main(), and adds them to the arrayOfPeople array. The array contains references (arrows) pointing to each instance.

Step through execution using the slider below the code to see each Student constructor being called, which in turn uses super() to call the Person superclass's constructor. Note how the static field Student.nextId increments after each constructor call.

(A practical note: Python Tutor doesn't support creating multiple .java files, so to visualize a program with multiple classes, put all of the classes in the code editor like in this example, and make only one of them public.)


Here's an example of polymorphism using dogs and cats as subclasses of Animal:

In the current step (Step 24), the stack contains main(), which calls System.out.println, which in turn calls the toString() method of the Cat instance, which calls the getName() method of its superclass, which returns "Whiskers". Note how the this reference for all the methods refers to the Cat instance.

Step back and forth using the slider to see polymorphism in action – either the Dog or Cat toString() method is called based on the run-time type of each Animal instance in the for loop.

Calling Methods and Passing Parameters

It can be hard for beginners to understand how parameters of various types are passed to Java method calls. Step-by-step visualizations can help clarify:

In this example, here at Step 9 the swapCarYears() method is being called with the toyota and ford objects as parameters. The visualization shows how references to these objects are passed into the method as c1 and c2, respectively. The objects themselves are not copied (unlike in, say, C++) ... the references to them are.

In contrast, step forward to Step 14 when updateNumCars() gets called. Here the current value of numCars is passed into the method as myNumCars (effectively running myNumCars = numCars). So incrementing myNumCars++ within that method does not alter the original numCars.

Visualizing Strings and Wrapped Objects

You may have noticed from the prior example that string values are displayed inline within the two Car instances. Here are those same object instances again, with an added desc local variable that's also a string:

This inline visualization matches how we intuitively think about strings, but strings in Java are actually objects just like Car. So that means it's technically more accurate to draw them standalone with references (arrows) pointing to them, like this:

Note how each String object is now standalone. Users can activate this display mode by checking "show Strings and wrappers (e.g., Integer) as objects" below the code editor:

However, it's not on by default since visualizations tend to look too cluttered when this mode is on due to too many objects and arrows appearing on-screen.

Relatedly, wrapped objects (a.k.a. boxed primitives, e.g., instances of Boolean, Integer, or Float) display as visually distinct boxes to distinguish them from primitives such as boolean, int, and float, which appear as plain values within the stack frame:

And when the "show as objects" display mode from above is on, those wrapped objects render standalone on the heap with references pointing to them, just like strings do:

Arrays

We've already seen one-dimensional arrays in some of the above examples. Here's a multi-dimensional array, which displays as a compact grid table (see a2 below):

This grid display is on by default via the "show array-of-arrays as 2D array" option below the code editor. Java actually represents a multi-dimensional array as an array of arrays, so if you uncheck that option, a2 renders as an outer array with arrows pointing to separate inner arrays, which is technically more accurate but harder to read at a glance. Ragged arrays (where rows have different lengths) display as grids too, with shorter rows padded by gray cells.

Java Collections

The visualizer renders eight Java Collections classes from java.util as clean, readable data structures: ArrayList, LinkedList, HashMap, TreeMap, LinkedHashMap, HashSet, TreeSet, and LinkedHashSet. (It used to display something like "ArrayList object" without showing any of its internal contents.) Here's an ArrayList of Integer objects:

Step through execution to see how add(1, 99) inserts an element into the middle of the list by shifting later elements to the right, and how remove(0) shifts them back to the left. Also note how each Integer element appears as a wrapped (box) object rather than simply 10, 15, or 30, to distinguish it from a primitive int like the local variable first.

And here's a HashMap, which displays its key-value pairs:

Step to the end to see how ages.put("Alice", 26) updates the value for an existing key instead of adding a new pair.

Reading Keyboard Input with Scanner

Java programs that read text input using java.util.Scanner now work. On the live site, whenever a Scanner read (e.g., nextInt() or nextLine()) needs input, the visualizer prompts the user to type a value. Then a "User input processed so far" display above the program output shows which portion of the input has been consumed at each execution step: consumed text appears in gray with strikethrough, and remaining text is in black. Inputs are saved so users can edit and re-use them without retyping, and they're preserved in shareable links. (Note that taking Scanner input from a File doesn't work yet, but it's coming soon – file input is a natural extension of this standard-input support, and it's currently under active development and testing.)

These step-by-step visualizations can demystify Scanner behaviors that often confuse students. Here is the classic nextInt()/nextLine() bug:

Here after the user enters "25", they never get a chance to enter their name. Why? Step through to see how sc.nextInt() consumes only the digits "25" from the input, so the following sc.nextLine() call consumes the leftover newline character and immediately returns an empty string for name instead of pausing to ask for more input. The "User input processed so far" display shows exactly what the program has consumed from stdin at every step.


Scanner's hasNext family of lookahead methods works too, which is how many courses teach reading a sequence of values when you don't know in advance how many will arrive. This loop keeps reading ints until the next token isn't one, then averages what it read:

At the current step (Step 11), the loop is in its second iteration: sc.nextInt() just consumed "20", so n is 20 and sum is about to grow from 10 to 30. Meanwhile "done" sits unconsumed at the end of the input display, and it stays that way to the very end of execution: hasNextInt() peeks at the next token to decide whether the loop should keep going, but never consumes it. When the loop finally reaches that non-numeric token, hasNextInt() returns false and execution moves on to compute the average. Sentinel-terminated input, type-checking lookahead, and Scanner's token-by-token consumption model are all visible in this one small example. (nextDouble(), next(), and mixed-type reads all work as well.)

Recursion

Stack frame visualizations are also useful for demonstrating recursion. Here is the classic factorial example:

You can also visualize more complex recursion, such as these examples in the CSAwesome e-textbook. Here is their binary search example:

Exception Handling

Visualizations can also help students understand unusual control flow during exception handling. This is especially useful when exceptions occur across different method calls and when there are try, catch, and finally blocks. Here's a small example:

Please Help Spread The Word!

The Java visualizer in Python Tutor can help your students understand and debug a variety of code that they encounter in introductory or intermediate-level courses.

Feel free to share this direct link in relevant course materials, chat groups, mailing lists, discussion forums, social media, or anywhere else: