- Class: A class is a template or blueprint for an object. It defines a new data type that can be used to create objects.
- Object: An object is an instance of a class and represents physical reality (occupies space in memory).
- State: Represents the data (attributes) of the object.
- Identity: Uniquely distinguishes one object from another (usually where it's stored in memory).
- Behavior: Describes what operations (methods) can be performed on the object.
-
Declaration:
Box mybox; // Declare reference to object
Here,
mybox
is a reference to an object of typeBox
. It does not yet refer to an actual object. -
Instantiation:
mybox = new Box(); // Allocate a Box object
This line allocates memory for a
Box
object using thenew
keyword and assigns a reference tomybox
.
-
The new keyword allocates memory dynamically (at runtime) for an object and returns a reference to it.
-
The reference is stored in a variable (in this case,
mybox
), which holds the memory address of the object.classname class-var = new classname();
- class-var: A variable of the class type being created.
- classname(): Constructor of the class, invoked when an object is instantiated.
- Primitive Types: Such as
int
andchar
are not objects in Java. They are implemented as normal variables for efficiency and do not requirenew
for instantiation.
- Example:
Box b1 = new Box(); Box b2 = b1;
- b1 and b2 both refer to the same object. No new memory is allocated for
b2
, and changes made throughb2
will affect the object referred to byb1
.
- b1 and b2 both refer to the same object. No new memory is allocated for
-
Parameter: A variable defined in a method that receives a value during method invocation.
Example:
int square(int i) { return i * i; }
- i is a parameter in the
square(int i)
method.
- i is a parameter in the
-
Argument: The actual value passed when invoking the method. In
square(100)
,100
is the argument.
- When creating an object:
Bus bus = new Bus();
- LHS (Left-hand side):
bus
(the reference) is checked by the compiler. - RHS (Right-hand side):
new Bus()
(the object creation) is handled by the JVM.
- LHS (Left-hand side):
This structure captures the essential concepts of classes, objects, memory allocation, and method parameters in Java.
- Definition:
-
The
this
keyword is used within a method or constructor to refer to the current object, i.e., the object on which the method or constructor was invoked. -
Example:
class Box { int width, height; Box(int width, int height) { this.width = width; // Refers to the instance variable this.height = height; // Refers to the instance variable } void displayDimensions() { System.out.println("Width: " + this.width + ", Height: " + this.height); } }
-
-
Purpose:
- Used to declare constants in Java. A field declared as
final
cannot be modified after it is initialized.
- Used to declare constants in Java. A field declared as
-
Usage Example:
final int FILE_OPEN = 2; // Constant field with all uppercase naming convention
-
Immutability for Primitive vs Reference Types:
-
For primitive types: The value cannot be changed once initialized.
-
For reference types: The reference to the object cannot be changed, but the object’s internal state can still be modified.
-
Example for reference type:
final Box box = new Box(5, 10); // The reference 'box' cannot point to another Box box.width = 20; // But you can modify the internal state of the Box object
-
-
Purpose:
- The
finalize()
method is used to perform cleanup actions before an object is reclaimed by the garbage collector.
- The
-
Example:
protected void finalize() { System.out.println("Object is being garbage collected"); }
-
Note: This method is rarely used in modern Java programs due to improvements in garbage collection and better alternatives for resource management like
try-with-resources
.
-
Definition:
- A constructor is a special method that is automatically called when an object is created. It has no return type and is used to initialize objects.
-
Example:
class Box { int width, height; // Constructor Box() { width = 10; height = 20; System.out.println("Box Constructor Called"); } } public class Main { public static void main(String[] args) { Box mybox = new Box(); // Calls the Box() constructor } }
-
Key Points:
- In Java, the constructor of a base class (superclass) is automatically called when an object of the derived class (subclass) is created.
- If the base class has no argument constructor, it gets invoked by default.
- If the base class has a parameterized constructor, the derived class must explicitly call it.
-
Example:
class Base { Base() { System.out.println("Base Class Constructor Called"); } } class Derived extends Base { Derived() { // Automatically calls the Base() constructor System.out.println("Derived Class Constructor Called"); } } public class Main { public static void main(String[] args) { Derived d = new Derived(); // Creates an instance of Derived, calls both constructors } }
Output:
Base Class Constructor Called Derived Class Constructor Called
-
Parameterized Constructor in Base Class:
class Base { int value; // Parameterized constructor Base(int value) { this.value = value; System.out.println("Base Class Constructor Called with value: " + value); } } class Derived extends Base { // Must explicitly call the Base class parameterized constructor Derived(int value) { super(value); // Call to Base class constructor System.out.println("Derived Class Constructor Called"); } } public class Main { public static void main(String[] args) { Derived d = new Derived(100); // Calls parameterized constructor of Base } }
Output:
Base Class Constructor Called with value: 100 Derived Class Constructor Called
This structure provides a comprehensive overview of the this
, final
, finalize()
methods, and the role of constructors and inheritance in Java with code examples to clarify the concepts.
Here's a detailed and structured note on Java packages, including examples and important details:
-
Definition:
- Packages are containers for classes that help in compartmentalizing the class namespace. They prevent name collisions and organize related classes into hierarchical structures.
-
Purpose:
- Naming: Avoids naming conflicts by allowing multiple classes with the same name in different packages.
- Visibility Control: Determines which classes and members are accessible from other classes.
-
Creating a Package:
- To create a package, include the
package
statement at the beginning of your Java source file.
package MyPackage; // Declares that the class belongs to MyPackage
- To create a package, include the
-
File System Structure:
- Java uses directories to represent packages. The directory structure must mirror the package hierarchy.
- For example, a class in the
java.awt.image
package must be stored injava/awt/image
directory.
-
Directory Naming:
- The directory name must exactly match the package name, including case sensitivity.
-
Finding Packages:
- Default Behavior: The Java runtime system starts searching for packages in the current working directory.
- Classpath: You can specify additional directories or JAR files using the
CLASSPATH
environment variable. - Command Line: Use the
-classpath
(or-cp
) option withjava
andjavac
commands to specify paths to your classes.
-
Importing Packages:
-
When importing a package, only the public members of the classes within that package are accessible. Non-public members are not accessible outside their package.
-
Example:
// In MyPackage/Person.java package MyPackage; public class Person { public String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public void display() { System.out.println("Name: " + name + ", Age: " + age); } }
// In another package or directory import MyPackage.Person; public class TestPerson { public static void main(String[] args) { Person p = new Person("Alice", 30); p.display(); // Accessible as display() is public // p.age; // Not accessible, as age is private } }
-
-
Classpath Example:
-
Setting CLASSPATH (environment variable): Add the directory where your
.class
files are located. -
Using Command Line:
javac -classpath /path/to/classes MyPackage/Person.java java -classpath /path/to/classes TestPerson
-
This note captures the essential aspects of Java packages, including their purpose, file system structure, and how to manage and import them, along with practical examples.
Here's a detailed and well-organized note on the static
keyword in Java, including explanations and code examples:
-
Definition:
- The
static
keyword in Java is used for creating class-level methods and variables. Static members belong to the class rather than to instances of the class.
- The
-
Characteristics of Static Members:
- Access: Static members can be accessed before any objects of the class are created and without reference to any specific object.
- Static Methods:
- Belong to the class, not to any instance.
- Can only access other static methods and static variables directly.
- Cannot access instance variables or methods.
- Cannot use
this
orsuper
keywords. - Can be accessed using the class name directly.
-
Example of Static Methods:
public class Human { String message = "Hello World"; public static void display(Human human) { System.out.println(human.message); // Accessing non-static variable through object reference } public static void main(String[] args) { Human kunal = new Human(); kunal.message = "Kunal's message"; Human.display(kunal); } }
-
Static Variables:
- Shared among all instances of a class.
- Initialized when the class is first loaded.
-
Static Blocks:
- Used for static initialization. Executes once when the class is first loaded.
class UseStatic { static int a = 3; static int b; static void meth(int x) { System.out.println("x = " + x); System.out.println("a = " + a); System.out.println("b = " + b); } static { System.out.println("Static block initialized."); b = a * 4; } public static void main(String args[]) { meth(42); } }
- Output:
Static block initialized. x = 42 a = 3 b = 12
-
Static Inner Classes:
- Only nested classes can be static.
- Static nested classes do not have access to instance variables of the outer class.
public class Static { static class Test { String name; public Test(String name) { this.name = name; } } public static void main(String[] args) { Test a = new Test("Kunal"); Test b = new Test("Rahul"); System.out.println(a.name); // Kunal System.out.println(b.name); // Rahul } }
-
Key Points:
- Static methods cannot be overridden because method resolution is done at compile-time for static methods.
- Static interface methods are not inherited by implementing classes or sub-interfaces.
- Static inner classes are not associated with an instance of the outer class and do not have access to its instance members.
-
Note on Static Classes:
- Only nested classes can be static. Static nested classes can have static variables and methods.
public class StaticDemo { // Static inner class static class InnerStatic { static int staticVar = 10; static void staticMethod() { System.out.println("Static Method in InnerStatic"); } } public static void main(String[] args) { InnerStatic.staticMethod(); // Access static method directly System.out.println("Static Variable: " + InnerStatic.staticVar); // Access static variable directly } }
This note provides a comprehensive overview of static members in Java, including how they function, examples, and specific rules regarding their usage.
The Singleton design pattern ensures that a class has only one instance and provides a global point of access to that instance. Here's a detailed breakdown of how the Singleton pattern is implemented in Java, using the provided code as an example:
package com.kunal.singleton;
import com.kunal.access.A;
public class Singleton {
// Private constructor to prevent instantiation from outside
private Singleton() {
}
// Private static variable to hold the single instance
private static Singleton instance;
// Public static method to provide access to the instance
public static Singleton getInstance() {
// Create the instance only if it does not exist
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
-
Private Constructor:
- The constructor
private Singleton()
ensures that the class cannot be instantiated from outside. This is crucial for maintaining a single instance of the class.
- The constructor
-
Private Static Variable:
private static Singleton instance;
is a static variable that holds the single instance of the class. This variable is private to prevent direct access from outside the class.
-
Public Static Method:
public static Singleton getInstance()
is a static method that provides the global access point to the single instance of the class.- Inside this method, a check is performed to see if the instance is
null
. If it isnull
, a new instance is created and assigned to theinstance
variable. - If the instance already exists, the existing instance is returned.
- Accessing the Singleton Instance:
- To get the single instance of the
Singleton
class, you callSingleton.getInstance()
. - This method ensures that only one instance of the class is created and reused throughout the application.
- To get the single instance of the
- Controlled Access: Ensures controlled access to the single instance of the class.
- Reduced Memory Usage: Since only one instance is created, it helps in reducing memory consumption.
- Global Access Point: Provides a global point of access to the instance.
-
Thread Safety: The given implementation is not thread-safe. In a multi-threaded environment, multiple threads might simultaneously pass the
null
check and create multiple instances. To make it thread-safe, consider using synchronization or the double-checked locking principle.public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }
-
Lazy Initialization: The instance is created only when
getInstance()
is called for the first time, which is known as lazy initialization.
- Configuration Manager: Often used in applications where a single configuration manager is needed to handle configuration settings.
- Package Import: The
import com.kunal.access.A;
statement indicates that this class might be using or extending functionality from another class or package. Ensure thatA
is appropriately defined if needed.
This note provides a clear explanation of the Singleton design pattern and how it is implemented in Java, along with the provided code example.
Inheritance is a fundamental concept in Java that allows one class to inherit the properties and behaviors (fields and methods) of another class. Here's a breakdown of key concepts related to inheritance, including the use of extends
, super
, and final
.
In Java, inheritance is achieved using the extends
keyword:
class Subclass extends Superclass {
// body of class
}
- Subclass: The class that inherits from another class.
- Superclass: The class being inherited from.
- A subclass can inherit only one superclass (Java does not support multiple inheritance directly).
-
While a subclass inherits all the fields and methods of a superclass, it cannot access private members of the superclass directly.
-
Superclass Reference to Subclass Object: You can assign a subclass object to a superclass reference, but the reference can only access methods and fields defined in the superclass.
Superclass ref = new Subclass(); // ref can only access superclass methods.
super
is used to:
- Call the superclass constructor.
- Access a superclass method or field that is hidden by the subclass.
-
Calling a Superclass Constructor: If a subclass needs to initialize fields from the superclass, it can call the superclass constructor using
super
.class BoxWeight extends Box { double weight; BoxWeight(double w, double h, double d, double m) { super(w, h, d); // Call superclass constructor weight = m; } }
-
Accessing Superclass Methods or Fields: When a subclass hides a superclass member with the same name, you can use
super
to refer to the superclass version.super.member; // Access the superclass method or field.
In a multilevel class hierarchy, super()
always refers to the constructor of the immediate superclass. Each constructor in the chain must call the constructor of its superclass.
class Box {
private double width, height, depth;
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
}
class BoxWeight extends Box {
double weight;
BoxWeight(double w, double h, double d, double m) {
super(w, h, d); // Call Box constructor
weight = m;
}
}
- super() must be the first statement in the subclass constructor if used.
- If a superclass constructor requires parameters, the subclass must pass them via
super()
.
The final
keyword can be used with methods and classes to prevent further inheritance or modification.
-
Prevent Overriding: Marking a method as
final
prevents subclasses from overriding it.final void show() { // cannot be overridden }
-
Prevent Inheritance: Marking a class as
final
prevents it from being extended.final class MyClass { // no other class can extend MyClass }
-
Performance: Declaring methods as
final
allows for early binding (method calls resolved at compile time), improving performance through inlining. -
Note on Static Methods: Static methods are class-level and are not subject to overriding. Thus, polymorphism does not apply to static methods.
- Polymorphism: In Java, polymorphism applies to methods, not fields. Thus, instance variables are not subject to polymorphism.
- Default Constructor: If
super()
is not used, the default or parameterless constructor of each superclass is called automatically.
class Box {
private double width, height, depth;
Box(Box ob) { // Constructor with object parameter
width = ob.width;
height = ob.height;
depth = ob.depth;
}
}
class BoxWeight extends Box {
double weight;
BoxWeight(BoxWeight ob) {
super(ob); // Call superclass constructor
weight = ob.weight;
}
}
In the above code, super(ob)
calls the Box
constructor, passing a BoxWeight
object, which invokes the constructor of the superclass.
By understanding these core concepts, you can effectively utilize inheritance, super
, and final
in Java to build efficient and scalable object-oriented programs.
Method overloading allows multiple methods in the same class to have the same name but different parameter lists. Here's a detailed explanation:
-
Same Method Name, Different Parameters: You can define multiple methods with the same name in a class as long as their parameters (number, types, or order) differ.
-
Different Return Types: While overloaded methods can have different return types, the return type alone does not distinguish overloaded methods.
-
Method Resolution: When a method is called, Java determines which method to invoke based on the argument list provided. It selects the best match according to the method's parameter types.
-
Automatic Type Conversion: If no exact match is found, Java will attempt to convert the arguments using its automatic type conversion rules (e.g.,
int
todouble
).
In this example, the method test(double)
is invoked even though test(int)
is not defined. This happens because Java automatically converts the int
argument to double
.
class OverloadDemo {
void test(double a) {
System.out.println("Inside test(double) a: " + a);
}
}
class Overload {
public static void main(String args[]) {
OverloadDemo ob = new OverloadDemo();
int i = 88;
// Automatic type conversion from int to double
ob.test(i); // Calls test(double) with i converted to double
ob.test(123.2); // Calls test(double) with a double argument
}
}
Explanation:
- The
test(int)
method is not defined inOverloadDemo
. - When
ob.test(i)
is called with anint
argument, Java automatically convertsi
to adouble
and callstest(double)
.
In Java, a method can return an object. This is useful when you want a method to return complex data or objects with multiple fields.
-
Returning Object References: When a method returns an object, it returns a reference to the object created within the method.
-
Dynamic Object Allocation: Since objects are dynamically allocated using
new
, they remain in memory as long as there are references to them, even after the method terminates. -
Garbage Collection: Once there are no references to an object, it becomes eligible for garbage collection, meaning the memory is freed.
In the following example, the incrByTen()
method returns a new Test
object with its a
field incremented by 10:
class Test {
int a;
// Constructor to initialize the 'a' field
Test(int i) {
a = i;
}
// Method that creates and returns a new object with 'a' incremented by 10
Test incrByTen() {
Test temp = new Test(a + 10);
return temp; // Return the new object
}
}
class RetOb {
public static void main(String args[]) {
Test ob1 = new Test(2); // Create a new object with 'a' initialized to 2
Test ob2;
// Call incrByTen() which returns a new Test object
ob2 = ob1.incrByTen();
System.out.println("ob1.a: " + ob1.a); // Output: 2
System.out.println("ob2.a: " + ob2.a); // Output: 12
}
}
Explanation:
- Object Creation: The
incrByTen()
method creates a newTest
object witha
incremented by 10. - Returning the Object: The method returns the reference to the newly created object. This reference is assigned to
ob2
. - Output:
ob1.a
remains 2 becauseob1
is unchanged.ob2.a
becomes 12 as it references the new object witha
incremented.
- Automatic Type Conversion: If no exact match is found during method overloading, Java attempts to convert the argument type (e.g.,
int
todouble
). - Returning Objects: When an object is returned from a method, it continues to exist as long as there is a reference to it, even after the method terminates.
- Garbage Collection: When no references to an object remain, it is eligible for garbage collection, and its memory is freed.
These concepts are crucial for writing efficient, flexible, and reusable code in Java.
Method overriding occurs when a subclass defines a method with the same name, return type, and parameters as a method in its superclass. The overridden method in the subclass hides the method in the superclass. When the method is called using a subclass object, the subclass version is executed, even if the reference is of the superclass type.
-
Same Name, Return Type, and Parameters: The method in the subclass must have the exact same name, return type, and parameters as the method in the superclass.
-
Method Resolution at Runtime: Overridden methods are resolved at runtime using dynamic method dispatch, which allows Java to achieve runtime polymorphism.
-
Superclass Reference to Subclass Object: A reference variable of the superclass can point to an object of the subclass. The actual method executed depends on the object type, not the reference type.
-
Return Type in Overriding: In overriding, the return type must be the same or a covariant return type (a subclass of the return type of the overridden method).
Let's look at an example where a method in a subclass overrides a method in its superclass:
// Superclass
class A {
// Method to be overridden
void display() {
System.out.println("Display from class A");
}
}
// Subclass
class B extends A {
// Overriding the display method
@Override
void display() {
System.out.println("Display from class B");
}
}
class TestOverride {
public static void main(String[] args) {
A a = new A(); // Reference and object of class A
B b = new B(); // Reference and object of class B
A ref; // Reference variable of type A
ref = a; // ref refers to object of class A
ref.display(); // Calls A's display method
ref = b; // ref refers to object of class B (upcasting)
ref.display(); // Calls B's display method (overridden)
}
}
Explanation:
- The
display()
method in classB
overrides thedisplay()
method in classA
. - When the reference
ref
of typeA
refers to an object of classB
, the overriddendisplay()
method inB
is called.
Output:
Display from class A
Display from class B
Dynamic method dispatch is the process through which Java resolves calls to overridden methods at runtime. The decision on which method to execute depends on the actual object type that the reference points to, rather than the reference type itself.
-
Runtime Polymorphism: Dynamic method dispatch allows for polymorphic behavior, meaning the same method call can invoke different method versions depending on the actual object type at runtime.
-
Superclass Reference: A superclass reference can refer to any object of a subclass. When a method is called using this reference, the method of the actual object's class is invoked.
// Superclass
class A {
// Method to be overridden
void display() {
System.out.println("Display from class A");
}
}
// Subclass
class B extends A {
// Overriding the display method
@Override
void display() {
System.out.println("Display from class B");
}
}
// Another Subclass
class C extends A {
// Overriding the display method
@Override
void display() {
System.out.println("Display from class C");
}
}
class TestDynamicDispatch {
public static void main(String[] args) {
A ref; // Superclass reference
B objB = new B();
C objC = new C();
// Dynamic method dispatch
ref = objB; // ref refers to an object of type B
ref.display(); // Calls B's display method
ref = objC; // ref refers to an object of type C
ref.display(); // Calls C's display method
}
}
Explanation:
- A superclass reference (
ref
) is used to point to objects of different subclasses (B
andC
). - The
display()
method that gets invoked depends on the object (objB
orobjC
) that the reference points to at runtime.
Output:
Display from class B
Display from class C
In method overriding, the return type of the overridden method in the subclass can be a subclass of the return type in the superclass. This is called a covariant return type.
// Superclass
class A {
A display() {
System.out.println("Display from class A");
return this;
}
}
// Subclass
class B extends A {
@Override
B display() {
System.out.println("Display from class B");
return this;
}
}
class TestCovariantReturn {
public static void main(String[] args) {
B b = new B();
b.display(); // Calls B's display method
}
}
Explanation:
- In class
A
, thedisplay()
method returns an object of typeA
. - In class
B
, the overriddendisplay()
method returns an object of typeB
, which is allowed becauseB
is a subclass ofA
.
Output:
Display from class B
- Method Overriding: When a subclass provides a specific implementation of a method that is already defined in its superclass.
- Dynamic Method Dispatch: The process through which the overridden method to be executed is determined at runtime based on the object's type.
- Covariant Return Types: In method overriding, the return type can be a subclass of the return type declared in the superclass method.
This enables Java's powerful runtime polymorphism, allowing methods to behave differently based on the actual object type at runtime.
Access control in Java is determined by access modifiers that specify the visibility of class members (fields and methods) across different contexts (classes, packages, and subclasses). The four main access levels are public, protected, private, and default (no modifier).
Here’s a breakdown of access levels across different contexts:
Modifier | Class | Package | Subclass (same pkg) | Subclass (diff pkg) | World |
---|---|---|---|---|---|
public | + | + | + | + | + |
protected | + | + | + | + | |
no modifier (default) | + | + | + | ||
private | + |
+
means accessible- Blank means not accessible
-
public
:- Accessible from anywhere (within the same class, package, subclass, or any other class in different packages).
-
protected
:- Accessible within the same class, package, and by subclasses, even if they are in different packages. However, the member can only be accessed by subclass objects, not by instances of the superclass from a different package.
-
default (no modifier)
:- Accessible only within the same package (package-private). Not accessible from outside the package, even by subclasses.
-
private
:- Accessible only within the same class. Not visible to other classes, subclasses, or outside packages.
Here’s a practical example of how protected access works in the context of different packages and inheritance.
// packageOne/Base.java
package packageOne;
public class Base {
protected void display() {
System.out.println("in Base");
}
}
// packageTwo/Derived.java
package packageTwo;
import packageOne.Base;
public class Derived extends Base {
public void show() {
// this is allowed, because we're in the subclass
display(); // Directly calling the inherited protected method
// this is allowed, because the method is being called on a Derived object
new Derived().display();
// this is NOT allowed, because we're creating an instance of Base (superclass),
// and outside of packageOne, the protected method cannot be called using a superclass reference
// new Base().display(); // Compile-time error!
}
}
display()
(direct call): This works because it is being called from within the subclass (Derived
), and theprotected
method is inherited.new Derived().display()
: This works because the method is called on an instance ofDerived
, andDerived
has access to theprotected
method of its superclass.new Base().display()
: This does not work because you are trying to access aprotected
method through aBase
instance outside thepackageOne
. Even thoughDerived
extendsBase
, theprotected
access does not apply to superclass instances from outside the package.
Protected members can be accessed from a subclass, but only through an instance of the subclass. The access to protected members of an instance of the superclass from outside the package is not allowed.
// packageOne/C.java
package packageOne;
public class C {
protected String message = "Protected message from class C";
}
// packageTwo/S.java
package packageTwo;
import packageOne.C;
public class S extends C {
public void accessProtected(C obj) {
// This works because 'message' is inherited by the S class
System.out.println(this.message); // Accessing inherited protected member
// This DOES NOT work because 'obj' is of type C, and we cannot access protected members through
// superclass instances outside the package.
// System.out.println(obj.message); // Compile-time error!
}
public static void main(String[] args) {
S sObj = new S();
C cObj = new C();
sObj.accessProtected(cObj); // Direct access to protected member via inheritance
}
}
- The
message
field in classC
isprotected
, so it can be accessed by subclasses ofC
, even if they are in different packages. - Direct Access (
this.message
): This works becauseS
inherits themessage
field. - Access via
C
instance (obj.message
): This does not work becauseobj
is of typeC
, andS
cannot access the protected member of an instance of the superclass from a different package.
Java enforces a rule that if an object obj
is an instance of a superclass C
and you are accessing a protected member, the static type of obj
must be exactly the subclass type (S
). If obj
is of type C
or another subclass S2
, accessing the protected member is not allowed.
// packageOne/C.java
package packageOne;
public class C {
protected void method() {
System.out.println("C's protected method");
}
}
// packageTwo/S.java
package packageTwo;
import packageOne.C;
public class S extends C {
public void callMethod(C obj) {
// This works, because we're calling the inherited method
this.method(); // Allowed
// This works, because obj is of type S (the current subclass)
if (obj instanceof S) {
((S)obj).method(); // Allowed due to explicit casting to subclass type
}
// This doesn't work, because obj is of type C (superclass)
// obj.method(); // Compile-time error!
}
}
this.method()
: Works because the subclassS
inherits the protected methodmethod()
from classC
.((S)obj).method()
: Works becauseobj
is explicitly cast toS
, so the static type becomesS
.obj.method()
: Does not work ifobj
is of typeC
, because access to protected methods is not allowed through superclass references from outside the package.
- Protected access allows subclass inheritance and access within the same package but restricts access through superclass instances from outside the package.
- The rule for protected access is that it’s only allowed when the object reference is of the subclass type or the class itself. Accessing protected members via superclass instances from a different package is not permitted.
In Java, abstract classes and interfaces are essential tools for defining the structure and behavior of classes in an object-oriented manner. Here's a detailed explanation of how abstract classes, methods, and interfaces work, highlighting their differences and practical use cases.
An abstract class in Java provides a base class that cannot be instantiated. It defines a generalized form to be shared by all its subclasses, leaving the implementation details to the subclasses. This allows you to define a template where certain behaviors must be implemented by subclasses.
- Abstract Methods: These methods have no implementation in the abstract class and must be overridden by any subclass.
abstract void methodName(); // No implementation
- Concrete Methods: Abstract classes can also include methods with complete implementations, allowing for partial implementation.
- Object Instantiation: You cannot instantiate an abstract class directly. However, you can declare references of the abstract class type.
- Constructor: Abstract classes can have constructors, but they are not used to create objects directly. Instead, they are used for initialization when a subclass is instantiated.
abstract class Animal {
abstract void makeSound(); // Abstract method
void sleep() {
System.out.println("Sleeping...");
}
}
class Dog extends Animal {
void makeSound() {
System.out.println("Bark");
}
}
- Mandatory Override: If a subclass does not implement all abstract methods, it must also be declared abstract.
- Static Methods: Abstract classes can have static methods, but static methods cannot be abstract.
- Visibility: Abstract classes can have private, protected, or public members.
An interface in Java is a completely abstract class that defines a contract for what a class can do without specifying how it does it. All methods in an interface are implicitly abstract until Java 8, where interfaces can also include default and static methods.
- Abstract Methods: All methods in an interface are abstract by default (prior to Java 8). Subclasses must implement these methods.
- Default Methods: From Java 8 onwards, interfaces can also have default methods, which provide a method body that can be overridden.
- Static Methods: Interfaces can also contain static methods, which must have implementations.
- Final Variables: All variables in an interface are implicitly
public
,static
, andfinal
.
interface Animal {
void makeSound(); // Abstract method
}
class Cat implements Animal {
public void makeSound() {
System.out.println("Meow");
}
}
Feature | Abstract Class | Interface |
---|---|---|
Methods | Can have both abstract and concrete methods. | Only abstract methods (until Java 8). Java 8+ allows default and static methods. |
Final Variables | Can have non-final and final variables. | All variables are public , static , and final by default. |
Implementation | Can implement methods of an interface. | Cannot implement methods of an abstract class. |
Inheritance | Can extend one class and implement multiple interfaces. | Can extend multiple interfaces. |
Access Modifiers | Can have private, protected, or public methods and variables. | All members are public by default. |
Object Instantiation | Cannot be instantiated directly but can have constructors. | Cannot be instantiated and does not have constructors. |
In Java, a class can only inherit from one superclass but can implement multiple interfaces, allowing multiple inheritance of behavior. Interfaces provide a way to define contracts for classes to follow, while abstract classes allow for shared code and partial implementation.
If you're designing a set of behaviors that all subclasses must follow, and some methods can have a common implementation, an abstract class is appropriate.
abstract class Bird {
abstract void fly();
void sleep() {
System.out.println("Bird is sleeping...");
}
}
class Sparrow extends Bird {
void fly() {
System.out.println("Sparrow flies");
}
}
If you want to define a contract for a class but don't care about how the behavior is implemented, an interface is more suitable.
interface Flyable {
void fly();
}
class Airplane implements Flyable {
public void fly() {
System.out.println("Airplane is flying");
}
}
- Use an abstract class when you want to provide a base class with some common behavior that other classes can inherit and possibly override.
- Use an interface when you want to specify that a class must implement certain behaviors, without enforcing how those behaviors are implemented.
These tools are essential for creating flexible, reusable, and maintainable code in Java.
In Java, multiple inheritance is not allowed for classes, meaning a class cannot directly inherit from more than one class. This is to avoid issues such as the "diamond problem," where two parent classes might provide different implementations of the same method, leading to ambiguity for the subclass.
To achieve multiple inheritance of behavior, Java uses interfaces. Unlike classes, interfaces allow a class to "inherit" abstract method signatures from multiple sources. Here's how interfaces work:
-
Interface Basics: An interface is a reference type, similar to a class, but it can only contain method signatures (abstract methods) and constants (static final variables). All methods in an interface are implicitly
public
andabstract
, and all variables arepublic
,static
, andfinal
. -
Differences from Classes:
- A class can have state (through instance variables), but an interface cannot.
- A class can implement multiple interfaces, allowing for multiple inheritance of types.
-
Abstract Methods: All methods in an interface are abstract by default (without implementation), but starting from Java 8, interfaces can include default methods and static methods with actual implementations.
-
Final and Static Variables: Variables declared in an interface are implicitly
final
andstatic
, meaning they are constants and shared across all instances of implementing classes. -
Class vs Interface:
- A class can maintain state and define both abstract and concrete methods.
- An interface defines behavior without specifying how that behavior is implemented. Classes that implement an interface are responsible for providing the method bodies.
-
Multiple Inheritance with Interfaces:
- A class can only extend one other class, but it can implement multiple interfaces. This allows a class to inherit behaviors from multiple sources while avoiding ambiguity found in multiple class inheritance.
-
Default Methods (Java 8+): An interface can provide a default implementation of a method using the
default
keyword. This allows interfaces to evolve by adding new methods without breaking existing code that implements the interface. -
Static Methods in Interfaces: Starting with Java 8, interfaces can also have static methods with bodies, but these methods are not inherited by implementing classes or subinterfaces.
interface A {
// abstract method
void doSomething();
// default method
default String getDefault() {
return "Default implementation";
}
// static method
static String getStatic() {
return "Static method in interface";
}
}
class B implements A {
// overriding the abstract method
public void doSomething() {
System.out.println("Doing something in class B");
}
// class B can call the default method if not overridden
}
public class InterfaceDemo {
public static void main(String[] args) {
B b = new B();
b.doSomething(); // Calls B's implementation
System.out.println(b.getDefault()); // Calls default implementation from interface A
System.out.println(A.getStatic()); // Calls static method from interface A
}
}
- Nested Interface: You can define an interface inside a class or another interface. Nested interfaces can have any access modifier (
public
,private
, etc.), unlike top-level interfaces, which must either bepublic
or use the default package access.
Example:
class OuterClass {
// Nested interface
public interface NestedInterface {
boolean isPositive(int x);
}
}
class InnerClass implements OuterClass.NestedInterface {
public boolean isPositive(int x) {
return x > 0;
}
}
public class NestedInterfaceDemo {
public static void main(String[] args) {
OuterClass.NestedInterface obj = new InnerClass();
System.out.println(obj.isPositive(10)); // Outputs: true
}
}
If a class implements two interfaces that have the same default method, the implementing class must override that method to avoid ambiguity.
interface X {
default void method() {
System.out.println("Interface X");
}
}
interface Y {
default void method() {
System.out.println("Interface Y");
}
}
class Z implements X, Y {
// Must override the conflicting method
public void method() {
System.out.println("Class Z overrides method");
}
}
public class MultipleInheritanceDemo {
public static void main(String[] args) {
Z obj = new Z();
obj.method(); // Output: Class Z overrides method
}
}
Feature | Abstract Class | Interface |
---|---|---|
Methods | Can have both abstract and concrete methods | Can have only abstract methods (Java 8+: default and static methods allowed) |
Variables | Can have instance variables, static and non-static variables | Only static and final variables |
Inheritance | Can extend one class | Can implement multiple interfaces |
Constructors | Can have constructors | Cannot have constructors |
Multiple Inheritance | Not supported | Supported via multiple interfaces |
Interfaces provide a flexible mechanism for defining shared behavior across different classes, while avoiding the complexity of multiple inheritance.
In Java, enumerations (enums) represent a list of named constants, but unlike in other languages, enums in Java are much more powerful because they define a class type. This capability allows enums to have additional functionality such as fields, methods, and constructors. Here’s a breakdown of important concepts and capabilities of enums in Java:
Enums are defined using the enum
keyword and can exist either inside or outside a class but not within methods.
enum Color {
RED, BLUE, GREEN;
}
public class EnumExample {
public static void main(String[] args) {
Color c1 = Color.RED;
System.out.println(c1); // Output: RED
}
}
Internally, enums are implemented as a class, where each constant is an instance of that class.
// Equivalent internal representation of enum Color
class Color {
public static final Color RED = new Color();
public static final Color BLUE = new Color();
public static final Color GREEN = new Color();
}
- Enums implicitly extend
java.lang.Enum
, which means they cannot extend any other class. Java does not support multiple inheritance, so enums cannot be a superclass either. - However, enums can implement interfaces, which allows them to behave polymorphically.
values()
: This method returns an array of all enum constants.for (Color color : Color.values()) { System.out.println(color); }
ordinal()
: This returns the position (index) of the enum constant, starting from zero.Color c1 = Color.RED; System.out.println(c1.ordinal()); // Output: 0
valueOf(String name)
: This method returns the enum constant matching the string provided.Color c2 = Color.valueOf("BLUE"); System.out.println(c2); // Output: BLUE
Enums can have constructors, but they are implicitly private or package-private. You cannot create new instances of enum outside the enum definition.
enum Fruit {
APPLE(100), BANANA(80), ORANGE(90);
private int price;
// Enum constructor
Fruit(int price) {
this.price = price;
}
public int getPrice() {
return price;
}
}
public class EnumConstructorExample {
public static void main(String[] args) {
System.out.println(Fruit.APPLE.getPrice()); // Output: 100
}
}
- Why are enum constructors private?: To prevent creating new instances, since enum constants are single instances and represent a fixed set of values.
Enums can contain concrete methods but no abstract methods. The toString()
method in java.lang.Enum
is overridden to return the constant name.
enum Status {
STARTED, IN_PROGRESS, COMPLETED;
public void display() {
System.out.println("Current Status: " + this);
}
}
public class EnumMethodExample {
public static void main(String[] args) {
Status s1 = Status.IN_PROGRESS;
s1.display(); // Output: Current Status: IN_PROGRESS
}
}
-
You can compare two enum constants using the
==
operator, as they are single instances (singletons).if (Color.RED == Color.BLUE) { System.out.println("Different colors"); } else { System.out.println("Same color"); }
-
equals()
: You can also compare two enum constants usingequals()
, but they will only be considered equal if they belong to the same enum and refer to the same constant.
You can have a main()
method within an enum and invoke it directly from the command line.
enum TestEnum {
RED, BLUE;
public static void main(String[] args) {
System.out.println("Enum main method invoked");
for (TestEnum t : TestEnum.values()) {
System.out.println(t);
}
}
}
To run:
javac TestEnum.java
java TestEnum
You can declare an enum inside a class or even inside another enum, known as nested enums. However, enums declared within a class can have any access modifier (public, private, protected), but top-level enums can only be public or package-private.
class OuterClass {
enum Day {
MONDAY, TUESDAY, WEDNESDAY;
}
}
public class NestedEnumExample {
public static void main(String[] args) {
OuterClass.Day day = OuterClass.Day.MONDAY;
System.out.println(day); // Output: MONDAY
}
}
Enums can implement interfaces, allowing them to define behavior across enum constants.
interface Printable {
void print();
}
enum Shape implements Printable {
CIRCLE, SQUARE;
@Override
public void print() {
System.out.println(this + " Shape");
}
}
public class EnumWithInterfaceExample {
public static void main(String[] args) {
Shape.CIRCLE.print(); // Output: CIRCLE Shape
}
}
If a class implements two interfaces that define the same default method, the class must override it. However, in case one interface extends another, the most derived version of the default method takes precedence.
Static methods in enums must have a body, and they are not inherited by implementing classes or subinterfaces.
- Enums in Java are more powerful than basic enumerations found in other languages, as they can contain fields, methods, and constructors.
- They implicitly extend
java.lang.Enum
, meaning they cannot extend other classes. - They can implement interfaces, and each enum constant can hold specific behavior.
- Enum methods include
values()
,ordinal()
, andvalueOf()
. - Enums provide strong type safety and are ideal for representing a fixed set of constants.
Enums are not just constants; they allow the representation of behaviors alongside constant values, giving more flexibility to how we use them in Java programs.
Here are a few additional points you can add to your cheat sheet to cover some common interview topics that could be beneficial:
- Definition: Getters and setters are methods used to access and modify private fields of a class.
- Usage:
- Getter: Returns the value of a private attribute.
public String getName() { return this.name; }
- Setter: Sets the value of a private attribute.
public void setName(String name) { this.name = name; }
- Getter: Returns the value of a private attribute.
- Encapsulation: These methods support encapsulation, by allowing controlled access to class attributes.
- Method Overloading: Multiple methods with the same name but different parameters (compile-time polymorphism).
- Method Overriding: Subclass provides a specific implementation for a method that is already defined in its superclass (runtime polymorphism).
- IS-A: Inheritance; example: a
Dog
IS-AAnimal
. - HAS-A: Composition; example: a
Car
HAS-AEngine
.
- Single Responsibility Principle: A class should have one, and only one, reason to change.
- Open/Closed Principle: Software entities should be open for extension but closed for modification.
- Liskov Substitution Principle: Subtypes should be substitutable for their base types without affecting correctness.
- Interface Segregation Principle: No client should be forced to depend on methods it does not use.
- Dependency Inversion Principle: High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Shallow Copy: Copies the fields of an object but does not copy objects referenced by those fields.
- Deep Copy: Copies the fields of an object and all objects referenced by those fields, creating an independent object.
- Checked Exceptions: Must be handled at compile-time.
- Unchecked Exceptions: Errors that occur at runtime.
- Commonly Asked: Explain how exceptions like
NullPointerException
,IndexOutOfBoundsException
, andIOException
work.
- Singleton: Ensures a class has only one instance.
- Factory: Creates objects without exposing the instantiation logic.
- Observer: Defines a one-to-many dependency between objects.
- Decorator: Dynamically adds functionality to an object without affecting other objects.
- Final Class: Cannot be subclassed.
- Final Method: Cannot be overridden by subclasses.
- Final Variables: Its value cannot be changed once initialized.
- Static Variables: Shared across all instances of a class.
- Static Methods: Can be called without creating an instance of the class.
- String: Immutable, meaning any change creates a new string object.
- StringBuilder: Mutable, used when modifications are needed, not thread-safe.
- StringBuffer: Mutable, thread-safe, used in multi-threaded environments.
These are a few important topics to enhance your cheat sheet. Let me know if you'd like more examples or detailed notes on any of these!