← Back to Home

Module 4: Testing and Debugging

Module Overview

Learn advanced testing techniques and debugging strategies for Java applications.

Learning Objectives

Advanced Inheritance and Polymorphism

Advanced inheritance and polymorphism concepts are crucial for designing robust, maintainable, and extensible applications. A deeper understanding of these concepts allows developers to leverage the full power of object-oriented programming.

Method Execution in Inheritance Hierarchies:

  • Method Dispatch: When a method is called on an object, Java determines which implementation to use based on the actual object type, not the reference type
  • Superclass Method Calling Overridden Method: When a superclass method calls another method that is overridden in a subclass, the subclass's implementation is called
  • Example:
    class Parent {
        public void doWork() {
            System.out.println("Parent working");
            this.prepareWork(); // Will call Child's implementation if called on Child
        }
        
        public void prepareWork() {
            System.out.println("Parent preparing");
        }
    }
    
    class Child extends Parent {
        @Override
        public void prepareWork() {
            System.out.println("Child preparing");
        }
    }
    
    // Usage
    Parent p = new Child();
    p.doWork(); // Outputs: "Parent working" followed by "Child preparing"

Access Modifiers and Inheritance:

  • Protected Members: Visible to subclasses and classes in the same package, but not to the outside world
  • This allows superclasses to expose functionality to subclasses without making it public
  • Example:
    public class DatabaseConnection {
        protected Connection getConnection() {
            // Implementation that returns a database connection
            // Available to subclasses but not to external classes
        }
        
        public void executeQuery(String sql) {
            Connection conn = getConnection();
            // Use connection to execute query
        }
    }

Superclass vs Interface Decision Making:

  • Use a Superclass When:
    • You want to share code implementation
    • You have a clear is-a relationship
    • You need to maintain state across all subclasses
  • Use an Interface When:
    • You want to define a contract without implementation
    • You need multiple inheritance (a class can implement multiple interfaces)
    • You want to define behavior that can be implemented by unrelated classes

Risks of Modifying Superclasses:

  • Adding or changing methods can break existing subclasses
  • Changing method behavior can cause unexpected issues in subclasses
  • Implementation details that subclasses depend on may change
  • Best practices:
    • Design superclasses carefully from the start
    • Document clearly which methods are meant to be overridden
    • Consider marking classes as final if they're not designed for extension
    • Use composition over inheritance when appropriate

Polymorphic Assignment and Type Casting:

// Polymorphic assignment - reference type vs object type
Shape shape = new Circle(10); // Shape is reference type, Circle is object type
shape.area(); // Calls Circle's implementation

// Accessing subclass-specific methods requires casting
if (shape instanceof Circle) {
    Circle circle = (Circle) shape;
    double diameter = circle.getDiameter(); // Method only available in Circle
}

Resources