Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
220: Building a Basic Library Management System
When you first tackle a project like a Library Management System, it's incredibly tempting to just start typing in the main method. You think, "I'll just need a list of books and a list of users," and before you know it, you've built a 500-line monster of a class where the logic for borrowing a book is tangled up with the code that prints the menu to the console. I've seen this happen to the best of us; it feels fast at first, but it's a technical debt trap.
The 'Parallel List' Nightmare
The most naive way to handle this is by using parallel lists. You might create an ArrayList<String> bookTitles and a corresponding ArrayList<Boolean> isAvailable. In your head, it makes sense: index 0 of the first list is the title, and index 0 of the second list is whether that specific book is checked out.
// The naive way: Parallel lists
List<String> bookTitles = new ArrayList<>();
List<Boolean> isAvailable = new ArrayList<>();
bookTitles.add("The Great Gatsby");
isAvailable.add(true);
Here is where this breaks: the moment you need to sort your books or remove one. If you sort the titles alphabetically but forget to shuffle the availability list in the exact same order, your data is now corrupted. You'll be telling a user that "The Great Gatsby" is available when it's actually "War and Peace" that's on the shelf. It's a fragile way to code because you're relying on the position of the data rather than the identity of the object.
Moving to a Domain Model
The professional approach is to encapsulate that data into a dedicated Book class. By grouping the title, ISBN, and availability status into a single object, you ensure that the data always travels together. If you move a Book object to a different part of a list, its status moves with it. It’s a simple shift, but it changes the system from being a collection of arrays into a collection of entities.
public class Book {
private final String isbn;
private final String title;
private boolean isAvailable = true;
public Book(String isbn, String title) {
this.isbn = isbn;
this.title = title;
}
// Getters and a method to toggle availability
public void setAvailable(boolean available) { isAvailable = available; }
public boolean isAvailable() { return isAvailable; }
public String getIsbn() { return isbn; }
}
O(n) vs O(1): The Search Bottleneck
Now, let's talk about how we find these books. A common mistake is to keep all books in a List<Book> and use a for loop to find a book by its ISBN every time a user wants to borrow something. For ten books, it's fine. For ten thousand, your application will start to lag. You're performing a linear search—checking every single item until you hit the right one.
I always recommend a Map<String, Book> for this. By using the ISBN as the key, you move from O(n) time complexity to O(1). You aren't searching through the library anymore; you're going straight to the exact shelf where the book lives. The trade-off is a tiny bit more memory usage, but the performance gain is massive as the dataset grows.
Logic Leaks and the Service Layer
Finally, you have to decide where the "business rules" go. Should the Book class have a method called borrowBook()? It seems intuitive, but that's a leak. A Book shouldn't know about the rules of the library—like whether a user has too many overdue books or if the library is currently closed.
Instead, I create a LibraryService. This class acts as the orchestrator. It takes a Member and a Book, checks the rules, and then updates the book's status. This keeps your data objects (POJOs) clean and your business logic centralized. If the library decides to change the borrowing limit from 5 books to 3, you only have to change one line in the service, not hunt through every entity class in your project.
📋 Practical Task
Implement an Overdue Penalty Calculator for the Library System
You have the basic Book and LibraryService structure, but now the library wants to charge fines for late returns. Your task is to extend the system without cluttering the Book class with financial logic.
Requirements:
- Create a
Loanclass that associates aBookwith aLocalDate(the date it was borrowed) and aLocalDate(the due date). - Add a method to your
LibraryServicecalledcalculateFine(Loan loan). - The fine should be calculated at a rate of $0.50 per day overdue. If the book is returned on or before the due date, the fine is $0.00.
- Ensure that the
calculateFinemethod usesjava.time.ChronoUnit.DAYS.between()to determine the difference between the due date and the current date (LocalDate.now()).
Verify your implementation by creating a Loan that is 5 days overdue and ensuring the returned fine is exactly 2.50.
There are no comments for now.