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.
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.
The Java visualizer can illustrate most object-oriented programming concepts that are taught in introductory and intermediate-level classes (no pun intended), including:
super()
callsthis
referenceThe 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
construtor 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.
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.
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
.
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 selecting "render all objects on the heap" 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.
Similarly, when this mode is on, wrapped
objects
(e.g., instances of Boolean
, Integer
, or Float
) display standalone
with references pointing to them, unlike primitives such as bool
,
int
, and float
, which appear inline within the stack frame:
We've already seen one-dimensional arrays in some of the above examples. Here's a multi-dimensional array, which is represented in Java as an array of arrays:
[Future work: implementing similar visualizations for ArrayList
,
LinkedList
, and other Java Collections
classes]
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:
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:
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: