THINK FIRST·CODE LATER

← Java Programming
Chapter 11 · Week 4–5

Object References, Constructors, and Overloading

Before You Start: What You Must Be Able to Do

Before the questions, make sure you can: draw what happens in memory when an object is created and when one reference is assigned to another; explain why == and equals give different answers for objects; say exactly what a method can and cannot do to an object passed as an argument; write a default and a parameterised constructor with the correct heading; state when the compiler supplies a constructor for you and when it does not; use this(...) to chain constructors; and read a chained call such as a.getB().getName().

Object creation, step by step

Student s1 = new Student("Ali", 65.5);
  1. Student s1 creates a reference variable; on its own it refers to no object (a field holds null; a local variable must be assigned before use).
  2. new Student(...) allocates the object and gives every field its default value.
  3. The matching constructor runs and sets the fields.
  4. The address of the object is assigned to s1.

The variable lives in the method's stack frame; the object lives on the heap. When the last reference to an object disappears, the object becomes eligible for garbage collection.

Assigning references, and equality

Student a = new Student("Ali", 60);
Student b = a;                  // ALIAS: one object, two names
Student c = new Student("Ali", 60);
a == b       // true  : same address
a == c       // false : two distinct objects, even with identical data
a.equals(c)  // false unless the class OVERRIDES equals

Inherited from Object, equals compares references, exactly like ==. A class that wants value equality must override it:

public boolean equals(Object o) {
    if (!(o instanceof Student)) return false;
    Student other = (Student) o;
    return this.id == other.id;          // compare the identifying fields
}

Passing references as arguments

A method receives a copy of the reference. It may therefore call mutators and change the object's state, and the caller sees those changes. If it assigns a new object to the parameter, the caller's variable is unaffected — the same rule as for arrays in Chapter 9.

static void raise(Student s) { s.setGrade(s.getGrade() + 5); }   // caller sees this
static void swapIn(Student s){ s = new Student("Other", 0); }    // caller sees nothing

Constructors

public class Student {
    private int id; private String name; private double grade;

    public Student() {                       // default (no-argument) constructor
        this("Unknown", 0.0);                // may delegate with this(...)
    }
    public Student(String name, double grade) {   // parameterised constructor
        this.name = name;
        this.grade = grade;
    }
}

The rules that are examined:

  • A constructor has exactly the class name and no return type at all — not even void. public void Student() is a perfectly legal method that happens to look like a constructor, and it is never called by new.
  • If you declare no constructor, the compiler inserts a no-argument constructor that does nothing. If you declare any constructor, it does not, so new Student() then fails to compile unless you wrote that version yourself.
  • Constructors may be overloaded like any other method, on the parameter list.
  • this(...) calls another constructor of the same class and must be the first statement.
  • A class may not contain two constructors with the same parameter list, and therefore cannot have two no-argument constructors.

Correct forms to memorise:

declare a default constructor public MyClass() { ... }
declare a parameterised one public Employee(String name, double salary) { this.name = name; ... }
create an object MyClass obj = new MyClass();
call an instance method obj.update(3, "Hi!");
call a static method MyClass.greetings();

Overloaded methods in an instance context

The same signature rule applies as in Chapter 8: the name plus the parameter list must differ. Overloading gives a family of related operations one honest name — add(int), add(double), add(Student) — instead of addInt, addDouble, addStudent.

Method-call chaining

When a method returns a reference, another call can be attached to it:

String city = student.getAddress().getCity().toUpperCase();

Each call is evaluated left to right on the object returned by the previous one. If any of them returns null, the next call throws a NullPointerException — which is why chains are convenient but must be used with care. A method that returns this allows chained mutators: order.setId(5).setQty(2);

Several classes working together

A realistic program has one class per concept plus a driver: Student, Course, Enrolment, and SchoolApp with main. Each class is responsible for its own data; the driver only orchestrates. Objects of one class routinely hold references to objects of another — the idea developed as composition in Chapter 14.

Remember
  • == compares addresses; equals compares content only if the class overrides it.
  • A constructor has no return type and is named after the class.
  • Declare one constructor and you lose the free no-argument one.
  • this(...) must be the first statement of a constructor.
  • A method can mutate the object you pass it, but cannot replace it.
Common Pitfalls
  • public void MyClass() — a method, not a constructor.
  • MyClass obj = MyClass();new is missing.
  • Adding a parameterised constructor and then calling new MyClass() elsewhere.
  • Comparing two objects with == and concluding that equal data means equal objects.

Ready? Close the notes and practise.

37 questions. Predict the output before you check — that is the skill the exam measures.