Join our community to see how developers are using Workik AI everyday.
Q6. Explain the concept of thread synchronization in Java.
Thread synchronization in Java is a mechanism to control the access of multiple threads to shared resources. It ensures that only one thread can access the resource at a time, preventing data inconsistency and race conditions.
Key Components of Thread Synchronization:
Q7. What are Java Streams and how are they used?
Java Streams, introduced in Java 8, are a powerful tool for processing sequences of elements in a functional style. They allow developers to perform complex data manipulations and transformations with concise and readable code. Streams support operations such as filtering, mapping, and reducing, and they can work with collections, arrays, and other data sources.
Key Concepts of Java Streams:
Example: Using Java Streams
Here is an example that combines several stream operations:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filter even numbers, square them, and collect the result into a list
List
evenSquares = numbers.stream()
.filter(n -> n % 2 == 0) // Intermediate operation: filter
.map(n -> n * n) // Intermediate operation: map
.collect(Collectors.toList()); // Terminal operation: collect
System.out.println(evenSquares); // Output: [4, 16, 36, 64, 100]
}
}
Here's a simplified flow chart of Stream Processing
Q8. What is the purpose of the
volatile
keyword in Java?
The
volatile
keyword in Java ensures visibility and ordering of variable updates across threads. It guarantees that:
However,
volatile
does not guarantee atomicity for compound actions like incrementing a variable. It is primarily used for flags or state variables accessed by multiple threads.
Q9. Describe the use of
try-with-resources
in Java.
The use of
try-with-resources
in Java
try
parentheses are automatically closed when the
try
block exits, regardless of whether the exit is due to normal execution or an exception.
try
keyword is followed by parentheses that declare one or more resources.
AutoCloseable
interface, which includes the
close()
method.
AutoCloseable
, such as
InputStream
,
OutputStream
,
Reader
,
Writer
,
Connection
,
Statement
, etc.
close()
is called on the resource, even if an exception occurs.
try
block and the
close()
method throw exceptions, the exception from the
try
block is suppressed, and the exception from the
close()
method is propagated.
finally
blocks to close resources.
try-with-resources
statement.
Q10. Explain the concept of lambda expressions in Java.
Lambda expressions in Java, introduced in Java 8, provide a clear and concise way to represent a method interface using an expression. They are used primarily to define the inline implementation of a functional interface. A functional interface is an interface with a single abstract method, and lambda expressions enable you to treat functionality as a method argument or store it in a variable.
(parameters) -> expression
or
(parameters) -> { statements }
Runnable
,
Callable
,
Comparator
, etc.
Example Usage
Sorting a List using Lambda Expression:
import java.util.Arrays;
import java.util.List;
public class SortExampleLambda {
public static void main(String[] args) {
List names = Arrays.asList("Peter", "Anna", "Mike", "Xenia");
names.sort((a, b) -> a.compareTo(b));
System.out.println(names);
}
}
Here's a simplified flow chart that illustrates usage of lambda expressions in sorting a list :
Request question
Please fill in the form below to submit your question.
Q11. What is the significance of the final keyword in Java?
The
final
keyword in Java is used to apply restrictions on classes, methods, and variables.
Significance of the
final
Keyword
final
variable is initialized, its value cannot be changed. This makes the variable a constant.
Final
variables ensure thread safety because their values cannot be modified after initialization, preventing data inconsistency in concurrent environments.
final int MAX_VALUE = 100;
final
method cannot be overridden by subclasses. This ensures that the original implementation remains unchanged and provides consistent behavior.
public final void display() {
System.out.println("Display method");
}
final
class cannot be subclassed. This is useful for creating immutable classes and for ensuring that the class’s implementation cannot be extended or altered.
Final
classes prevent subclassing, which can be important for security-sensitive applications to prevent unauthorized or insecure extensions.
public final class Utility {
// Class implementation
}
Q12. What are the differences between checked and unchecked exceptions in Java?
Q13. Explain the use of the transient keyword in Java.
The transient keyword in Java is used to indicate that a particular field should not be serialized. Serialization is the process of converting an object's state into a byte stream so that it can be saved to a file or transmitted over a network. When an object is serialized, all non-transient fields are included in the serialized representation.
Lets understand with code:
import java.io.*;
class User implements Serializable {
private static final long serialVersionUID = 1L;
String username;
transient String password; // This field will not be serialized
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
public class TransientExample {
public static void main(String[] args) {
User user = new User("admin", "password123");
// Serialize the user object
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
out.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize the user object
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.ser"))) {
User deserializedUser = (User) in.readObject();
System.out.println("Username: " + deserializedUser.username);
System.out.println("Password: " + deserializedUser.password); // This will print null
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Here's a flow chart that sums it all up
Q14. Describe the Singleton design pattern and how to implement it in Java.
The Singleton design pattern is a creational pattern that ensures a class has only one instance and provides a global point of access to it. The three common implementations in Java are:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Q15. What is Java Reflection and how is it used?
Java Reflection is a powerful feature that allows a Java program to inspect and manipulate the internal properties of classes, methods, and fields at runtime. It is part of the
java.lang.reflect
package.
How to Use Java Reflection
Class
Object
Class
object using various methods:
Class.forName("className")
object.getClass()
ClassName.class
Class
object to get details about the class.
Constructor
objects to create new instances.
Method
objects to invoke methods on instances.
Field
objects to read or write fields on instances.
Understand how it is used with below Flow Chart:
Request question
Please fill in the form below to submit your question.
Q16. Explain the concept of Generics in Java.
Generics in Java provide a way to create classes, interfaces, and methods that operate on types specified by the programmer at compile-time. They enhance code reusability, type safety, and readability.
Key Concepts
class Box<T> { }
<T> void add(T element) { }
T
is a type parameter that can be replaced with any class type when the class or method is used.
class Box<T extends Number> { }
means that
T
can only be a subclass of
Number
.
public <T> void printArray(T[] inputArray) { }
?
represents an unknown type. For example,
List<?>
can hold any type of
List
.
List<? extends Number>
means a list of objects that are instances of
Number
or its subclasses.
List<? super Integer>
means a list of objects that are instances of
Integer
or its superclasses.
Q17. What is the ExecutorService framework in Java?
The
ExecutorService
framework in Java is part of the
java.util.concurrent
package. It provides a higher-level replacement for managing and controlling threads. This framework simplifies the process of executing tasks asynchronously, managing a pool of threads, and handling their lifecycle.
Key Features
submit()
method.
Runnable
and
Callable
tasks, and it returns a
Future
object that represents the result of an asynchronous computation.
shutdown()
: Initiates an orderly shutdown where previously submitted tasks are executed, but no new tasks will be accepted.
shutdownNow()
: Attempts to stop all actively executing tasks and halts the processing of waiting tasks.
Flow chart of
ExecutorService
Usage:
Q18. How does Java handle multiple inheritance?
Java does not support multiple inheritance with classes to avoid complexity and ambiguity. However, it supports multiple inheritance through interfaces. A class can implement multiple interfaces, allowing the use of multiple types.
Lets understand through Flow Chart
Q19. Explain the use of
Stream
API with an example.
The
Stream
API, introduced in Java 8, provides a powerful way to process sequences of elements in a functional programming style. It allows for operations such as filtering, mapping, and reducing, enabling developers to write more readable and concise code.
Key Concepts
Streams
can be created from collections, arrays, or I/O resources.
stream
into another
stream
and are lazy (e.g.,
filter
,
map
).
stream
processing (e.g.,
forEach
,
collect
).
Example
Below is an example that demonstrates the use of the Stream API to filter, map, and collect elements from a list.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamAPIExample {
public static void main(String[] args) {
List names = Arrays.asList("John", "Jane", "Jack", "Doe");
// Filter names that start with 'J', convert to uppercase, and collect the result into a list
List result = names.stream()
.filter(name -> name.startsWith("J"))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(result); // Output: [JOHN, JANE, JACK]
}
}
Explanation
names
using
names.stream()
.
Q20. What are annotations in Java and how are they used?
Annotations in Java are a form of metadata that provide data about a program but are not part of the program itself. They have no direct effect on the operation of the code they annotate. Annotations can be used to provide information to the compiler, help with code analysis, or be processed at runtime by frameworks or tools.
How Annotations are Used
@Override
: Tells the compiler that a method is intended to override a method in a superclass.
@Deprecated
: Marks a method or class as deprecated, generating a warning if it is used.
@SuppressWarnings
: Suppresses specific compiler warnings.
Request question
Please fill in the form below to submit your question.
Q21. What is the difference between Comparable and Comparator in Java?
The difference in
Comparable
and
Comparator
are as follows:
Q17. What is the ExecutorService framework in Java?
Implementing a thread-safe singleton in Java can be achieved in several ways. One of the most common and efficient methods is using the Bill Pugh Singleton Design. However, other methods like double-checked locking and using the enum type are also effective.
getInstance()
is called.
public class Singleton {
private Singleton() {
// private constructor to prevent instantiation
}
private static class SingletonHelper {
// Nested class is referenced after getInstance() is called
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
volatile
keyword ensures that multiple threads handle the instance variable correctly.
if (instance == null)
) is not synchronized to improve performance.
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
// private constructor to prevent instantiation
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
public enum Singleton {
INSTANCE;
public void someMethod() {
// method implementation
}
}
Q23. Explain the concept of Autoboxing and Unboxing in Java.
Autoboxing and Unboxing are two key concepts in Java that allow automatic conversion between primitive data types and their corresponding wrapper classes. This feature simplifies code by enabling developers to work with primitives and objects seamlessly.
Autoboxing
Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes. For instance, converting an
int
to an
Integer
, or a
double
to a
Double
.
Unboxing
Unboxing is the reverse process where the Java compiler automatically converts an object of a wrapper class to its corresponding primitive type. For instance, converting an
Integer
to an
int
, or a
Double
to a
double
.
Let's look at a more comprehensive example that demonstrates both autoboxing and unboxing:
import java.util.ArrayList;
import java.util.List;
public class AutoboxingUnboxingExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
// Autoboxing: Adding primitive int to List of Integer
for (int i = 0; i < 5; i++) {
list.add(i); // int is autoboxed to Integer
}
System.out.println("List: " + list);
// Unboxing: Retrieving Integer from List and assigning to int
int sum = 0;
for (Integer num : list) {
sum += num; // Integer is unboxed to int
}
System.out.println("Sum: " + sum);
}
}
Q24. What is the Java
Optional
class and how is it used?
The
Optional
class in Java, introduced in Java 8, is a container object which may or may not contain a non-null value. It is used to represent optional values and provides a more expressive way to handle the presence or absence of values, thereby avoiding
NullPointerException
.
Key Methods of Optional
of()
: Creates an
Optional
with a specified non-null value.
ofNullable()
: Creates an
Optional
that may hold a null value.
empty()
: Returns an empty
Optional
.
isPresent()
: Returns true if the
Optional
contains a value, otherwise false.
ifPresent()
: Executes a given action if a value is present.
orElse()
: Returns the value if present, otherwise returns a default value.
orElseGet()
: Returns the value if present, otherwise invokes a
Supplier
and returns the result.
orElseThrow()
: Returns the value if present, otherwise throws an exception.
Q25. What are the differences between
HashMap
and
Hashtable
in Java?
The difference between
HashMap
and
Hashtable
in Java are as follows:
Request question
Please fill in the form below to submit your question.
Q1: Write a Java method to create a fixed thread pool using ExecutorService and submit tasks that print numbers from 1 to 10. Ensure the proper shutdown of the executor service.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 10; i++) {
final int num = i;
executor.submit(() -> {
System.out.println("Number: " + num);
});
}
executor.shutdown();
}
}
Q2: Implement the Factory design pattern to create different types of vehicles (Car, Bike, Truck).
// Vehicle.java
public interface Vehicle {
void drive();
}
// Car.java
public class Car implements Vehicle {
public void drive() {
System.out.println("Driving a car.");
}
}
// Bike.java
public class Bike implements Vehicle {
public void drive() {
System.out.println("Riding a bike.");
}
}
// Truck.java
public class Truck implements Vehicle {
public void drive() {
System.out.println("Driving a truck.");
}
}
// VehicleFactory.java
public class VehicleFactory {
public static Vehicle createVehicle(String type) {
switch (type) {
case "Car":
return new Car();
case "Bike":
return new Bike();
case "Truck":
return new Truck();
default:
throw new IllegalArgumentException("Unknown vehicle type");
}
}
}
// Main.java
public class Main {
public static void main(String[] args) {
Vehicle car = VehicleFactory.createVehicle("Car");
car.drive();
Vehicle bike = VehicleFactory.createVehicle("Bike");
bike.drive();
}
}
Q3: Identify the performance issue in the following code snippet and provide a solution.
public class PerformanceTest {
public static void main(String[] args) {
String result = "";
for (int i = 0; i < 10000; i++) {
result += "Number: " + i;
}
System.out.println(result);
}
}
The performance issue is due to the use of string concatenation in a loop, which creates many intermediate
String
objects. Use
StringBuilder
instead:
public class PerformanceTest {
public static void main(String[] args) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < 10000; i++) {
result.append("Number: ").append(i);
}
System.out.println(result.toString());
}
}
Q4: Write a Java method that uses reflection to print all methods of a given class.
import java.lang.reflect.Method;
public class ReflectionExample {
public static void printMethods(Class<?> clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Method: " + method.getName());
}
}
public static void main(String[] args) {
printMethods(String.class);
}
}
Explanation:
printMethods
method uses Java Reflection to get all declared methods of the given class.
Method
objects and prints each method's name.
printMethods
is called with
String.class
, so it prints all methods of the
String
class.
Q5: Write a unit test for a method that calculates the sum of two integers using JUnit.
// Calculator.java
public class Calculator {
public int sum(int a, int b) {
return a + b;
}
}
// CalculatorTest.java
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class CalculatorTest {
@Test
public void testSum() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.sum(2, 3));
}
}
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void printName() {
System.out.println("User name: " + name);
}
}
1. Add null check in the constructor.
2. Mark
name
as final.
3. Override
toString
,
equals
, and
hashCode
methods.
public class User {
private final String name;
public User(String name) {
if (name == null) {
throw new IllegalArgumentException("Name cannot be null");
}
this.name = name;
}
public void printName() {
System.out.println("User name: " + name);
}
@Override
public String toString() {
return "User{name='" + name + "'}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return name.equals(user.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
Q7: Write a Spring Boot application with a REST endpoint
/greet
that returns "Hello, World!"
// SpringBootApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class, args);
}
}
// GreetingController.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
@GetMapping("/greet")
public String greet() {
return "Hello, World!";
}
}
Q8: Write a method to fetch all records from a table
employees
using JDBC.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class DatabaseExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/yourdatabase";
String user = "yourusername";
String password = "yourpassword";
try (Connection con = DriverManager.getConnection(url, user, password);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM employees")) {
while (rs.next()) {
System.out.println("ID: " + rs.getInt("id"));
System.out.println("Name: " + rs.getString("name"));
System.out.println("Position: " + rs.getString("position"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class BubbleSort {
public static void main(String[] args) {
int[] numbers = {5, 3, 8, 4, 2};
bubbleSort(numbers);
System.out.println(Arrays.toString(numbers));
}
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
Issue:
The outer loop runs
n
times instead of
n-1
times, and the inner loop does not account for already sorted elements in the later iterations, making the algorithm inefficient.
Fix:
Adjust the outer loop to run
n-1
times and the inner loop to run
n-1-i
times to avoid unnecessary comparisons.
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] numbers = {5, 3, 8, 4, 2};
bubbleSort(numbers);
System.out.println(Arrays.toString(numbers));
}
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class NumberRemover {
public static void main(String[] args) {
List numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
removeEvenNumbers(numbers);
System.out.println(numbers);
}
public static void removeEvenNumbers(List numbers) {
for (Integer number : numbers) {
if (number % 2 == 0) {
numbers.remove(number);
}
}
}
}
Issue:
The code throws a
ConcurrentModificationException
because it modifies the list while iterating over it using a for-each loop.
Fix:
Use an
Iterator
to remove elements safely while iterating over the list.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class NumberRemover {
public static void main(String[] args) {
List numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
removeEvenNumbers(numbers);
System.out.println(numbers);
}
public static void removeEvenNumbers(List numbers) {
Iterator iterator = numbers.iterator();
while (iterator.hasNext()) {
Integer number = iterator.next();
if (number % 2 == 0) {
iterator.remove();
}
}
}
}
Request question
Please fill in the form below to submit your question.