Java Question Bank with Answers
Answers for Core Java Question Bank –
Chapter 4: Exception Handling, Strings, and Files
Difficult Level Questions
18. Design and implement a Java program that simulates a simple Account class.
This class should have methods for deposit(double amount) and withdraw(double amount).
Create a user-defined InsufficientFundsException that is thrown if a withdrawal attempt exceeds the current balance.
The withdraw method should declare this exception using throws.
Demonstrate in your main method how to call the withdraw method within a try-catch block to handle the InsufficientFundsException gracefully, printing an appropriate message.
Answer:
This solution involves creating a custom checked exception and then demonstrating its use in an Account class with a withdraw method that throws this exception, and a main method that handles it.
Step 1: Define the custom InsufficientFundsException
// Custom Checked Exception for insufficient funds
class InsufficientFundsException extends Exception {
private double currentBalance;
private double withdrawalAmount;
public InsufficientFundsException {
super(message);
this.currentBalance = currentBalance;
this.withdrawalAmount = withdrawalAmount;
}
public double getCurrentBalance() {
return currentBalance;
}
public double getWithdrawalAmount() {
return withdrawalAmount;
}
}
Step 2: Implement the Account class
public class Account {
private String accountNumber;
private double balance;
public Account {
this.accountNumber = accountNumber;
this.balance = initialBalance;
System.out.printf("Account %s created with initial balance: $%.2f%n", accountNumber, balance);
}
public void deposit(double amount) {
if (amount < 0) {
System.out.println("Deposit amount cannot be negative.");
return;
}
balance += amount;
System.out.printf("Deposited $%.2f into account %s. New balance: $%.2f%n", amount, accountNumber, balance);
}
// The withdraw method declares that it might throw InsufficientFundsException
public void withdraw(double amount) throws InsufficientFundsException {
if (amount < 0) {
System.out.println("Withdrawal amount cannot be negative.");
return; // Or throw an IllegalArgumentException
}
if (balance < amount) {
// Throw the custom InsufficientFundsException
throw new InsufficientFundsException(
"Insufficient funds. Cannot withdraw $" + amount + ". Current balance: $" + balance,
balance,
amount
);
}
balance -= amount;
System.out.printf("Withdrew $%.2f from account %s. New balance: $%.2f%n", amount, accountNumber, balance);
}
public double getBalance() {
return balance;
}
public String getAccountNumber() {
return accountNumber;
}
public void displayAccountInfo() {
System.out.printf("Account Number: %s, Current Balance: $%.2f%n", accountNumber, balance);
}
}
Step 3: Demonstrate in main method (creation, deposit, and handling withdrawal attempts)
// Step 1: Custom Checked Exception
class InsufficientFundsException extends Exception {
private final double currentBalance;
private final double withdrawalAmount;
// Constructor accepting message, current balance, and attempted withdrawal
public InsufficientFundsException(String message, double currentBalance, double withdrawalAmount) {
super(message);
this.currentBalance = currentBalance;
this.withdrawalAmount = withdrawalAmount;
}
public double getCurrentBalance() {
return currentBalance;
}
public double getWithdrawalAmount() {
return withdrawalAmount;
}
}
// Step 2: Account Class
class Account {
private final String accountNumber;
private double balance;
public Account(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
System.out.printf("Account %s created with initial balance: $%.2f%n", accountNumber, balance);
}
public void deposit(double amount) {
if (amount < 0) {
System.out.println("Deposit amount cannot be negative.");
return;
}
balance += amount;
System.out.printf("Deposited $%.2f into account %s. New balance: $%.2f%n", amount, accountNumber, balance);
}
// Withdraw method that throws the custom exception
public void withdraw(double amount) throws InsufficientFundsException {
if (amount < 0) {
System.out.println("Withdrawal amount cannot be negative.");
return;
}
if (balance < amount) {
throw new InsufficientFundsException(
"Insufficient funds. Cannot withdraw $" + amount + ".",
balance,
amount
);
}
balance -= amount;
System.out.printf("Withdrew $%.2f from account %s. New balance: $%.2f%n", amount, accountNumber, balance);
}
public double getBalance() {
return balance;
}
public void displayAccountInfo() {
System.out.printf("Account Number: %s, Current Balance: $%.2f%n", accountNumber, balance);
}
}
// Step 3: Main Class to Demonstrate
public class AccountSystemDemo {
public static void main(String[] args) {
Account myAccount = new Account("12345-A", 500.00);
myAccount.displayAccountInfo();
// Test Case 1: Successful withdrawal
System.out.println("\n— Test Case 1: Successful Withdrawal —");
try {
myAccount.withdraw(200.00);
} catch (InsufficientFundsException e) {
System.err.println("Transaction failed: " + e.getMessage());
System.err.printf("Attempted to withdraw $%.2f with current balance $%.2f%n",
e.getWithdrawalAmount(), e.getCurrentBalance());
}
myAccount.displayAccountInfo();
// Test Case 2: Insufficient funds withdrawal
System.out.println("\n— Test Case 2: Insufficient Funds Withdrawal —");
try {
myAccount.withdraw(400.00); // Current balance is 300, so this should fail
} catch (InsufficientFundsException e) {
System.err.println("Transaction failed: " + e.getMessage());
System.err.printf("Attempted to withdraw $%.2f with current balance $%.2f%n",
e.getWithdrawalAmount(), e.getCurrentBalance());
}
myAccount.displayAccountInfo();
// Test Case 3: Deposit and then successful withdrawal
System.out.println("\n— Test Case 3: Deposit and then Successful Withdrawal —");
myAccount.deposit(300.00); // Balance becomes 600
try {
myAccount.withdraw(150.00);
} catch (InsufficientFundsException e) {
System.err.println("Transaction failed: " + e.getMessage());
System.err.printf("Attempted to withdraw $%.2f with current balance $%.2f%n",
e.getWithdrawalAmount(), e.getCurrentBalance());
}
myAccount.displayAccountInfo();
System.out.println("\nAll transactions processed. Program ends.");
}
}
19. Evaluate the efficiency and best practices when performing extensive string manipulations in Java. Compare the performance implications of using String concatenation repeatedly in a loop versus using StringBuilder or StringBuffer. Provide a conceptual analysis and suggest scenarios where each would be most appropriate.
Answer:
Conceptual Analysis of Efficiency and Performance Implications:
In Java, the choice between String, StringBuilder, and StringBuffer for string manipulation, especially extensive operations in loops, has significant performance and memory implications due to their fundamental differences in mutability and thread-safety.
1. String Concatenation (+ operator or concat() method):
o Implication: String objects are immutable. Every time you use the + operator or the concat() method to modify a String, a new String object is created to hold the modified content. The original String remains unchanged.
o Performance: In a loop, this leads to the creation of many intermediate, short-lived String objects. This is highly inefficient because:
§ Memory Overhead: Each new object requires memory allocation.
§ CPU Overhead: Copying characters from the old string to the new string takes CPU cycles.
§ Garbage Collection: The many intermediate objects become eligible for garbage collection, increasing GC activity and potentially causing performance pauses.
o Conceptual Example: s = s + "a"; inside a loop for 1000 iterations could involve creating 1000 new String objects.
2. StringBuilder:
o Implication: StringBuilder objects are mutable. When you append, insert, or delete characters, these operations are performed directly on the existing StringBuilder object in memory. No new objects are created for each modification.
o Performance: This makes StringBuilder significantly more efficient for extensive string manipulations, especially within a single thread. It avoids the overheads associated with immutable String objects.
o Thread-Safety: It is not thread-safe. If multiple threads try to modify the same StringBuilder instance concurrently, it can lead to data corruption or unexpected results.
3. StringBuffer:
o Implication: StringBuffer objects are also mutable, similar to StringBuilder. Modifications happen in-place.
o Performance: It is efficient for string manipulations compared to String. However, it is slightly less efficient than StringBuilder in a single-threaded environment because all its methods are synchronized. This synchronization adds a small performance overhead for thread safety, even if only one thread is using it.
o Thread-Safety: It is thread-safe. Its synchronized methods ensure that only one thread can modify the StringBuffer at a time, making it suitable for multi-threaded environments.
Performance Hierarchy:
StringBuilder > StringBuffer
>>> String
Best Practices and Scenarios:
1. Use String When:
o Immutability is Desired: When the string’s content should not change after creation (e.g., security-sensitive data, keys in maps, or objects that should be hashable).
o Few or No Modifications: For strings that are mostly read or undergo very few modifications. Java compilers often optimize simple String concatenations at compile time (e.g., String s = "Hello" + " " + "World"; is often optimized to String s = "Hello World";).
o String Literals: For constant string values.
o Scenario: Storing a user’s name, password, or fixed labels in a GUI.
2. Use StringBuilder When:
o Extensive Manipulations in a Single-Threaded Environment: This is the most common use case for StringBuilder. When you need to build a complex string, append many small strings, or perform operations like reverse(), insert(), delete() within a single thread.
o High Performance is Critical: When performance is a primary concern and thread safety is not an issue.
o Scenario: Building a dynamic SQL query, generating a report, constructing a JSON string from data, processing large text files line by line where lines are modified before output.
3. Use StringBuffer When:
o Extensive Manipulations in a Multi-Threaded Environment: When the same string buffer might be accessed and modified by multiple threads concurrently, and you need to ensure thread safety to prevent data corruption.
o Legacy Code: StringBuffer has been around since Java 1.0, so you might encounter it in older codebases where thread-safe mutable strings were needed before StringBuilder was introduced in Java 5.
o Scenario: A shared log buffer where multiple threads concurrently append messages, a shared buffer for real-time data processing from multiple sources.
20. Design a comprehensive Java program for file handling that:
Creates a new text file named data.txt.
Writes at least five lines of text, including a mix of numbers and words, into data.txt.
Reads the content from data.txt line by line.
For each line read, attempts to convert any numeric part of the line into an integer. If a NumberFormatException occurs, it should log the error to the console but continue processing.
Finally, counts the total number of lines and the total number of successfully parsed integers from the file, printing these statistics to the console.
Ensure all file operations are enclosed in try-catch-finally blocks to handle potential IOException and ensure resources are properly closed.
Answer:
This program demonstrates a complete file handling workflow, including creation, writing, reading, parsing with exception handling, and resource management using try-with-resources (which implicitly handles finally for auto-closeable resources).
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ComprehensiveFileHandler {
private static final String FILE_NAME = "data.txt";
public static void main(String[] args) {
// Phase 1: Create and write to file
System.out.println("— Phase 1: Creating and Writing to " + FILE_NAME + " —");
writeLinesToFile();
System.out.println("Phase 1 Complete.\n");
// Phase 2: Read from file, parse numbers, and collect statistics
System.out.println("— Phase 2: Reading from " + FILE_NAME + " and Processing —");
readAndProcessFile();
System.out.println("Phase 2 Complete.\n");
}
private static void writeLinesToFile() {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_NAME))) {
writer.write("Line 1: This file contains some text and numbers like 101.\n");
writer.write("Line 2: Another line with 200 and some words.\n");
writer.write("Line 3: Pure text line.\n");
writer.write("Line 4: A number 303 at the end.\n");
writer.write("Line 5: Mixed content 404 text and 505.\n");
writer.write("Line 6: Invalid number ‘abc’ here.\n");
System.out.println("Successfully wrote 6 lines to " + FILE_NAME);
} catch (IOException e) {
System.err.println("Error writing to file " + FILE_NAME + ": " + e.getMessage());
}
}
private static void readAndProcessFile() {
int totalLines = 0;
int successfullyParsedIntegers = 0;
System.out.println("Reading content from " + FILE_NAME + ":");
try (BufferedReader reader = new BufferedReader(new FileReader(FILE_NAME))) {
String line;
Pattern numberPattern = Pattern.compile("\\b\\d+\\b"); // Regex to match whole numbers
while ((line = reader.readLine()) != null) {
totalLines++;
System.out.println(" Processing Line " + totalLines + ": " + line);
Matcher matcher = numberPattern.matcher(line);
while (matcher.find()) {
String numberStr = matcher.group();
try {
int number = Integer.parseInt(numberStr);
System.out.println(" Found and parsed integer: " + number);
successfullyParsedIntegers++;
} catch (NumberFormatException e) {
System.err.println(" Error: Could not parse ‘" + numberStr + "’ to an integer. Skipping. (" + e.getMessage() + ")");
}
}
}
} catch (IOException e) {
System.err.println("Error reading from file " + FILE_NAME + ": " + e.getMessage());
}
System.out.println("\n— Processing Statistics —");
System.out.println("Total lines read: " + totalLines);
System.out.println("Total successfully parsed integers: " + successfullyParsedIntegers);
}
}