Core Java Concepts: OOP Principles and Data Type Management

Runtime Polymorphism

The concept of polymorphism describes a situation where a single entity behaves differently under various circumstances. In object-oriented programming, this occurs when a subclass provides a specific implementation of a method that is already defined in its superclass.

Consider a base vehicle configuration called BaseVehicle. We can add a utility method named displaySpecs to show current state data.

public void displaySpecs(){
    System.out.println("\nVehicle is in transmission " + this.currentGear
        + " with a pedal rate of " + this.pedalRate +
        " and travelling at a velocity of " + this.velocity + ". ");
}

To illustrate overriding behavior, we extend BaseVehicle into specialized types. For an off-road variant, we introduce a suspensionType attribute tracking whether the front shock is present (e.g., "Front") or dual shocks exist ("Dual").

public class OffRoadVehicle extends BaseVehicle {
    private String suspensionType;

    public OffRoadVehicle(
               int initialPedalRate,
               int initialVelocity,
               int initialGear,
               String type){
        super(initialPedalRate,
              initialVelocity,
              initialGear);
        this.setSuspension(type);
    }

    public String getSuspension(){
      return this.suspensionType;
    }

    public void setSuspension(String type) {
        this.suspensionType = type;
    }

    public void displaySpecs() {
        super.displaySpecs();
        System.out.println("The OffRoadVehicle features" +
            " a " + getSuspension() + " suspension system.");
    }
} 

Note how displaySpecs invokes the parenet implementation before adding specific details regarding suspension.

Similarly, a road-racing variant tracks tire dimensions. We define RoadRaceVehicle with a width property measured in millimeters.

public class RoadRaceVehicle extends BaseVehicle{
    // Dimension in millimeters (mm)
    private int tireWidth;

    public RoadRaceVehicle(int initialPedalRate,
                    int initialVelocity,
                    int initialGear,
                    int newWidth){
        super(initialPedalRate,
              initialVelocity,
              initialGear);
        this.setTireWidth(newWidth);
    }

    public int getTireWidth(){
      return this.tireWidth;
    }

    public void setTireWidth(int width){
        this.tireWidth = width;
    }

    public void displaySpecs(){
        super.displaySpecs();
        System.out.println("The RoadRaceVehicle uses " + getTireWidth() +
            " MM tires.");
    }
}

A verification class creates instances of each type using a common reference variable, demonstrating dynamic binding.

public class TestVehicles {
  public static void main(String[] args){
    BaseVehicle v01, v02, v03;

    v01 = new BaseVehicle(20, 10, 1);
    v02 = new OffRoadVehicle(20, 10, 5, "Dual");
    v03 = new RoadRaceVehicle(40, 20, 8, 23);

    v01.displaySpecs();
    v02.displaySpecs();
    v03.displaySpecs();
  }
}

The Java runtime dispatches calls based on the actual object instance rather than the declared variable type. This mechanism is known as virtual method invocation.

Field Hiding and Super Usage

If a subclass defines a field with the same name as one in its superclass, the subclass field shadows the parent field. Direct access requires referencing the shadowing member, though super allows access to hidden fields, although this practice is discouraged due to potential confusion.

Accessing Parent Members

The super keyword facilitates interaction with inherited components. It allows calling overridden methods or accessing hidden fields. Consider a hierarchy where ParentEntity defines a print method:

public class ParentEntity {

    public void logMethod() {
        System.out.println("Logged in ParentEntity.");
    }
}

When ChildEntity overrides this, super.logMethod() explicitly targets the parent's logic:

public class ChildEntity extends ParentEntity {

    public void logMethod() {
        super.logMethod();
        System.out.println("Logged in ChildEntity");
    }
    public static void main(String[] args) {
        ChildEntity c = new ChildEntity();
        c.logMethod();    
    }
}

Executing this prints output from both the parent and child contexts.

Constructor Chaining

The super keyword is mandatory for invoking parent constructors within subclasses. The call must appear as the very first statement in a constructor.

public OffRoadVehicle(int height, 
                    int pedalRate,
                    int velocity,
                    int gear) {
    super(pedalRate, velocity, gear);
    seatHeight = height;
}   

Syntax includes either super(); for no-arg constructors or super(args); matching specific signatures. If omitted, the compiler attempts to insert a call to the no-argument constructor automatically. If the parent lacks one, compilation fails. This cascading initialization is referred to as the constructor chain.

The Root Object Class

All Java types implicitly inherit from java.lang.Object. Key instance methods available include:

  • protected Object clone(): Creates a shallow copy. Implements Cloneable interface to permit cloning.
  • public boolean equals(Object obj): Compares content equality.
  • protected void finalize(): Called prior to garbage collection.
  • public final Class getClass(): Returns runtime class metadata.
  • public int hashCode(): Generates a hash integer value.
  • public String toString(): Provides string representation.

Thread synchronization relies on additional methods like wait, notify, and notifyAll.

Implementation Details

Cloning: The clone() method performs a shallow copy. To enable it, the class must implement Cloneable. Failure to do so throws CloneNotSupportedException. Signature typically looks like public Object clone() throws CloneNotSupportedException. Custom logic may be required if the object holds references to other mutable objects to ensure independence between the clone and the original.

Equality: By default, equals() checks reference identity (==). For logical comparison, override is necessary. If equals is overridden, hashCode must also be updated to maintain contract consistency.

Finalization: finalize() offers cleanup hooks but execution timing is undefined. Relying on it for resource release (like file handles) is risky; prefer try-with-resources statements instead.

Hashing: Equality implies equal hash codes. Changing equality logic necessitates adjusting hashing algorithms accordingly.

Example: Logical Comparison

public class LibraryItem {
    String isbnCode;

    public boolean equals(Object obj) {
        if (obj instanceof LibraryItem) {
            return isbnCode.equals(((LibraryItem)obj).isbnCode);
        }
        return false;
    }
}

Two distinct LibraryItem instances with identical ISBNs will now evaluate as equal via equals().

ToString Representation

Overriding toString() yields useful debug information. Printing an object instance often defaults to this method's output.

System.out.println(firstItem.toString());
// Expected: ISBN: 0201914670; Title Example

Immutability and Finality

Using the final modifier prevents modification. A final method cannot be overridden by subclasses. A final class cannot be extended entirely. Unchangeable behaviors, such as algorithm constants, often warrant this protection.

class GameLogic {
    enum PlayerSide { RED, BLUE }
    ...
    final PlayerSide determineFirstPlayer() {
        return PlayerSide.RED;
    }
    ...
}

Abstraction

An abstract class cannot be instantiated and serves as a template for subclasses. It may contain concrete implementations or abstract declarations requiring child implementation.

public abstract class GraphicElement {
   // declare fields
   // declare nonabstract methods
   abstract void render();
}

If a subclass remains incomplete regarding abstract methods, it too must be declared abstract. Interfaces offer alternatives for defining behavior across unrelated types, supporting multiple inheritance capabilities, whereas abstract classes allow shared non-static state.

Key distinctions:

  • Abstract Classes: Suitable for sharing code among closely related entities. Support non-public access modifiers and state fields.
  • Interfaces: Ideal for specifying contracts for disparate types. All methods are implicitly public (unless default/static).

Standard Number Handling

While primitives are efficient, wrapper classes provide object-oriented functionality for numerical types. These include Byte, Short, Integer, Long, Float, and Double, all extending the abstract Number class.

Utility Features

Wrappers facilitate operations difficult with primitives alone, such as storing numbers in collections. They expose constants like MIN_VALUE and MAX_VALUE, plus conversion utilities to strings or other bases.

// Decode hex, binary, octal
Integer.decode(s); 

// Parse string to int
int i = Integer.parseInt(s, radix);

// ValueOf for wrapping primitives
Integer.valueOf(i); 

Formatting Output

Controlled output utilizes PrintStream.format() or printf(). Conversion specifiers guide the layout:

  • %d: Decimal integer.
  • %f: Floating-point number.
  • %n: Platform-independent newline.

Example usage:

double pi = Math.PI;
System.out.format("%.3f%n", pi);   // Prints: 3.142

For locale-specific or complex patterns, DecimalFormat allows defining templates (e.g., "###,##0.00") to handle grouping separators, currency symbols, and precision rounding.

Mathematical Operations

The java.lang.Math class offers static utility methods for advanced calculations. Constants E and PI are accessible statically.

import static java.lang.Math.*;

void compute() {
    double result = sin(radians); // Direct access via static import
}

Core functions cover absolute values, rounding, ceiling/floor, and trigonometric conversions. Trigonometry functions expect angles in radians; use toRadians() for conversion.

public class CalculationDemo {
    public static void main(String[] args) {
        double degrees = 45.0;
        double rads = Math.toRadians(degrees);

        System.out.println("Sin: " + Math.sin(rads));
        System.out.println("Tan: " + Math.tan(rads));
    }
}

Random integers can be generated by scaling Math.random():

int randomInt = (int)(Math.random() * 10); // Range 0-9

Character Manipulation

Use the char primitive for individual characters. To treat them as objects, utilize the Character wrapper. Note that character objects are immutable.

Escape sequences allow special control characters within strings. Common sequences include \t (tab), \n (newline), and \' (single quote).

char letter = '\u0041'; // Unicode uppercase A

Character methods assist in classification, such as isDigit(), isWhitespace(), or case conversion via toUpperCase().

String Management

Strings represent immutable character sequences. Constructing literals creates underlying objects managed by the JVM. While the object itself cannot change, methods return new instances resulting from transformations.

Basic Operations

Retrieval is handled by charAt(index). Length is available via length(). Concatenation can occur using + or the explicit concat() method.

String part = text.substring(startIndex, endIndex);

The substring operation copies characters; modifying the original string is impossible. Searching relies on indexOf() and contains(). Case sensitivity is managed by comparing variants or providing flags.

Mutability with StringBuilder

For scenarios requiring frequent modifications (e.g., building large dynamic strings), StringBuilder provides a mutable buffer. Unlike String, appending does not create new objects every time.

StringBuilder buffer = new StringBuilder(palindrome);
buffer.reverse();

This approach reduces memory overhead compared to repeated concatenation in loops. append() accepts almost any data type, converting it to string internally.

Other utilities include deleting ranges, inserting data at indices, and replacing substrings. Remember that toString() converts the builder back to an immutable string instance when finalized.

Tags: java OOP Polymorphism Inheritance math-library

Posted on Fri, 28 Aug 2026 16:05:26 +0000 by joad