Polymorphism enables objects of different classes to be treated as instances of a common superclass, allowing methods to be overridden to provide specific behaviors. In Java, this is demonstrated through inheritance and method overriding.
For example, consider a base class Vehicle with a method displayInfo():
public void displayInfo() {
System.out.println("Vehicle details: Basic model");
}
Subclasses like Car and Motorcycle can extend Vehicle and override displayInfo() to include unique attributes. The Car class might add a doorCount field:
public class Car extends Vehicle {
private int doorCount;
public Car(int doors) {
this.doorCount = doors;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Car has " + doorCount + " doors.");
}
}
Similarly, Motorcycle could include a hasSidecar field:
public class Motorcycle extends Vehicle {
private boolean hasSidecar;
public Motorcycle(boolean sidecar) {
this.hasSidecar = sidecar;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Motorcycle sidecar: " + hasSidecar);
}
}
A test program can create instances and invoke their methods:
public class TestVehicles {
public static void main(String[] args) {
Vehicle v1 = new Vehicle();
Vehicle v2 = new Car(4);
Vehicle v3 = new Motorcycle(false);
v1.displayInfo();
v2.displayInfo();
v3.displayInfo();
}
}
Output:
Vehicle details: Basic model
Vehicle details: Basic model
Car has 4 doors.
Vehicle details: Basic model
Motorcycle sidecar: false
The Java Virtual Machine dynamically selects the appropriate method based on the actual object type, a mechanism known as virtual method invocation.
Field hiding occurs when a subclass declares a field with the same name as one in the superclass, making the superclass field inaccessible by simple name. Access requires the super keyword, though this practice is generally discouraged for clarity.
The super keyword is also used to invoke superclass constructors or methods. For instance, in a subclass constructor:
public Motorcycle(boolean sidecar, int speed) {
super(speed);
this.hasSidecar = sidecar;
}
If no explicit superclass constructor call is made, the compiler inserts a call to the no-argument constructor of the superclass.
All classes implicitly inherit from Object, which provides methods like equals(), hashCode(), toString(), and clone(). Overriding these methods requires careful implementation to maintain contracts, such as ensuring equals() and hashCode() consistnecy.
Final classes and methods prevent further inheritance or overriding. For example:
public final class UtilityClass {
public final int computeValue() {
return 42;
}
}
Abstract classes define incomplete implementations and cannot be instantiated. They may include abstract methods without bodies:
public abstract class Shape {
public abstract double area();
}
Concrete subclasses must implement all abstract methods. Abstract classes are suitable for sharing code among related classes, while interfaces define contracts for unrelated classes.
Inheritance summary: A class has one direct superclass (except Object) and inherits fields and methods. Overriding and hiding alter inherited behaviors, and the Object class provides foundational methods. Final and abstract modifiers control extensibility.
Number classes like Integer and Double wrap primitive types, enabling object-oriented features. Autoboxing and unboxing automatically convert between primitives and wrappers. Useful methods include valueOf() for parsing strings and toString() for conversions.
Formatted output can be achieved with printf() or String.format(), using format specifiers like %d for integers and %f for floats. The DecimalFormat class allows custom patterns for numeric display.
The Math class offers mathematical functions, such as sqrt(), pow(), and trigonometric operations. Random numbers can be generated with Math.random() or java.util.Random.
Characters are handled with the char primitive or Character wrapper, which provides methods like isDigit() and toUpperCase(). Escape sequences (e.g., \n for newline) represent special characters in strings.
Strings are immutable objects; operations return new strings. Methods include length(), substring(), concat(), and replace(). For mutable sequences, StringBuilder offers efficient manipulation with methods like append() and reverse(). Its capacity adjusts dynamically as needed.
String comparisons use equals(), compareTo(), or regionMatches(). Conversion between strings and numbers involves parseInt() or valueOf() for parsing, and toString() or concatenation for the reverse.