Core Java Interview Questions Guide
Core Java Interview Questions Guide
Java provides synchronization mechanisms crucial for the proper functioning of multi-threaded applications, primarily through the synchronized keyword and blocks. The synchronized keyword can be applied to methods and blocks to ensure that only one thread can execute the code block at a given time, thereby preventing data inconsistency and race conditions. Additionally, synchronized blocks can be used to synchronize a specific piece of code on an object, allowing for more fine-grained control over synchronization. The use of synchronized methods or blocks ensures thread safety when accessing shared resources simultaneously .
Java provides several mechanisms for memory management, including automatic garbage collection and explicit memory handling features. The garbage collector automatically reclaims memory by destroying objects that are no longer referenced, which helps prevent memory leaks. Additionally, Java uses a memory model that includes stack and heap memory, where method calls and local variables are stored in the stack, while objects are allocated on the heap. This separation helps manage memory more efficiently during program execution. Moreover, finalization allows an object to clean up resources before being garbage collected, though it is generally not used in favor of try-with-resources or explicit resource management .
Exception handling in Java is a mechanism to manage runtime errors, allowing a program to continue execution rather than terminate abruptly. It involves the use of try-catch blocks, where code that may throw exceptions is placed in the try block, and potential exceptions are caught and handled in the catch block. Additionally, a finally block can be used to execute code regardless of whether an exception occurs, such as resource cleanup. By providing a structured way to handle errors, exception handling improves program robustness and stability, ensuring that critical processes are completed even when unexpected issues arise .
Interfaces in Java can only declare methods and cannot provide any implementation (prior to Java 8 where default methods are allowed), allowing a class to implement multiple interfaces. Abstract classes, on the other hand, can have both fully defined and abstract methods, providing a base implementation that can be shared across subclasses. Abstract classes can also contain fields and constructors. Interfaces are used when a contract needs to be defined across classes without requiring common code, whereas abstract classes are chosen when there is a clear hierarchical relationship with shared code across subclasses. Use interfaces when you want to enforce a certain capability across different classes and abstract classes when creating a shared base class .
HashMap and Hashtable in Java both implement the Map interface and store key-value pairs. However, Hashtable is synchronized, meaning it is thread-safe and suitable for concurrent access by multiple threads, which can result in slower performance compared to HashMap. In contrast, HashMap is not synchronized and allows for more efficient single-threaded access. HashMap permits null keys and values, whereas Hashtable does not. These differences influence their usage; HashMap is preferred for non-threaded applications or when manual synchronization is used, while Hashtable is suitable for legacy multi-threaded environments where built-in synchronization is necessary .
Java is considered an object-oriented programming language because it is based on the concepts of objects and classes, which encapsulate data and behavior in a way that can be reused and manipulated. The key principles of OOP that Java follows include encapsulation, abstraction, inheritance, and polymorphism. Encapsulation is achieved through access modifiers that restrict access to certain components of an object. Abstraction allows developers to focus on essential qualities rather than specific characteristics of one particular object. Inheritance enables the creation of new classes based on existing ones, promoting code reuse. Polymorphism allows methods to do different things based on the objects they are dealing with .
'Shadowing' in Java occurs when a subclass or inner class declares a field with the same name as a field in its superclass or outer class. This can affect variable visibility as the subclass's field shadows or hides the inherited superclass field, making it inaccessible directly by its name. Accessing the superclass field then requires using super or fully qualified names depending on context. While shadowing can be intentional for overriding purposes, it can also lead to confusion and errors if developers are unaware of which variable they are referencing, introducing potential bugs in the program .
The Java Virtual Machine (JVM) executes a Java program by taking the compiled bytecode from the Java compiler and interpreting or compiling it into machine code suitable for the host system through Just-In-Time (JIT) compilation. The JVM starts by loading the class files and verifying bytecode, then it proceeds to interpret or use the JIT compiler to convert sections of bytecode into native machine code to improve performance. The JIT compiler optimizes the execution by translating frequently called methods to native code once and caching them for future use, reducing overhead and leveraging execution speed .
Polymorphism in Java can be classified into two main types: compile-time (static) polymorphism and runtime (dynamic) polymorphism. Compile-time polymorphism is achieved through method overloading, where multiple methods have the same name but differ in parameter type or number, allowing for invocation based on the signature. This type of polymorphism is resolved during compile-time. Runtime polymorphism is achieved through method overriding, where a subclass provides a specific implementation of a method declared in its superclass. This ensures that the method call is resolved at runtime, based on the object's actual type. This flexibility allows for more dynamic and reusable code structures .
The 'final' keyword in Java is used to declare constants, prevent method overriding, and inheritance. When applied to a variable, it makes the variable's value immutable; for methods, it prevents overriding; for classes, it prevents inheritance. 'Finally' is a block used in conjunction with try-catch that executes code after a try block, ensuring resource deallocation or cleanup regardless of whether an exception occurred. 'Finalization' is a process linked to the garbage collection that allows an object to clean up resources before being collected, though it is often replaced by explicit resource management due to its unpredictable nature. Each serves distinct purposes but collectively ensures effective resource management and program correctness .