Java Fundamentals Overview
Java Fundamentals Overview
Static polymorphism, also known as compile-time polymorphism, occurs via method overloading. It is resolved during compile time based on the method signature. Example: If a class has multiple methods with the same name but different parameters, the compiler decides which method to call based on the arguments supplied. Dynamic polymorphism, or runtime polymorphism, uses method overriding, where the method to be invoked is determined at runtime based on the object reference. An example is when a subclass provides a specific implementation for a method declared in its superclass, which is invoked through a superclass reference .
Dynamic polymorphism in Java allows a method to perform different tasks based on the object invoking it, making method calls resolved at runtime. This is beneficial for method overriding as it enables a class to provide a specific implementation of a method that is already defined in its superclass. It facilitates the use of a single interface to represent different types of objects, improving code flexibility and reusability. For example, a method call to an overridden method in a subclass will execute that method in the subclass, not the superclass's version .
Static method overriding is not possible in Java because static methods are class-specific and are bound to the class, not the object instance. Their resolution is done at compile time, which means that the method call is determined from the type of reference variable, not the actual type of the object it points to. Hence, there is no dynamic method dispatch, which is required for method overriding .
Java ensures security by eliminating the use of pointers, which prevents direct access to memory. This design choice minimizes the risk of pointer manipulation and memory corruption, thereby safeguarding applications from a variety of security vulnerabilities .
Packages in Java serve several purposes: they prevent naming conflicts by acting as namespaces, provide a logical grouping of related classes, and enhance code maintainability. By grouping classes that serve similar functionalities, packages help in organizing code in a modular way, making it easier to manage large codebases .
Inheritance reduces code redundancy by allowing common code to reside in a superclass from which multiple subclasses can inherit. This reduces duplication as subclasses reuse the existing code. For instance, a superclass 'Vehicle' might have a method for 'startEngine'. Different subclasses like 'Car', 'Bike', or 'Truck' can inherit 'Vehicle' and start their engines without rewriting the code. This promotes less maintenance since changes in the superclass automatically reflect across all subclasses. Inheritance enables framework creation by allowing the superclass to define generic behavior (startEngine), while subclasses provide specifics (how they start).
Encapsulation in Java, facilitated by access modifiers, supports the Liskov Substitution Principle (LSP) by ensuring that subclasses can replace their superclasses without altering the desired properties of the program. By controlling access to class members and enforcing constraints through encapsulation, Java ensures that methods in subclasses fulfill contracts defined in superclasses. For example, modifying access from 'public' to 'protected' violates LSP as it restricts access, while moving from 'protected' to 'public' is compliant since it widens access .
Java uses 'pass-by-value' for parameter passing, meaning that when a method is called, a copy of each argument is passed to the method. Hence, modifications to the parameters within the method do not affect the original arguments. For primitive data types, changes made to the parameter will not affect the original value outside the method. For objects, the actual reference is copied, so the original object can be modified, but reassigning the object within the method to a new reference does not affect the actual reference outside the method .
Encapsulation in Java involves restricting direct access to certain components of an object and can be achieved using access modifiers like private, protected, and public. It helps prevent reference leaking by controlling the exposure of class members. For instance, private variables can be accessed only via public methods, often called getters and setters. To mitigate reference leaking, when a method returns a reference to a mutable object, a copy of the object should be returned instead. In the case of a class with a private Date field, returning a clone of the Date object can prevent the caller from modifying the original object .
Java achieves platform independence through the use of bytecode. When a Java application is compiled, it is converted into bytecode rather than machine-specific code. This bytecode can be executed on any system that has a Java Virtual Machine (JVM), which interprets the bytecode into machine code appropriate for the host system, allowing the application to run on any platform without modification .