The Object You Already Know
Here’s a secret most web students discover too late: you have been working with objects since the very first HTML tag you ever wrote.
When you write <img id="demoIcon" src="bolt.svg" alt="bolt icon">, the browser doesn’t just park that tag somewhere and forget about it. It creates a full object — specifically an instance of a built-in class called HTMLImageElement. Every HTML attribute (src, alt, width, height) becomes a property of that object. If you were to write this as a Java class, those attributes would be private fields.
When you later write img.src = 'flash.svg' in JavaScript, it looks like simple variable assignment. It is not. It calls a setter that the browser defined for you — one that validates the URL, cancels the previous network request, and kicks off a new image load. The assignment syntax just hides all of that.
The table to the right maps every <img> attribute you already know to its Java equivalent. This is the same object — just seen through two different lenses.
The <img> Element as a Java Class
| HTML Attribute | Java Field | Java Setter |
|---|---|---|
src |
private String src | setSrc(String s) |
alt |
private String alt | setAlt(String a) |
width |
private int width | setWidth(int w) |
height |
private int height | setHeight(int h) |
id |
private String id | setId(String i) |
Private by Default
Java declares fields private explicitly. The browser’s HTMLImageElement does the same thing implicitly — you cannot reach into its internal storage directly. You can only change src through the interface it exposes.
Assignment Syntax Hides the Gate
In JavaScript, img.src = 'flash.svg' looks like direct field access — it is not. It calls a setter the browser defined. Java is more honest: you write img.setSrc("flash.svg") and there is no question that a method call is happening.
Two Ways to Set a Property
Same object. Same goal. Two languages with very different opinions about who controls the gate.
The DOM exposes object properties through assignment syntax. Under the hood the browser calls a setter — but it looks like a plain =. When you write your own class, the set keyword makes that explicit.
// In HTML: <img id="demoIcon" src="bolt.svg"> // The img IS an object (HTMLImageElement). const img = document.getElementById('demoIcon'); // Looks like assignment — it's a SETTER CALL: // (null guard: demoIcon only exists on the TNT Home page) if (img) { img.src = 'images/flash.svg'; // ← setter! } // The browser cancels the old request and starts a new one. // Write your own class with an explicit setter: class TNTImage { constructor(src, alt) { this._src = src; this._alt = alt; } set src(value) { // 'set' keyword = setter this._src = value; } get src() { return this._src; } } const myImg = new TNTImage('bolt.svg', 'bolt'); myImg.src = 'flash.svg'; // calls setter — still looks like = // ⚠ JS can't fully enforce private. A determined // developer could still write: myImg._src = 'x';
private enforces it”Java makes encapsulation explicit. Fields declared private are physically locked — the compiler refuses to compile any code that accesses them from outside the class. The setter is the only door, and it is not optional.
public class TNTImage { private String src; // padlocked — no outside access private String alt; public TNTImage(String src, String alt) { this.src = src; this.alt = alt; } // You write the gate. Java enforces it. public void setSrc(String src) { if (src == null || src.isEmpty()) throw new IllegalArgumentException("src required"); this.src = src; // side effects go here } public String getSrc() { return src; } } /* Usage: TNTImage myImg = new TNTImage("bolt.svg", "bolt"); myImg.setSrc("flash.svg"); // explicit method call required // myImg.src = "flash.svg"; ← COMPILE ERROR: src is private */
Run it in JDoodle — File 1: TNTImage.java (copy above) • File 2: this driver
// TNTImageDriver.java
// JDoodle multi-file: paste TNTImage.java as File 1, this file as File 2
public class TNTImageDriver {
public static void main(String[] args) {
System.out.println("=== TNTImage Setter Demo ===\n");
// Constructor sets the initial src — getter reads it back
TNTImage bolt = new TNTImage("bolt.svg", "lightning bolt");
System.out.println("getSrc(): " + bolt.getSrc());
// setSrc() is the ONLY legal way to change src from outside
bolt.setSrc("flash.svg");
System.out.println("After setSrc: " + bolt.getSrc());
// Setter validates — empty string is rejected at the gate
System.out.println("\n--- Validation in the setter ---");
try {
bolt.setSrc("");
} catch (IllegalArgumentException e) {
System.out.println("Caught: " + e.getMessage());
}
System.out.println("src unchanged: " + bolt.getSrc());
// Circle demo — setRadius() triggers side-effect recalculation
System.out.println("\n=== Circle: Side-Effect Setter ===");
Circle c = new Circle();
c.setRadius(5.0);
System.out.printf("r=5.0 area=%7.3f circ=%7.3f%n",
c.getArea(), c.getCircumference());
c.setRadius(10.0);
System.out.printf("r=10.0 area=%7.3f circ=%7.3f%n",
c.getArea(), c.getCircumference());
System.out.println("\n--- Thanks for using our program! ---");
}//end main
}//end class TNTImageDriver
// Circle: non-public class can live in the same .java file as the driver
class Circle {
private double radius;
private double area;
private double circumference;
public void setRadius(double r) {
this.radius = r;
this.area = Math.PI * r * r; // side effect
this.circumference = 2 * Math.PI * r; // side effect
}
public double getRadius() { return radius; }
public double getArea() { return area; }
public double getCircumference() { return circumference; }
}//end class Circle
_src naming convention is a
gentleman’s agreement — a determined developer can still write myImg._src = 'x'
and bypass the setter entirely. Java’s private keyword is enforced by
the compiler at build time. Attempt direct access and Java
refuses to compile your program. The gate is not optional.
The Side Effect Problem
If a setter just stores a value, why not skip it and use a public field? The answer is side effects — the extra work that needs to happen automatically whenever a property changes.
Think about a Circle class with three properties: radius, area, and circumference. Change radius and the other two must update at the same moment. If you allow direct field access, a developer can change radius and forget to recalculate — now your object holds three numbers that contradict each other. The object is broken and it doesn’t know it.
The setter prevents that. It is the one and only place radius can change — so it is the right place to trigger the recalculation. The object stays internally consistent no matter what.
The same logic applies to img.src. When you change the source of an image, the browser doesn’t just store a new string. It cancels the in-flight network request for the old image and fires a new one for the new image. That is a side effect. If the browser let you write directly to an internal string field, none of that cleanup would happen — you would get the wrong image, or no image at all.
Circle class — with and without a setter
public class Circle { // public: no protection public double radius; public double area; public double circumference; } Circle c = new Circle(); c.radius = 5.0; // c.area is still 0.0 ← WRONG! // c.circumference still 0.0 ← WRONG! // Nothing updated the dependent fields.
public class Circle { private double radius; private double area; private double circumference; public void setRadius(double r) { this.radius = r; // side effects: auto-recalculate this.area = Math.PI * r * r; this.circumference = 2 * Math.PI * r; } // getters: getRadius, getArea, etc. } Circle c = new Circle(); c.setRadius(5.0); // area = 78.54 ✓ circumference = 31.42 ✓
The Setter That Started This Whole Conversation
Head back to the TNT home page. In the JavaScript in Action section,
click Toggle Icon. Watch the bolt swap from yellow to red–orange and back.
That is one line of JavaScript:
img.src = 'images/np_flash_639950_ED5861.svg'.
A setter call. You’ve been running it the whole time.
Languages at a Glance
| Feature | JavaScript | Java | Python 3 |
|---|---|---|---|
| Field access control | Convention only — _fieldName signals “private” but is not enforced |
Compiler-enforced — private keyword blocks all outside access |
Coming Later |
| Setter syntax | set propName(val) { } inside a class, or implicit via DOM |
public void setX(Type val) { } — must be written explicitly |
Coming Later |
| Getter syntax | get propName() { return ...; } |
public Type getX() { return x; } |
Coming Later |
| Side effects in setter | Yes — any code inside the set block runs on assignment |
Yes — any code inside the method body runs on each call | Coming Later |
| Can the setter be bypassed? | Yes — obj._field = val skips the setter entirely |
No — private is a compile-time barrier with no workaround |
Coming Later |
| Language philosophy | “We’re all adults here — trust the convention” | “Rules are rules — the compiler protects the contract” | Coming Later |
@property decorator handles getters and setters
elegantly — it sits between JavaScript’s convention and Java’s enforcement
in a very Pythonic way. It will be added to this page once your Python class reaches decorators.
When that comparison arrives, it will be worth the wait.