Oracle Java Foundations study materials
Oracle Java Foundations study resources

Static Hiding, Field Shadowing and a Constructor That Prints Too Early

Oracle Java Foundations · Put your knowledge to work in context.

All case studies

Static Hiding, Field Shadowing and a Constructor That Prints Too Early

A team is reviewing these classes before a refactor.

class Base {
    static String kind() { return "base"; }
    String who() { return "Base"; }
    int size = 1;
    Base() { System.out.print(who() + " "); }
}
class Child extends Base {
    static String kind() { return "child"; }
    String who() { return "Child"; }
    int size = 2;
    Child() { System.out.print(size + " "); }
}

The snippet under review is Base b = new Child(); followed by System.out.print(b.kind() + b.who() + b.size);. One reviewer expects b.kind() to print child; another predicts the total output is Child 2 ChildChild1. A third would add @Override to kind() and to who().

1. State the exact console output and justify each of the four printed pieces.

Read the worked answer

Output: Child 2 baseChild1.

  1. new Child() triggers Child's constructor, which implicitly calls super() first. Base's constructor body runs who(). That is an instance method call, so it dispatches dynamically on the actual object type, Child - printing Child .
  2. Child's field initializer int size = 2; then runs, and Child's constructor body prints 2 .
  3. b.kind() is a static method. Static methods are resolved at compile time from the declared type of the reference, which is Base, so base is printed. No dynamic dispatch occurs.
  4. b.who() is virtual - the runtime object is Child, so Child is printed. b.size is a field access, resolved at compile time on Base, so 1 is printed. Fields are not polymorphic; there are two distinct size variables in the object.

2. What happens if `@Override` is added to `Child.kind()`, and why does the same annotation on `who()` cause no problem?

Read the worked answer

@Override on kind() is a compile-time error. kind() is static, and static methods are hidden, not overridden. The annotation asserts that the method overrides or implements a supertype method, and an instance method in a superclass cannot be overridden by a static method in a subclass (and vice versa). Neither can a static method override another static method - that is hiding. The compiler therefore rejects the claim.

who() is an ordinary instance method with a compatible signature and return type, so it genuinely overrides Base.who(). @Override is exactly correct there and is valuable: if Base.who() were later renamed, the annotation turns a silent behaviour change into a compiler error.

Practical rule: never place @Override on a static method; when a reviewer sees it, the intended fix is usually to make the method an instance method, or to remove the misleading annotation and call it through the class name.

3. Suppose `Child.who()` were changed to return `"Child" + size`. What would step 1 of the output become, and what general hazard does this illustrate?

Read the worked answer

It would print Child0 , because Child's field initializers run only after Base's constructor body has completed. At the moment Base() calls the overridden who(), the Child part of the object exists but int size = 2; has not executed yet, so size is still the default 0. The array of field values is defaulted before any constructor body runs.

This is the classic 'calling an overridable method from a constructor' hazard. The superclass constructor observes a half-initialized subclass: fields are default (0, false, null) and subclass invariants have not been established. A null-valued field dereferenced inside such a method causes a NullPointerException before the object is even constructed. Mitigations: keep constructors free of overridable calls, or make the method final/private, or move the work into a factory method invoked after construction completes.