Java supports three types of variables:
void myMethod() { int localVar = 5; // Local variable }
public class MyClass { int instanceVar; // Instance variable }
public class MyClass { static int staticVar; // Static variable }
Example: String s1 = new String("hello"); String s2 = new String("hello"); System.out.println(s1 == s2); // false System.out.println(s1.equals(s2)); // true
final: A keyword used to declare constants, prevent inheritance, or prevent method overriding.
final int CONSTANT = 100;
finally: A block used in exception handling to execute important code such as closing resources, irrespective of whether an exception is handled or not.
try { // code } finally { // cleanup code } finalize(): A method called by the garbage collector before an object is destroyed. protected void finalize() { // cleanup code }
Example: int anInt = 10; boolean aBoolean = true; char aChar = 'A';
Example: int a = 10; float b = 5.5f; float result = a + b; // int 'a' is promoted to float
Example: double d = 10.5; int i = (int) d; // Explicit casting
Implicit casting happens automatically when converting a smaller type to a larger type.
int i = 100; long l = i; // Implicit casting Explicit casting is needed when converting a larger type to a smaller type. double d = 100.04; int i = (int) d; // Explicit casting
int number = 10; if (number > 0) { System.out.println("Positive number"); } else { System.out.println("Non-positive number"); }
In simple words, the Java switch statement executes one statement from multiple conditions.
int day = 3; switch (day) { case 1: System.out.println("Monday"); break; case 2: System.out.println("Tuesday"); break; case 3: System.out.println("Wednesday"); break; default: System.out.println("Invalid day"); break; }
The ternary operator is a shorthand for the if-else statement. It has the form condition ? expr1 : expr2.
Example: int a = 10, b = 20; int min = (a < b) ? a : b; // Returns the smaller of a and b
It contains several branches with an if condition inside another if condition ...
Example: int number = 10; if (number > 0) { if (number % 2 == 0) { System.out.println("Positive even number"); } else { System.out.println("Positive odd number"); } } else { System.out.println("Non-positive number"); }
for (int i = 0; i < 10; i++) { if (i==5) { break; // Exit the loop when i equals 5 } System.out.println(i); }
An array is declared by specifying its data type followed by an identifier, and in Java, this process is similar to declaring other variables, but with an additional pair of brackets [] to indicate that it is an array.
Example: int[] arr = new int[5]; // Declaration and instantiation arr[0] = 10; // Initialization
You can iterate over an array using a for loop or a forEach loop. Using a for loop, you can iterate through the array indices from 0 to the array's length (ArrayName.length), accessing and processing each element at the corresponding index.
Example: int[] arr = {1, 2, 3, 4, 5}; for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); }
int[] arr = {1, 2, 3, 4, 5}; int length = arr.length;
A multidimensional array is an array of arrays.
int[][] arr = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
import java.util.Arrays; int[] arr = {5, 3, 8, 1, 2}; Arrays.sort(arr); // Sorts the array in ascending order
A class is a blueprint for objects. An object is an instance of a class.
Example: public class MyClass { int value; public MyClass(int value) { this.value = value; } } public class Main { public static void main(String[] args) { MyClass obj = new MyClass(10); // Creating an object } }
Constructors are special methods used to initialize objects. They have the same name as the class and no return type.
Example: public class MyClass { int value; public MyClass(int value) { this.value = value; // Constructor } }
Inheritance allows a class to inherit properties and methods from another class.
Example: public class Animal { void eat() { System.out.println("Eating..."); } } public class Dog extends Animal { void bark() { System.out.println("Barking..."); } } public class Main { public static void main(String[] args) { Dog dog = new Dog(); dog.eat(); // Inherited method dog.bark(); // Own method } }
Method overriding allows a subclass to provide a specific implementation of a method already defined in its superclass.
Example: public class Animal { void sound() { System.out.println("Animal makes a sound"); } } public class Dog extends Animal { @Override void sound() { System.out.println("Dog barks"); } } public class Main { public static void main(String[] args) { Dog dog = new Dog(); dog.sound(); // Outputs: Dog barks } }
public class MyClass { void display(int a) { System.out.println(a); } void display(String a) { System.out.println(a); } }• Method Overriding: Same method name and parameters, but different implementations in the parent and child classes.
Example: public class Animal { void sound() { System.out.println("Animal makes a sound"); } } public class Dog extends Animal { @Override void sound() { System.out.println("Dog barks"); } }
Java provides four types of access modifiers: public, protected, default (no keyword), and private.
The final modifier can be used with variables, methods, and classes. For variables, it makes them constants; for methods, it prevents overriding; and for classes, it prevents inheritance.
The static modifier is used to define class-level variables and methods, which can be accessed without creating an instance of the class.
The transient modifier is used to indicate that a field should not be serialized when the containing object is serialized
The synchronized modifier is used to control the access of multiple threads to a shared resource, ensuring that only one thread can access the resource at a time.
You can create a copy of an object by implementing the Cloneable interface and overriding the clone() method.
Type casting is converting one data type into another. You can perform it explicitly by placing the target type in parentheses before the value to be converted.
A shallow copy copies an object's fields as they are, whereas a deep copy creates a copy of the object along with the objects referenced by its fields.
Garbage collection in Java is an automatic process that deallocates memory by identifying and disposing of objects that are no longer referenced.
Java provides several garbage collectors, including Serial, Parallel, CMS (Concurrent Mark-Sweep), and G1 (Garbage-First).
An abstract class is a class that cannot be instantiated and is meant to be subclassed. It can have abstract methods (without implementation) and concrete methods (with implementation).
An abstract method is defined using the abstract keyword and does not have a body.
Example: abstract void myMethod();
Yes, an abstract class can have a constructor, which can be used to initialize common fields for subclasses.
An abstract class can have both abstract and concrete methods, while an interface can only have abstract methods (prior to Java 8). An abstract class can have fields and constructors, whereas an interface cannot (prior to Java 8).
You inherit an abstract class by using the extends keyword and providing implementations for all abstract methods.
Example: public class MyClass extends MyAbstractClass { @Override void myMethod() { // Implementation } }
The extends keyword is used to inherit a class. It indicates that the class is a subclass of a specified superclass.
A private member is accessible only within the class it is declared.
A protected member is accessible within its own package and by subclasses outside the package.
Class inheritance allows a class to inherit properties and methods from another class.
Example: class Animal { void eat() { System.out.println("Eating..."); } } class Dog extends Animal { void bark() { System.out.println("Barking..."); } }
You can restrict inheritance by using the final keyword with a class.
Example: public final class MyClass { // Class body }
The super keyword is used to refer to the immediate parent class object. It can be used to access parent class methods, constructors, and fields.
You call a parent class constructor using super() in the subclass constructor.
Example: class Parent { Parent() { System.out.println("Parent constructor"); } } class Child extends Parent { Child() { super(); System.out.println("Child constructor"); } }
When applied to a class, the final keyword prevents the class from being subclassed.
When applied to a method, the final keyword prevents the method from being overridden in subclasses.
No, once a final variable has been assigned a value, it cannot be changed.
Java does not support multiple inheritance to avoid complexity and simplify the language, preventing issues like the "diamond problem."
The diamond problem occurs when a class inherits from two classes that have a common superclass, leading to ambiguity in the inheritance hierarchy.
Java allows multiple inheritance through interfaces, as interfaces only provide method signatures without implementation, avoiding the diamond problem.
Yes, a class can implement multiple interfaces.
Example: class MyClass implements Interface1, Interface2 { // Implement methods from both interfaces }
Using interfaces allows for a more flexible and decoupled design, enabling multiple inheritance without the complexities associated with class inheritance.
Polymorphism in Java is the ability of an object to take on many forms. It allows one interface to be used for a general class of actions. The most common use of polymorphism in Java is when a parent class reference is used to refer to a child class object.
Example: class Animal { void sound() { System.out.println("Animal makes a sound"); } } class Dog extends Animal { @Override void sound() { System.out.println("Dog barks"); } } class Cat extends Animal { @Override void sound() { System.out.println("Cat meows"); } } public class Main { public static void main(String[] args) { Animal myDog = new Dog(); Animal myCat = new Cat(); myDog.sound(); // Outputs: Dog barks myCat.sound(); // Outputs: Cat meows } }
There are two types of polymorphism in Java:
Method overloading occurs when two or more methods in the same class have the same name but different parameters.
Example: class MathOperation { int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } int add(int a, int b, int c) { return a + b + c; } } public class Main { public static void main(String[] args) { MathOperation operation = new MathOperation(); System.out.println(operation.add(5, 3)); // Outputs: 8 System.out.println(operation.add(5.5, 3.5)); // Outputs: 9.0 System.out.println(operation.add(1, 2, 3)); // Outputs: 6 } }
Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass.
Example: class Vehicle { void run() { System.out.println("Vehicle is running"); } } class Bike extends Vehicle { @Override void run() { System.out.println("Bike is running"); } } public class Main { public static void main(String[] args) { Vehicle myVehicle = new Bike(); myVehicle.run(); // Outputs: Bike is running } }
Runtime polymorphism in Java is achieved through method overriding. When an overridden method is called through a reference of the parent class, Java determines which version (parent class or child class) of the method to execute based on the object being referred to. This is called dynamic method dispatch.
Example: class Shape { void draw() { System.out.println("Drawing a shape"); } } class Circle extends Shape { @Override void draw() { System.out.println("Drawing a circle"); } } class Rectangle extends Shape { @Override void draw() { System.out.println("Drawing a rectangle"); } } public class Main { public static void main(String[] args) { Shape myShape; myShape = new Circle(); myShape.draw(); // Outputs: Drawing a circle myShape = new Rectangle(); myShape.draw(); // Outputs: Drawing a rectangle } }
A package in Java is a namespace that organizes a set of related classes and interfaces. Packages are used to avoid name conflicts, to control access, to make searching/locating and usage of classes, interfaces, enumerations, and annotations easier, and to group related classes together.
Example: package com.example.mypackage; public class MyClass { public void display() { System.out.println("Hello from MyClass in com.example.mypackage"); } }
The import statement is used to bring one or more classes into visibility, allowing you to refer to them without specifying their full package names. A fully qualified name is the complete name of a class including its package name.
Example: // Using import statement import java.util.ArrayList; public class Test { ArrayListlist = new ArrayList<>(); } // Using fully qualified name public class Test { java.util.ArrayList list = new java.util.ArrayList<>(); }
If no package statement is specified, the classes in the source file belong to an unnamed package, which is also called the default package. It is generally not recommended for large-scale applications because it does not provide any namespace management.
Example: // No package declaration, belongs to the default package public class MyClass { public void display() { System.out.println("Hello from the default package"); } }
Yes, you can use the wildcard character (*) to import all classes from a package.
Example: import java.util.*; public class Test { ArrayListlist = new ArrayList<>(); HashMap map = new HashMap<>(); }
An interface in Java is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. Unlike abstract classes, interfaces cannot have any method implementations (prior to Java 8).
Example: interface Animal { void sound(); } class Dog implements Animal { public void sound() { System.out.println("Dog barks"); } }
Yes, an interface can extend another interface. This is similar to how a class can inherit another class.
Example: interface Animal { void sound(); } interface Pet extends Animal { void play(); } class Dog implements Pet { public void sound() { System.out.println("Dog barks"); } public void play() { System.out.println("Dog plays"); } }
Default methods are methods defined in interfaces with the default keyword and can have a body. They were introduced in Java 8 to allow adding new methods to interfaces without breaking the existing implementations.
Example: interface Animal { void sound(); default void breathe() { System.out.println("Animal breathes"); } } class Dog implements Animal { public void sound() { System.out.println("Dog barks"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.sound(); // Outputs: Dog barks dog.breathe(); // Outputs: Animal breathes } }
Yes, interfaces can contain fields, but they are implicitly public, static, and final, meaning they are constants.
Example: interface Constants { int MAX_VALUE = 100; } public class Test { public static void main(String[] args) { System.out.println(Constants.MAX_VALUE); // Outputs: 100 } }
A class can implement multiple interfaces by listing them separated by commas in the implements clause.
Example: interface Animal { void sound(); } interface Pet { void play(); } class Dog implements Animal, Pet { public void sound() { System.out.println("Dog barks"); } public void play() { System.out.println("Dog plays"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.sound(); // Outputs: Dog barks dog.play(); // Outputs: Dog plays } }
Exception handling in Java is a mechanism to handle runtime errors, allowing the normal flow of the program to be maintained. It uses try, catch, throw, throws, and finally blocks.
Example: public class Example { public static void main(String[] args) { try { int division = 10 / 0; } catch (ArithmeticException e) { System.out.println("ArithmeticException: Division by zero."); } finally { System.out.println("This block is always executed."); } } }
Checked exceptions are exceptions that are checked at compile-time, while unchecked exceptions are checked at runtime. Checked exceptions must be either caught or declared in the method using throws keyword.
Example: // Checked exception example import java.io.*; public class Example { public static void main(String[] args) { try { FileReader file = new FileReader("nonexistentfile.txt"); } catch (FileNotFoundException e) { System.out.println("FileNotFoundException: File not found."); } } } // Unchecked exception example public class Example { public static void main(String[] args) { try { int division = 10 / 0; } catch (ArithmeticException e) { System.out.println("ArithmeticException: Division by zero."); } } }
The finally block provides a mechanism to execute crucial code, such as resource cleanup, regardless of whether an exception is thrown or not.
Example: public class Example { public static void main(String[] args) { try { int division = 10 / 0; } catch (ArithmeticException e) { System.out.println("ArithmeticException: Division by zero."); } finally { System.out.println("This block is always executed."); } } }
Custom exceptions can be created by extending the Exception class.
Example: class CustomException extends Exception { public CustomException(String message) { super(message); } } public class Example { public static void main(String[] args) { try { throw new CustomException("This is a custom exception."); } catch (CustomException e) { System.out.println(e.getMessage()); } } }
The throw keyword is used to explicitly throw an exception, while throws is used to declare an exception in the method signature.
Example: public class Example { public static void main(String[] args) { try { validate(15); } catch (Exception e) { System.out.println(e.getMessage()); } } static void validate(int age) throws Exception { if (age < 18) { throw new Exception("Age not valid"); } else { System.out.println("Welcome!"); } } }
A thread in Java is a lightweight process that allows concurrent execution of two or more parts of a program.
Example: class MyThread extends Thread { public void run() { System.out.println("Thread is running."); } } public class Example { public static void main(String[] args) { MyThread t1 = new MyThread(); t1.start(); } }
Implement the Runnable interface and pass an instance to a Thread object.
Example: class MyRunnable implements Runnable { public void run() { System.out.println("Thread is running."); } } public class Example { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread t1 = new Thread(myRunnable); t1.start(); } }
The synchronized keyword is used to control access to a method or a block of code by multiple threads.
Example: class Table { synchronized void printTable(int n) { for (int i = 1; i <= 5; i++) { System.out.println(n * i); try { Thread.sleep(400); } catch(InterruptedException e) { System.out.println(e); } } } } class MyThread extends Thread { Table t; MyThread(Table t) { this.t=t; } public void run() { t.printTable(5); } } public class Example { public static void main(String[] args) { Table obj=new Table(); MyThread t1=new MyThread(obj); MyThread t2=new MyThread(obj); t1.start(); t2.start(); } }
The start() method creates a new thread and calls the run() method to execute the code. Calling the run() method directly does not create a new thread but runs in the current thread.
Example: class MyThread extends Thread { public void run() { System.out.println("Thread is running."); } } public class Example { public static void main(String[] args) { MyThread t1 = new MyThread(); t1.start(); // Creates a new thread t1.run(); // Runs in the current thread } }
Thread deadlock occurs when two or more threads are waiting for each other to release resources. To prevent deadlock, you can use a timeout or a careful design to avoid circular dependencies.
Example: class Resource { synchronized void method1(Resource r) { System.out.println("Inside method1"); r.method2(); } synchronized void method2() { System.out.println("Inside method2"); } } public class Example { public static void main(String[] args) { Resource r1 = new Resource(); Resource r2 = new Resource(); Thread t1 = new Thread(() -> r1.method1(r2)); Thread t2 = new Thread(() -> r2.method1(r1)); t1.start(); t2.start(); } }
I/O streams in Java are used to perform input and output operations. They are categorized into byte streams and character streams.
Example: import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Example { public static void main(String[] args) throws IOException { FileInputStream in = new FileInputStream("input.txt"); FileOutputStream out = new FileOutputStream("output.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } in.close(); out.close(); } }
FileInputStream is used for reading raw byte data, while FileReader is used for reading character data.
Example: import java.io.FileReader; import java.io.IOException; public class Example { public static void main(String[] args) throws IOException { FileReader reader = new FileReader("input.txt"); int c; while ((c = reader.read()) != -1) { System.out.print((char) c); } reader.close(); } }
Exceptions in I/O operations are handled using try-catch blocks and can also use try-with-resources for automatic resource management.
Example: import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Example { public static void main(String[] args) { try (FileInputStream in = new FileInputStream("input.txt"); FileOutputStream out = new FileOutputStream("output.txt")) { int c; while ((c = in.read()) != -1) { out.write(c); } } catch (IOException e) { System.out.println("IOException: " + e.getMessage()); } } }
BufferedReader and BufferedWriter are used to read and write text from and to an input stream, respectively, with buffering to improve efficiency.
Example: import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Example { public static void main(String[] args) { try (BufferedReader reader = new BufferedReader(new FileReader("input.txt")); BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) { String line; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } } catch (IOException e) { System.out.println("IOException: " + e.getMessage()); } } }
Serialization in Java is the process of converting an object into a byte stream, allowing the object to be easily saved to a file or sent over a network. Deserialization is the reverse process.
Example: import java.io.*; class Person implements Serializable { private static final long serialVersionUID = 1L; String name; int age; Person(String name, int age) { this.name = name; this.age = age; } } public class Example { public static void main(String[] args) { try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"))) { Person person = new Person("John", 30); out.writeObject(person); } catch (IOException e) { System.out.println("IOException: " + e.getMessage()); } try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"))) { Person person = (Person) in.readObject(); System.out.println("Name: " + person.name + ", Age: " + person.age); } catch (IOException | ClassNotFoundException e) { System.out.println("Exception: " + e.getMessage()); } } }
The Java Collection Framework is a unified architecture for representing and manipulating collections, enabling collections to be easily extended and used.
Example: import java.util.ArrayList; import java.util.List; public class Example { public static void main(String[] args) { Listlist = new ArrayList<>(); list.add("Java"); list.add("Python"); list.add("C++"); for (String s : list) { System.out.println(s); } } }
The main interfaces are Collection, List, Set, Queue, Map, and their sub-interfaces like SortedSet, SortedMap, etc.
Example: import java.util.HashMap; import java.util.Map; public class Example { public static void main(String[] args) { Mapmap = new HashMap<>(); map.put(1, "Java"); map.put(2, "Python"); map.put(3, "C++"); for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } } }
ArrayList is based on a dynamic array, while LinkedList is based on a doubly linked list. ArrayList provides faster random access, while LinkedList provides better performance for insertion and deletion operations.
Example: import java.util.ArrayList; import java.util.LinkedList; public class Example { public static void main(String[] args) { ArrayListarrayList = new ArrayList<>(); arrayList.add("Java"); arrayList.add("Python"); LinkedList linkedList = new LinkedList<>(); linkedList.add("C++"); linkedList.add("JavaScript"); System.out.println("ArrayList: " + arrayList); System.out.println("LinkedList: " + linkedList); } }
HashSet is implemented using a hash table, and it does not guarantee any order of elements. TreeSet is implemented using a red-black tree and maintains elements in a sorted order.
Example: import java.util.HashSet; import java.util.TreeSet; public class Example { public static void main(String[] args) { HashSethashSet = new HashSet<>(); hashSet.add("Java"); hashSet.add("Python"); hashSet.add("C++"); TreeSet treeSet = new TreeSet<>(hashSet); System.out.println("HashSet: " + hashSet); System.out.println("TreeSet: " + treeSet); } }
The Map interface represents a collection of key-value pairs and does not allow duplicate keys. It provides methods to add, remove, and access elements based on keys.
Example: import java.util.HashMap; import java.util.Map; public class Example { public static void main(String[] args) { Mapmap = new HashMap<>(); map.put("Java", 1); map.put("Python", 2); map.put("C++", 3); for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } } }
Java 21 introduces various new features and enhancements, such as virtual threads, pattern matching for switch expressions, and improved serialization.
Example: // Virtual Threads Example (JEP 425) import java.util.concurrent.Executors; public class Example { public static void main(String[] args) { var executor = Executors.newVirtualThreadPerTaskExecutor(); for (int i = 0; i < 10; i++) { executor.submit(() -> { System.out.println("Running in a virtual thread"); }); } executor.shutdown(); } }
Pattern matching for switch expressions allows matching against types and extracting values in a more concise and readable way.
Example: public class Example { public static void main(String[] args) { Object obj = "Java"; String result = switch (obj) { case Integer i -> "Integer: " + i; case String s -> "String: " + s; default -> "Unknown type"; }; System.out.println(result); // Outputs: String: Java } }
Java 21 introduces improvements to serialization to make it more secure and efficient. These improvements help in reducing the risk of vulnerabilities.
Example: import java.io.*; class Person implements Serializable { private static final long serialVersionUID = 1L; String name; int age; Person(String name, int age) { this.name = name; this.age = age; } } public class Example { public static void main(String[] args) { try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"))) { Person person = new Person("John", 30); out.writeObject(person); } catch (IOException e) { System.out.println("IOException: " + e.getMessage()); } try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"))) { Person person = (Person) in.readObject(); System.out.println("Name: " + person.name + ", Age: " + person.age); } catch (IOException | ClassNotFoundException e) { System.out.println("Exception: " + e.getMessage()); } } }
Java 21 enhances the Records feature, allowing records to be more flexible and expressive, including features like allowing records to implement interfaces.
Example: public interface Greetable { String greet(); } public record Person(String name, int age) implements Greetable { @Override public String greet() { return "Hello, " + name; } } public class Example { public static void main(String[] args) { Person person = new Person("John", 30); System.out.println(person.greet()); // Outputs: Hello, John } }
Java 21 introduces several library improvements, such as new methods in the String class, enhancements to the Collections framework, and more.
Example: import java.util.List; public class Example { public static void main(String[] args) { // Example of new String methods String str = " Hello, Java "; System.out.println(str.stripLeading()); // Outputs: "Hello, Java " System.out.println(str.stripTrailing()); // Outputs: " Hello, Java" // Example of new Collections framework enhancements Listlist = List.of("Java", "Python", "C++"); list.forEach(System.out::println); } }