Home
3Constructors, Overloading & Reference Passing
3

Lab Manual III · CSE 202

Constructors, Overloading & Reference Passing

Give a Rectangle class default, parameterised and copy constructors chained with this(…), overload area() and describe(), and find out exactly what Java passes to a method.

← → move between slides · F full screen · M slide list

Before we start

Why this lab

You pass a rectangle to a method and it comes back twice as big. You pass a number and it comes back unchanged. Why the difference?

Java always passes a copy: a copy of the number, or a copy of the reference to the object. This lab proves it with a Rectangle class.

Lab 3 of 10

Lab at a glance

After this lab you can

  • Define default, parameterised and copy constructors
  • Chain constructors with this(…) to avoid repeated code
  • Overload methods by changing the parameter list
  • Explain how the compiler picks an overload
  • Distinguish passing primitives from passing object references
  • Explain why reassigning a parameter does not affect the caller

Before you start

  • Lab 2: classes, objects, constructors, encapsulation
  • Primitive vs reference types

Plan for the session

  1. Theory recap
  2. Walk through the program, section by section, and run it
  3. In-lab tasks, shown to the instructor
  4. Report exercises at home

Theory 1 of 5

Default, parameterised and copy constructors

public Rectangle()                         // default: 1 x 1, White
public Rectangle(double side)              // square
public Rectangle(double w, double h)       // rectangle
public Rectangle(double w, double h, String colour)
public Rectangle(Rectangle other)          // copy
  • Java writes a no-argument constructor only if you write none yourself.
  • A copy constructor builds a new, independent object with the same field values.

Theory 2 of 5

Constructor chaining with this(…)

  • Simpler constructors delegate to the full one, so validation lives in one place.

Constructor chaining with this(…)

public Rectangle(double side) {
    this(side, side, "White");   // must be the first statement
}

Theory 3 of 5

Method overloading

public double area()                                   // own fields
public double area(double scaleFactor)                 // scaled
public static double area(double width, double height) // any size
  • Same name, different parameter lists (number, type or order).
  • Resolved by the compiler at compile time.
  • A different return type or access modifier alone is not an overload — compile error.

Theory 4 of 5

Passing a primitive

  • The method gets a copy of the value; changing the copy cannot touch the caller’s variable.

Passing a primitive

static void passPrimitive(double side) { side = side * 2; }

double side = 10.0;
passPrimitive(side);   // side is still 10.0

Theory 5 of 5

Passing an object reference

  • The method gets a copy of the reference: both point to the same object, so r.scale(2.0) is visible to the caller.
  • Reassigning the parameter (r = new Rectangle(…)) only moves the copy; the caller still sees the original.
  • So Java is always pass-by-value — for objects the value is a reference.

Implementation design

Program design

Implementation design

Execution flow

  1. Build rectangles with each of the five constructors
  2. Copy r3, change r3’s colour and show the copy is unchanged
  3. Call the three area() overloads and the two describe() overloads
  4. Pass a primitive: unchanged after the call
  5. Pass a reference and scale: the change is visible
  6. Reassign the parameter: the caller’s object is untouched

All constructors funnel into the full one, which validates width and height.

Code walkthrough 1 of 13 · Rectangle.java

Constructor chaining

public Rectangle() {
    this(1.0, 1.0, "White");
}

public Rectangle(double side) {
    this(side, side, "White");
}

public Rectangle(double width, double height) {
    this(width, height, "White");
}
  • Each simple constructor passes defaults to the full one with this(…).

Code walkthrough 2 of 13 · Rectangle.java

The full constructor validates

public Rectangle(double width, double height, String colour) {
    if (width > 0) {
        this.width = width;
    } else {
        this.width = 1.0;
    }
    if (height > 0) {
        this.height = height;
    } else {
        this.height = 1.0;
    }
    this.colour = colour;
}
  • All checks live in one place, whichever constructor was called.

Code walkthrough 3 of 13 · Rectangle.java

The copy constructor

// Copy constructor
public Rectangle(Rectangle other) {
    this(other.width, other.height, other.colour);
}
  • Builds a new object from another one’s values; it can read other’s private fields because it is the same class.

Code walkthrough 4 of 13 · Rectangle.java

Three overloads of area()

public double area() {
    return width * height;
}

public double area(double scaleFactor) {
    return area() * scaleFactor;
}

public static double area(double width, double height) {
    return width * height;
}
  • Same name, different parameters.
  • The static one needs no object: Rectangle.area(3, 8).

Code walkthrough 5 of 13 · Rectangle.java

describe() overloads and scale()

public String describe() {
    return String.format("Rectangle[%.2f x %.2f, colour=%s, area=%.2f]",
            width, height, colour, area());
}

public String describe(String prefix) {
    return prefix + ": " + describe();
}

public void scale(double factor) {

Code walkthrough 5 of 13 · Rectangle.java

describe() overloads and scale() (cont.)

    if (factor > 0) {
        width = width * factor;
        height = height * factor;
    }
}
  • describe(prefix) reuses describe().
  • scale() changes the object itself.

Code walkthrough 6 of 13 · Lab3Demo.java

Passing a primitive

static void passPrimitive(double side) {
    System.out.println("Inside passPrimitive: side before = " + side);
    side = side * 2;
    System.out.println("Inside passPrimitive: side after  = " + side);
}
  • side here is a copy; doubling it cannot change the caller’s variable.

Code walkthrough 7 of 13 · Lab3Demo.java

Passing a reference

static void passReference(Rectangle rectangle) {
    System.out.println("Inside passReference before scaling: " + rectangle.describe());
    rectangle.scale(2.0);
    System.out.println("Inside passReference after scaling : " + rectangle.describe());
}
  • The parameter points to the caller’s object, so scaling is visible after the call.

Code walkthrough 8 of 13 · Lab3Demo.java

Reassigning the parameter

static void tryReassignReference(Rectangle rectangle) {
    rectangle = new Rectangle(99.0, 99.0, "Invisible");
    System.out.println("Inside tryReassignReference: " + rectangle.describe());
}
  • Only the local copy of the reference moves to the new object.

Code walkthrough 9 of 13 · Lab3Demo.java

Using every constructor

// 1. Constructor Variants
System.out.println("=== Constructor Variants ===");
Rectangle r0 = new Rectangle();
System.out.println("Default constructor    : " + r0.describe());
Rectangle r1 = new Rectangle(5.0);
System.out.println("Square constructor     : " + r1.describe());
Rectangle r2 = new Rectangle(4.0, 7.0);
System.out.println("Two-arg constructor    : " + r2.describe());
Rectangle r3 = new Rectangle(3.0, 6.0, "Blue");
System.out.println("Full constructor       : " + r3.describe());
Rectangle r4 = new Rectangle(r3);
System.out.println("Copy constructor       : " + r4.describe());
  • The compiler picks a constructor by the arguments.

Code walkthrough 10 of 13 · Lab3Demo.java

Copies are independent

// 2. Copy Constructor Independence
System.out.println("\n=== Copy Constructor Independence ===");
r3.setColour("Green");
System.out.println("Original after change  : " + r3.describe());
System.out.println("Copy remains unchanged : " + r4.describe());
  • Changing r3 does not affect r4.

Code walkthrough 11 of 13 · Lab3Demo.java

Calling the overloads

// 3. Method Overloading
System.out.println("\n=== Method Overloading: area() ===");
Rectangle rect = new Rectangle(6.0, 4.0, "Yellow");
System.out.printf("area()          = %.2f%n", rect.area());
System.out.printf("area(1.5)       = %.2f%n", rect.area(1.5));
System.out.printf("area(3.0, 8.0)  = %.2f%n", Rectangle.area(3.0, 8.0));

System.out.println("\n=== Method Overloading: describe() ===");
System.out.println(rect.describe());
System.out.println(rect.describe("Main rectangle"));
  • Arguments decide which area() and describe() run.

Code walkthrough 12 of 13 · Lab3Demo.java

Pass-by-value: a primitive

// 4. Passing Primitive Value
System.out.println("\n=== Passing a Primitive Value ===");
double side = 10.0;
System.out.println("Before method call: side = " + side);
passPrimitive(side);
System.out.println("After method call : side = " + side);
  • side is still 10.0 after the call: the method doubled a copy.

Code walkthrough 13 of 13 · Lab3Demo.java

Pass-by-value: references

// 5. Passing Object Reference
System.out.println("\n=== Passing an Object Reference ===");
Rectangle ref = new Rectangle(5.0, 5.0, "Blue");
System.out.println("Before method call: " + ref.describe());
passReference(ref);
System.out.println("After method call : " + ref.describe());

// 6. Reassigning Reference Parameter
System.out.println("\n=== Reassigning Reference Parameter ===");
Rectangle original = new Rectangle(2.0, 3.0, "Pink");
System.out.println("Before method call: " + original.describe());
tryReassignReference(original);
System.out.println("After method call : " + original.describe());
  • Mutating through the reference: the change is visible.
  • Reassigning the reference: the caller still sees the Pink 2 × 3 rectangle.

Step through the code · 1 of 3

Trace: constructor chaining with this(…)

The stack grows by one frame when one constructor hands over to another.

Step through the code · 1 of 3

Trace: constructor chaining with this(…) (cont.)

Step through the code · 2 of 3

Trace: pass-by-value, three ways

Before stepping, predict which call changes something the caller can see.

Step through the code · 2 of 3

Trace: pass-by-value, three ways (cont.)

Step through the code · 3 of 3

Trace: copy or alias?

r4 is a copy made by the copy constructor; r5 is just another reference. Which one changes when r3 does?

Step through the code · 3 of 3

Trace: copy or alias? (cont.)

For reference · 2 files

The complete program

public class Rectangle {

    private double width;
    private double height;
    private String colour;

    public Rectangle() {
        this(1.0, 1.0, "White");
    }

    public Rectangle(double side) {
        this(side, side, "White");
    }

    public Rectangle(double width, double height) {
        this(width, height, "White");
    }

    public Rectangle(double width, double height, String colour) {
        if (width > 0) {
            this.width = width;
        } else {
            this.width = 1.0;
        }
        if (height > 0) {
            this.height = height;
        } else {
            this.height = 1.0;
        }
        this.colour = colour;
    }

    // Copy constructor
    public Rectangle(Rectangle other) {
        this(other.width, other.height, other.colour);
    }

    public double area() {
        return width * height;
    }

    public double area(double scaleFactor) {
        return area() * scaleFactor;
    }

    public static double area(double width, double height) {
        return width * height;
    }

    public String describe() {
        return String.format("Rectangle[%.2f x %.2f, colour=%s, area=%.2f]",
                width, height, colour, area());
    }

    public String describe(String prefix) {
        return prefix + ": " + describe();
    }

    public void scale(double factor) {
        if (factor > 0) {
            width = width * factor;
            height = height * factor;
        }
    }

    public double getWidth() { return width; }
    public double getHeight() { return height; }
    public String getColour() { return colour; }
    public void setColour(String colour) { this.colour = colour; }

    @Override
    public String toString() {
        return describe();
    }
}
public class Lab3Demo {

    static void passPrimitive(double side) {
        System.out.println("Inside passPrimitive: side before = " + side);
        side = side * 2;
        System.out.println("Inside passPrimitive: side after  = " + side);
    }

    static void passReference(Rectangle rectangle) {
        System.out.println("Inside passReference before scaling: " + rectangle.describe());
        rectangle.scale(2.0);
        System.out.println("Inside passReference after scaling : " + rectangle.describe());
    }

    static void tryReassignReference(Rectangle rectangle) {
        rectangle = new Rectangle(99.0, 99.0, "Invisible");
        System.out.println("Inside tryReassignReference: " + rectangle.describe());
    }

    public static void main(String[] args) {

        // 1. Constructor Variants
        System.out.println("=== Constructor Variants ===");
        Rectangle r0 = new Rectangle();
        System.out.println("Default constructor    : " + r0.describe());
        Rectangle r1 = new Rectangle(5.0);
        System.out.println("Square constructor     : " + r1.describe());
        Rectangle r2 = new Rectangle(4.0, 7.0);
        System.out.println("Two-arg constructor    : " + r2.describe());
        Rectangle r3 = new Rectangle(3.0, 6.0, "Blue");
        System.out.println("Full constructor       : " + r3.describe());
        Rectangle r4 = new Rectangle(r3);
        System.out.println("Copy constructor       : " + r4.describe());

        // 2. Copy Constructor Independence
        System.out.println("\n=== Copy Constructor Independence ===");
        r3.setColour("Green");
        System.out.println("Original after change  : " + r3.describe());
        System.out.println("Copy remains unchanged : " + r4.describe());

        // 3. Method Overloading
        System.out.println("\n=== Method Overloading: area() ===");
        Rectangle rect = new Rectangle(6.0, 4.0, "Yellow");
        System.out.printf("area()          = %.2f%n", rect.area());
        System.out.printf("area(1.5)       = %.2f%n", rect.area(1.5));
        System.out.printf("area(3.0, 8.0)  = %.2f%n", Rectangle.area(3.0, 8.0));

        System.out.println("\n=== Method Overloading: describe() ===");
        System.out.println(rect.describe());
        System.out.println(rect.describe("Main rectangle"));

        // 4. Passing Primitive Value
        System.out.println("\n=== Passing a Primitive Value ===");
        double side = 10.0;
        System.out.println("Before method call: side = " + side);
        passPrimitive(side);
        System.out.println("After method call : side = " + side);

        // 5. Passing Object Reference
        System.out.println("\n=== Passing an Object Reference ===");
        Rectangle ref = new Rectangle(5.0, 5.0, "Blue");
        System.out.println("Before method call: " + ref.describe());
        passReference(ref);
        System.out.println("After method call : " + ref.describe());

        // 6. Reassigning Reference Parameter
        System.out.println("\n=== Reassigning Reference Parameter ===");
        Rectangle original = new Rectangle(2.0, 3.0, "Pink");
        System.out.println("Before method call: " + original.describe());
        tryReassignReference(original);
        System.out.println("After method call : " + original.describe());
    }
}

Copy or download each file, save it under the same name, then compile and run: javac *.java → java Lab3Demo

Implementation

Expected output

Compare the last three sections: the primitive is unchanged, the scaled object changed, the reassigned parameter left the original alone.

Interactive 1 of 1

Which overload does the compiler pick?

In-lab task 1 of 3

Task 1: Overloaded perimeter() methods

Add three overloads to Rectangle and call each from main.
Requirements
Show a hint
Mirror the three area() overloads; the scaled version can call perimeter().

In-lab task 2 of 3

Task 2: Copy constructor independence test

Prove that a copy is a separate object.
Requirements
Show a hint
The copy constructor copied the values of width and height into a new object.

In-lab task 3 of 3

Task 3: Design a Circle class

Constructors, overloads and reference passing with circles.
Requirements
Show a hint
Chain the constructors to Circle(double radius, String colour) and validate radius there.

In-lab tasks

Show your work during the lab

For each task, show the instructor:

  • the source code
  • a successful compilation
  • the running output
  • a short explanation of the logic you used

Lab report · exercise 1 of 2

Geometric shape library

Classes Triangle and Cylinder with a driver class.
Requirements

Lab report · exercise 2 of 2

Student record mutation study

A StudentRecord with a double[] grades array, to study copying and mutation.
Requirements

Lab report

Writing the lab report

  • Problem statement and your design (classes, fields, methods)
  • Complete source code, formatted and commented
  • Sample output or screenshots for normal and edge cases
  • A short discussion: what each OOP concept did in your program

Academic integrity

Copying from the internet, classmates, seniors or any other unauthorized source is strictly prohibited. Full marks may be deducted if plagiarism, copied work or academic dishonesty is detected. Complete the tasks, implementation, output analysis and report yourself.

Discussion and analysis

Viva questions (1 of 2)

Why must this(…) be the first statement in a constructor?
Show the answer
The object must be fully initialised by the delegated constructor before any other code runs; Java enforces it at compile time.
Can two methods differ only in return type?
Show the answer
No. The call calculate() would be ambiguous, so it is a compile error.

Discussion and analysis

Viva questions (2 of 2)

Is Java pass-by-reference for objects?
Show the answer
No. It passes the reference by value: mutation through the copy is visible, but reassigning the parameter is not.
When does a copy constructor fail to give independence?
Show the answer
When a field is itself a reference (such as an array) and only the reference is copied — a shallow copy. Copy the array too for a deep copy.

Check yourself

Quiz

Summary

Summary

  • Default, parameterised and copy constructors give flexible object creation.
  • Chain constructors with this(…) so validation lives in one place.
  • Overloads share a name but differ in parameters; the compiler chooses.
  • Java passes copies: of values for primitives, of references for objects.
  • Mutation through a reference is visible; reassigning the parameter is not.