STEPPER_REGISTRY.register("java-17/classes-and-objects", {
label: 'Classes and objects',
panelConfig: {
stack: true,
heap: true,
custom: false,
legend: [
{ color:'#1d5799', border:'#9dc0e8', label:'Currently running frame' },
{ color:'#3d3490', border:'#b0a8e0', label:'Object instance (heap)' },
{ color:'#a0540a', border:'#e8c070', label:'Just assigned or created' },
{ color:'#095e40', border:'#90d4b8', label:'Holding a value' },
]
},
scenarios: [
{
label: "What is a class?",
lines: [
{ code: [['tok-kw','class '],['tok-cls','Cat'],['tok-op',' {']] },
{ code: [['',' '],['tok-type','String '],['tok-field','name'],['tok-op',';']] },
{ code: [['',' '],['tok-type','int '],['tok-field','age'],['tok-op',';']] },
{ blank: true },
{ code: [['',' '],['tok-cls','Cat'],['tok-op','('],['tok-type','String '],['tok-op','n, '],['tok-type','int '],['tok-op','a) {']] },
{ code: [['',' '],['tok-field','name'],['tok-op',' = n;']] },
{ code: [['',' '],['tok-field','age'],['tok-op',' = a;']] },
{ code: [['',' '],['tok-op','}']] },
{ blank: true },
{ code: [['',' '],['tok-kw','void '],['tok-meth','meow'],['tok-op','() {']] },
{ code: [['',' '],['tok-op','System.out.println('],['tok-field','name'],['tok-op',' + '],['tok-str','" says: Meow!"'],['tok-op',');']] },
{ code: [['',' '],['tok-op','}']] },
{ code: [['tok-op','}']] },
],
steps: [
{ line:0, stack:[], heap:[], expl:{label:'A class is a blueprint', text:'class Cat defines a blueprint. It describes what data a Cat holds and what it can do. No actual Cat exists yet.'} },
{ line:1, stack:[], heap:[], expl:{label:'Fields: the data', text:'String name is a field -- a piece of data every Cat will have. Fields describe the state of an object.'} },
{ line:2, stack:[], heap:[], expl:{label:'Another field', text:'int age is a second field. Every Cat object will have its own name and its own age.'} },
{ line:4, stack:[], heap:[], expl:{label:'The constructor', text:'Cat(String n, int a) is the constructor. It has the same name as the class. Java runs it automatically when you create a new Cat.'} },
{ line:5, stack:[], heap:[], expl:{label:'Setting the fields', text:'name = n stores the value passed in. The constructor job is to set up the fields so the object starts in a valid state.'} },
{ line:9, stack:[], heap:[], expl:{label:'A method: the behavior', text:'meow() is a method -- something a Cat can do. It uses name, which belongs to whichever Cat object calls it.'} },
{ line:12, stack:[], heap:[], expl:{label:'The blueprint is complete', text:'The class definition is done. But we still have zero Cat objects. A blueprint by itself does nothing -- we need to build from it.'} },
]
},
{
label: "Creating an instance",
lines: [
{ code: [['tok-kw','class '],['tok-cls','Cat'],['tok-op',' { ... }']] },
{ blank: true },
{ code: [['tok-kw','class '],['tok-cls','Main'],['tok-op',' {']] },
{ code: [['',' '],['tok-kw','static '],['tok-kw','void '],['tok-meth','main'],['tok-op','(String[] args) {']] },
{ code: [['',' '],['tok-cls','Cat '],['tok-op','mittens = '],['tok-kw','new '],['tok-cls','Cat'],['tok-op','('],['tok-str','"Mittens"'],['tok-op',', '],['tok-num','3'],['tok-op',');']] },
{ code: [['',' '],['tok-op','}']] },
{ code: [['tok-op','}']] },
],
steps: [
{ line:2, stack:[], heap:[], expl:{label:'Two classes', text:'We have our Cat blueprint. Now we write a Main class with a main() method to actually run code.'} },
{ line:3, stack:[{name:'main()',vars:[],active:true}], heap:[], expl:{label:'main() starts', text:'Java enters main(). A frame is pushed onto the call stack.'} },
{ line:4, stack:[{name:'main()',vars:[{n:'mittens',v:'null',fresh:false,cls:false}],active:true}], heap:[], expl:{label:'The new keyword', text:'new Cat("Mittens", 3) tells Java: build a real Cat object right now using the blueprint. Java allocates space in memory for it.'} },
{ line:4, stack:[{name:'Cat()',vars:[{n:'n',v:'"Mittens"',fresh:true},{n:'a',v:'3',fresh:true}],active:true},{name:'main()',vars:[{n:'mittens',v:'null',fresh:false}],active:false}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'null',fresh:false},{n:'age',v:'0',fresh:false}],active:false}], expl:{label:'Constructor runs', text:'Java calls the Cat constructor. A new frame is pushed. The object exists in memory but its fields are not set yet.'} },
{ line:4, stack:[{name:'Cat()',vars:[{n:'n',v:'"Mittens"',fresh:false},{n:'a',v:'3',fresh:false}],active:true},{name:'main()',vars:[{n:'mittens',v:'null',fresh:false}],active:false}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:true},{n:'age',v:'3',fresh:true}],active:true}], expl:{label:'Fields are set', text:'The constructor sets name = "Mittens" and age = 3 on the new object. The object now has its own data.'} },
{ line:4, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:true,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false}], expl:{label:'Variable holds a reference', text:'The constructor finishes, its frame is popped. The variable mittens now holds a reference pointing to the Cat object in memory -- not the object itself.'} },
{ line:5, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false}], expl:{label:'main() ends', text:'main() closes. The object in memory goes away with it. Program ends.'} },
]
},
{
label: "Two instances",
lines: [
{ code: [['tok-kw','class '],['tok-cls','Cat'],['tok-op',' { ... }']] },
{ blank: true },
{ code: [['tok-kw','class '],['tok-cls','Main'],['tok-op',' {']] },
{ code: [['',' '],['tok-kw','static '],['tok-kw','void '],['tok-meth','main'],['tok-op','(String[] args) {']] },
{ code: [['',' '],['tok-cls','Cat '],['tok-op','mittens = '],['tok-kw','new '],['tok-cls','Cat'],['tok-op','('],['tok-str','"Mittens"'],['tok-op',', '],['tok-num','3'],['tok-op',');']] },
{ code: [['',' '],['tok-cls','Cat '],['tok-op','mochi = '],['tok-kw','new '],['tok-cls','Cat'],['tok-op','('],['tok-str','"Mochi"'],['tok-op',', '],['tok-num','5'],['tok-op',');']] },
{ code: [['',' '],['tok-op','}']] },
{ code: [['tok-op','}']] },
],
steps: [
{ line:3, stack:[{name:'main()',vars:[],active:true}], heap:[], expl:{label:'main() starts', text:'main() begins. We are going to create two separate Cat objects from the same blueprint.'} },
{ line:4, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:true,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:true},{n:'age',v:'3',fresh:true}],active:false}], expl:{label:'First instance created', text:'new Cat("Mittens", 3) builds a Cat object in memory. mittens holds a reference to it. This object has its own name and age.'} },
{ line:5, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:true,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:true},{n:'age',v:'5',fresh:true}],active:false}], expl:{label:'Second instance created', text:'new Cat("Mochi", 5) builds a completely separate object. mochi holds a reference to it. Same blueprint, totally independent data.'} },
{ line:5, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:false,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:false},{n:'age',v:'5',fresh:false}],active:false}], expl:{label:'Two objects, one blueprint', text:'Both mittens and mochi are Cats, but they are distinct objects. Changing Mittens name would have zero effect on Mochi. Each instance owns its data.'} },
{ line:6, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:false,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:false},{n:'age',v:'5',fresh:false}],active:false}], expl:{label:'Done', text:'main() closes. Program ends.'} },
]
},
{
label: "Calling instance methods",
lines: [
{ code: [['tok-kw','class '],['tok-cls','Cat'],['tok-op',' { ... }']] },
{ blank: true },
{ code: [['tok-kw','class '],['tok-cls','Main'],['tok-op',' {']] },
{ code: [['',' '],['tok-kw','static '],['tok-kw','void '],['tok-meth','main'],['tok-op','(String[] args) {']] },
{ code: [['',' '],['tok-cls','Cat '],['tok-op','mittens = '],['tok-kw','new '],['tok-cls','Cat'],['tok-op','('],['tok-str','"Mittens"'],['tok-op',', '],['tok-num','3'],['tok-op',');']] },
{ code: [['',' '],['tok-cls','Cat '],['tok-op','mochi = '],['tok-kw','new '],['tok-cls','Cat'],['tok-op','('],['tok-str','"Mochi"'],['tok-op',', '],['tok-num','5'],['tok-op',');']] },
{ blank: true },
{ code: [['',' '],['tok-op','mittens.'],['tok-meth','meow'],['tok-op','();'],['',' '],['tok-cmt','// "Mittens says: Meow!"']] },
{ code: [['',' '],['tok-op','mochi.'],['tok-meth','meow'],['tok-op','();'],['',' '],['tok-cmt','// "Mochi says: Meow!"']] },
{ code: [['',' '],['tok-op','}']] },
{ code: [['tok-op','}']] },
],
steps: [
{ line:3, stack:[{name:'main()',vars:[],active:true}], heap:[], expl:{label:'main() starts', text:'main() begins.'} },
{ line:4, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:true,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false}], expl:{label:'Mittens created', text:'mittens now points to a Cat object with name "Mittens" and age 3.'} },
{ line:5, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:true,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:false},{n:'age',v:'5',fresh:false}],active:false}], expl:{label:'Mochi created', text:'mochi now points to a separate Cat object with name "Mochi" and age 5. Two objects, both from the same class.'} },
{ line:7, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:false,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:false},{n:'age',v:'5',fresh:false}],active:false}], expl:{label:'Calling meow() on mittens', text:'mittens.meow() -- the dot means "call this method on that specific object." Java follows the reference in mittens to find the right object.'} },
{ line:7, stack:[{name:'meow() on @Cat1',vars:[],active:true},{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:false,cls:true}],active:false}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:true},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:false},{n:'age',v:'5',fresh:false}],active:false}], expl:{label:'meow() runs on Mittens', text:'Java enters meow() with the Mittens object as the target. Inside the method, name refers to Mittens own field -- "Mittens". It prints "Mittens says: Meow!"'} },
{ line:8, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:false,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:false},{n:'age',v:'5',fresh:false}],active:false}], expl:{label:'Now calling meow() on mochi', text:'mochi.meow() calls the exact same method code, but this time on the Mochi object. Java follows the reference in mochi.'} },
{ line:8, stack:[{name:'meow() on @Cat2',vars:[],active:true},{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:false,cls:true}],active:false}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:false},{n:'age',v:'5',fresh:false}],active:true}], expl:{label:'Same method, different object', text:'Now name inside meow() refers to Mochi name -- "Mochi". It prints "Mochi says: Meow!" One method definition, two different results depending on which object called it.'} },
{ line:9, stack:[{name:'main()',vars:[{n:'mittens',v:'@Cat1',fresh:false,cls:true},{n:'mochi',v:'@Cat2',fresh:false,cls:true}],active:true}], heap:[{id:'c1',type:'Cat',ref:'@Cat1',fields:[{n:'name',v:'"Mittens"',fresh:false},{n:'age',v:'3',fresh:false}],active:false},{id:'c2',type:'Cat',ref:'@Cat2',fields:[{n:'name',v:'"Mochi"',fresh:false},{n:'age',v:'5',fresh:false}],active:false}], expl:{label:'Done', text:'main() finishes. This is the core idea: one class, many independent instances, each with its own state.'} },
]
}
]
});