Building objects properly
Setting fields one by one after new is clumsy and error-prone. Forget one line and lesson 5-1's defaults leave a null hiding in the object, waiting to crash some later method.
Real codebases therefore treat "an object is valid the instant it exists" as a rule. A constructor is a special method that runs during new and sets the object up in one step. It has the same name as the class and no return type at all:
class Point { int x; int y; Point(int x, int y) { this.x = x; this.y = y; } } Point p = new Point(3, 4);
this means the object being built, like Python's self, so this.x = x copies the parameter into the field of the same name. The whole construct is the direct translation of Python's __init__.
Once you define any constructor, callers must use it. Plain new Point() stops compiling unless you also define a no-argument constructor, which the next section covers.
A Point that can measure itself
A constructor plus an instance method that computes from the fields.
public class Main { public static void main(String[] args) { Point p = new Point(3, 4); System.out.println("(" + p.x + ", " + p.y + ")"); System.out.println("distance: " + p.distanceFromOrigin()); } } class Point { int x; int y; Point(int x, int y) { this.x = x; this.y = y; } double distanceFromOrigin() { return Math.sqrt(x * x + y * y); } }
Output
(3, 4) distance: 5.0
The distance is √(3² + 4²) = 5, the 3-4-5 triangle, which is a convenient way to see that the method really read both fields.
distanceFromOrigin takes no parameters and still has data to work with, since the fields are part of the object. That is the whole difference between an instance method and a static one, which would need the coordinates handed in.
Constructors overload as well
A class may define several constructors with different parameter lists, since the overloading rule from lesson 4-2 applies to them too. A common pattern is a convenience constructor that fills in defaults by delegating to the full one with this(...):
class Point { int x; int y; Point(int x, int y) { this.x = x; this.y = y; } Point() { this(0, 0); // the origin } }
this(0, 0) calls the other constructor and must be the first line of the body, because the object has to be initialized once before anything else runs.
One warning is worth its weight here. Java supplies a free no-argument constructor only while you define none yourself, so the moment you write Point(int x, int y) the plain new Point() stops compiling unless you add it back explicitly, exactly as above.
Why the body says this.x = x
The parameter x shadows the field x, so inside the constructor the bare name x refers to the nearest declaration, which is the parameter. this.x explicitly names the object's field.
| Written | Means |
|---|---|
x | the parameter |
this.x | the field on this object |
Plain x = x would assign the parameter to itself. It compiles without complaint and leaves the field sitting at its default of 0, which is a classic silent bug.
Naming the parameter something different, such as startX, avoids the shadowing entirely. Most Java code keeps the matching names and the this. prefix anyway, because the signature then reads exactly like the field list.
A Rectangle with two computed values
One constructor and two methods that derive results from the fields.
public class Main { public static void main(String[] args) { Rectangle r = new Rectangle(4.0, 5.0); System.out.println("area: " + r.area()); System.out.println("perimeter: " + r.perimeter()); } } class Rectangle { double width; double height; Rectangle(double width, double height) { this.width = width; this.height = height; } double area() { return width * height; } double perimeter() { return 2 * (width + height); } }
Output
area: 20.0 perimeter: 18.0
Both methods return double and take no parameters, reading width and height straight from the object. The results print with a trailing .0 because they are doubles, not ints.
Neither value is stored as a field, which is deliberate. Area and perimeter are computed on demand from the two numbers that define the rectangle, so they can never disagree with the size the way a stale stored copy could.