Day 10: Final Project
Today's Goal
Put everything you've learned over the past 9 days into practice by building a console-based task management application.
Project Overview
| Item | Details |
|---|---|
| App name | TaskManager |
| Features | CRUD operations, file persistence, search and filter |
| Technologies | Classes, inheritance, collections, file I/O, Stream API |
flowchart TB
subgraph App["TaskManager"]
UI["Main Menu<br>User Input"]
Service["TaskService<br>Business Logic"]
Storage["FileStorage<br>File I/O"]
Model["Task / Priority<br>Data Model"]
end
UI --> Service --> Storage
Service --> Model
style UI fill:#3b82f6,color:#fff
style Service fill:#22c55e,color:#fff
style Storage fill:#f59e0b,color:#fff
style Model fill:#8b5cf6,color:#fff
Step 1: Data Model
Priority (Enum)
enum Priority {
HIGH("High"), MEDIUM("Medium"), LOW("Low");
private final String label;
Priority(String label) {
this.label = label;
}
String getLabel() { return label; }
}
Task (Class)
import java.time.LocalDate;
class Task {
private static int nextId = 1;
private final int id;
private String title;
private String description;
private Priority priority;
private boolean completed;
private final LocalDate createdDate;
Task(String title, String description, Priority priority) {
this.id = nextId++;
this.title = title;
this.description = description;
this.priority = priority;
this.completed = false;
this.createdDate = LocalDate.now();
}
// Constructor for restoring from file
Task(int id, String title, String description,
Priority priority, boolean completed, LocalDate createdDate) {
this.id = id;
this.title = title;
this.description = description;
this.priority = priority;
this.completed = completed;
this.createdDate = createdDate;
if (id >= nextId) {
nextId = id + 1;
}
}
// Getters
int getId() { return id; }
String getTitle() { return title; }
String getDescription() { return description; }
Priority getPriority() { return priority; }
boolean isCompleted() { return completed; }
LocalDate getCreatedDate() { return createdDate; }
// Setters
void setTitle(String title) { this.title = title; }
void setDescription(String description) { this.description = description; }
void setPriority(Priority priority) { this.priority = priority; }
void setCompleted(boolean completed) { this.completed = completed; }
// CSV conversion
String toCsv() {
return String.join(",",
String.valueOf(id),
title,
description,
priority.name(),
String.valueOf(completed),
createdDate.toString()
);
}
static Task fromCsv(String csv) {
String[] parts = csv.split(",", 6);
return new Task(
Integer.parseInt(parts[0]),
parts[1],
parts[2],
Priority.valueOf(parts[3]),
Boolean.parseBoolean(parts[4]),
LocalDate.parse(parts[5])
);
}
@Override
public String toString() {
String status = completed ? "Done" : "Todo";
return String.format("[%s] #%d %s [%s] - %s (%s)",
status, id, title, priority.getLabel(), description, createdDate);
}
}
Step 2: File Storage
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
class FileStorage {
private final Path filePath;
FileStorage(String filename) {
this.filePath = Path.of(filename);
}
List<Task> load() {
List<Task> tasks = new ArrayList<>();
try {
if (Files.exists(filePath)) {
List<String> lines = Files.readAllLines(filePath);
for (String line : lines) {
if (!line.isBlank()) {
tasks.add(Task.fromCsv(line));
}
}
}
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
}
return tasks;
}
void save(List<Task> tasks) {
try {
List<String> lines = tasks.stream()
.map(Task::toCsv)
.toList();
Files.write(filePath, lines);
} catch (IOException e) {
System.err.println("Error writing file: " + e.getMessage());
}
}
}
Step 3: Business Logic
import java.util.*;
import java.util.stream.Collectors;
class TaskService {
private final List<Task> tasks;
private final FileStorage storage;
TaskService(FileStorage storage) {
this.storage = storage;
this.tasks = new ArrayList<>(storage.load());
}
// Add task
Task addTask(String title, String description, Priority priority) {
Task task = new Task(title, description, priority);
tasks.add(task);
storage.save(tasks);
return task;
}
// Complete task
boolean completeTask(int id) {
return findById(id).map(task -> {
task.setCompleted(true);
storage.save(tasks);
return true;
}).orElse(false);
}
// Delete task
boolean deleteTask(int id) {
boolean removed = tasks.removeIf(t -> t.getId() == id);
if (removed) {
storage.save(tasks);
}
return removed;
}
// Get all tasks
List<Task> getAllTasks() {
return Collections.unmodifiableList(tasks);
}
// Get pending tasks
List<Task> getPendingTasks() {
return tasks.stream()
.filter(t -> !t.isCompleted())
.toList();
}
// Filter by priority
List<Task> getTasksByPriority(Priority priority) {
return tasks.stream()
.filter(t -> t.getPriority() == priority)
.toList();
}
// Search by keyword
List<Task> search(String keyword) {
String lower = keyword.toLowerCase();
return tasks.stream()
.filter(t -> t.getTitle().toLowerCase().contains(lower)
|| t.getDescription().toLowerCase().contains(lower))
.toList();
}
// Statistics
Map<String, Object> getStatistics() {
Map<String, Object> stats = new LinkedHashMap<>();
stats.put("Total tasks", tasks.size());
stats.put("Completed", tasks.stream().filter(Task::isCompleted).count());
stats.put("Pending", tasks.stream().filter(t -> !t.isCompleted()).count());
Map<Priority, Long> byPriority = tasks.stream()
.collect(Collectors.groupingBy(Task::getPriority, Collectors.counting()));
stats.put("By priority", byPriority);
return stats;
}
// Find by ID
private Optional<Task> findById(int id) {
return tasks.stream()
.filter(t -> t.getId() == id)
.findFirst();
}
}
Step 4: Main Menu
import java.util.List;
import java.util.Scanner;
public class TaskManager {
private final TaskService service;
private final Scanner scanner;
TaskManager() {
FileStorage storage = new FileStorage("tasks.csv");
this.service = new TaskService(storage);
this.scanner = new Scanner(System.in);
}
void run() {
System.out.println("=== Task Manager ===");
while (true) {
showMenu();
String choice = scanner.nextLine().trim();
switch (choice) {
case "1" -> addTask();
case "2" -> listTasks(service.getAllTasks());
case "3" -> listTasks(service.getPendingTasks());
case "4" -> completeTask();
case "5" -> deleteTask();
case "6" -> searchTasks();
case "7" -> filterByPriority();
case "8" -> showStatistics();
case "0" -> {
System.out.println("Goodbye!");
return;
}
default -> System.out.println("Invalid selection.");
}
System.out.println();
}
}
private void showMenu() {
System.out.println("""
--- Menu ---
1. Add task
2. Show all tasks
3. Show pending tasks
4. Complete task
5. Delete task
6. Search by keyword
7. Filter by priority
8. Statistics
0. Exit
""");
System.out.print("Choice: ");
}
private void addTask() {
System.out.print("Title: ");
String title = scanner.nextLine();
System.out.print("Description: ");
String description = scanner.nextLine();
System.out.print("Priority (1: High, 2: Medium, 3: Low): ");
Priority priority = switch (scanner.nextLine().trim()) {
case "1" -> Priority.HIGH;
case "3" -> Priority.LOW;
default -> Priority.MEDIUM;
};
Task task = service.addTask(title, description, priority);
System.out.println("Added: " + task);
}
private void listTasks(List<Task> tasks) {
if (tasks.isEmpty()) {
System.out.println("No tasks found.");
return;
}
tasks.forEach(System.out::println);
System.out.printf("(%d tasks)%n", tasks.size());
}
private void completeTask() {
System.out.print("Task ID to complete: ");
try {
int id = Integer.parseInt(scanner.nextLine().trim());
if (service.completeTask(id)) {
System.out.println("Task #" + id + " completed.");
} else {
System.out.println("Task not found.");
}
} catch (NumberFormatException e) {
System.out.println("Invalid ID.");
}
}
private void deleteTask() {
System.out.print("Task ID to delete: ");
try {
int id = Integer.parseInt(scanner.nextLine().trim());
if (service.deleteTask(id)) {
System.out.println("Task #" + id + " deleted.");
} else {
System.out.println("Task not found.");
}
} catch (NumberFormatException e) {
System.out.println("Invalid ID.");
}
}
private void searchTasks() {
System.out.print("Keyword: ");
String keyword = scanner.nextLine();
listTasks(service.search(keyword));
}
private void filterByPriority() {
System.out.print("Priority (1: High, 2: Medium, 3: Low): ");
Priority priority = switch (scanner.nextLine().trim()) {
case "1" -> Priority.HIGH;
case "3" -> Priority.LOW;
default -> Priority.MEDIUM;
};
listTasks(service.getTasksByPriority(priority));
}
private void showStatistics() {
var stats = service.getStatistics();
System.out.println("=== Statistics ===");
stats.forEach((key, value) ->
System.out.printf("%s: %s%n", key, value));
}
public static void main(String[] args) {
new TaskManager().run();
}
}
10-Day Recap
flowchart TB
D1["Day 1<br>Java Basics<br>Hello World"]
D2["Day 2<br>Variables &<br>Data Types"]
D3["Day 3<br>Control Flow"]
D4["Day 4<br>Arrays &<br>Strings"]
D5["Day 5<br>Methods"]
D6["Day 6<br>Classes &<br>Objects"]
D7["Day 7<br>Inheritance &<br>Polymorphism"]
D8["Day 8<br>Exceptions &<br>File I/O"]
D9["Day 9<br>Collections &<br>Generics"]
D10["Day 10<br>Final Project"]
D1 --> D2 --> D3 --> D4 --> D5
D5 --> D6 --> D7 --> D8 --> D9 --> D10
style D1 fill:#3b82f6,color:#fff
style D2 fill:#3b82f6,color:#fff
style D3 fill:#3b82f6,color:#fff
style D4 fill:#3b82f6,color:#fff
style D5 fill:#3b82f6,color:#fff
style D6 fill:#22c55e,color:#fff
style D7 fill:#22c55e,color:#fff
style D8 fill:#f59e0b,color:#fff
style D9 fill:#f59e0b,color:#fff
style D10 fill:#8b5cf6,color:#fff
| Day | Topic | Key Concepts |
|---|---|---|
| 1 | Welcome to Java | JDK, JVM, main, println |
| 2 | Variables and Data Types | Primitives, String, operators |
| 3 | Control Flow | if, switch, for, while |
| 4 | Arrays and Strings | Arrays, String, StringBuilder |
| 5 | Methods | Parameters, return values, overloading, recursion |
| 6 | Classes and Objects | Constructors, encapsulation, static, records |
| 7 | Inheritance and Polymorphism | extends, interface, abstract, sealed |
| 8 | Exceptions and File I/O | try-catch, Files, Path |
| 9 | Collections and Generics | List, Map, Set, Stream API |
| 10 | Final Project | Integrating all concepts |
What's Next
You've mastered the fundamentals of Java. Here are some topics to explore next.
| Topic | Description |
|---|---|
| Lambda expressions and functional interfaces | Deeper understanding of the Stream API |
| Multithreading | Concurrent programming, ExecutorService |
| Databases | JDBC, JPA |
| Web frameworks | Spring Boot |
| Build tools | Maven, Gradle |
| Testing | JUnit 5, Mockito |
| Design patterns | GoF patterns |
Exercises
Exercise 1: Feature Addition
Add an edit feature to the TaskManager that allows updating a task's title, description, and priority.
Exercise 2: Applied
Add a due date (dueDate) field to tasks and implement a feature to display overdue tasks.
Challenge
Change the storage format from CSV to JSON. Without using any external libraries, implement your own JSON string generation and parsing.
References
Congratulations! You've completed the 10-day Java fundamentals course. The knowledge you've gained here forms a solid foundation for web application development, Android development, enterprise systems, and any other area where Java is used.