Build a Java Work Item Tracker
Build a Java command-line tracker with objects, collections, and validation.
Introduction
30 Second Summary
Team tasks often begin as quick notes in chat or scattered documents. As the list grows, it becomes harder to see what still needs attention.
In this project, you will build a command-line work item tracker using Java. When it runs, the terminal shows numbered work items with their statuses plus a readable rejection for a blank title.
What You'll Build
Your finished tracker prints a clear validation message followed by a numbered team list containing both completed work and work that remains open.
By the end of this project, you'll have:
- A Java program you can compile and run while seeing .java source become .class bytecode for the JVM.
- A visible demonstration of the fixed-size array limit followed by a growing tracker powered by ArrayList.
- A structured tracker that can create work items, mark work as complete, reject blank titles, and display TODO or DONE for every item.
- Secret Mission: add a reusable status filter that displays only work items matching a selected status.
Are there any prerequisites?
No Java knowledge is required. Basic Windows file and folder navigation is helpful.
Before We Start
Before any hands-on work, commit to what your terminal tracker should display. Plain Java reveals how the JVM runs classes whose objects form the application's structure.
Verify and Prepare Your Java Workspace
Your tracker will soon depend on the Java compiler. An installed JDK only helps when Windows PowerShell and Visual Studio Code can find it.
First, you will confirm the runtime uses a compatible JDK. You will confirm the compiler reports the same major version.
You will also prepare VS Code with its Java extension. The final runtime check connects your workspace to the same JDK.
In this step, get ready to:
- Verify that the Java runtime and compiler report the same major version of 17 or later.
- Confirm that Microsoft Extension Pack for Java version 0.31.1 or later is installed.
- Prepare the java-work-items workspace around the selected JDK.
Verify the Java toolchain
The Java runtime starts applications. The Java compiler turns source code into files that the runtime can execute.
- Press the Windows key to open Windows search.
- Type Windows PowerShell and press Enter to open it.
- Compare the runtime and compiler versions by running these commands:
java -version
javac -version
What do these commands show?
- The java -version command prints the product version used to run Java applications.
- The javac -version command prints the compiler version used to compile Java source files.
The first numeric component is the major version. Use your results to choose the matching tab.
✔️ I see version 17 or higher
Your runtime and compiler are compatible when both outputs start with the same major version of 17 or later. That confirms the command line can reach the complete Java toolchain.
ⓧ I see an older version
A JDK below version 17 cannot provide the compatibility this project expects. Install the newer JDK before preparing VS Code.
- Install the compatible JDK by running this command:
winget install Microsoft.OpenJDK.25
What does this command do?
This command adds a compatible JDK to Windows. The installed package includes the runtime plus the compiler your tracker needs.
- Close Visual Studio Code if it is running.
- Close the current Windows PowerShell window.
- Press the Windows key to open Windows search.
- Type Windows PowerShell and press Enter to open a fresh window.
- Recheck the runtime and compiler versions by running:
java -version
javac -version
Why use a fresh terminal?
A fresh PowerShell session reads the updated Java configuration. The two checks confirm that Windows now finds the newly installed runtime plus compiler.
You should now see the same major version of 17 or later in both outputs.
ⓧ Command not found
Windows cannot currently locate the Java runtime or compiler. Installing the JDK supplies both tools.
- Install the compatible JDK by running this command:
winget install Microsoft.OpenJDK.25
What does this command do?
This command adds a compatible JDK to Windows. The installed package includes the runtime plus the compiler your tracker needs.
- Close Visual Studio Code if it is running.
- Close the current Windows PowerShell window.
- Press the Windows key to open Windows search.
- Type Windows PowerShell and press Enter to open a fresh window.
- Recheck the runtime and compiler versions by running:
java -version
javac -version
Why use a fresh terminal?
A fresh PowerShell session reads the updated Java configuration. The two checks confirm that Windows can now locate the runtime plus compiler.
You should now see the same major version of 17 or later in both outputs.
JDK installation not completing?
- Check that your internet connection is active before trying the installation again.
- Restart Windows PowerShell before repeating the version checks.
- Ask for help with the installation error you can see.
Verify the Java extension
The Microsoft-published Extension Pack for Java gives VS Code its Java language support. Version 0.31.1 or later keeps that support compatible with this project.
- Press the Windows key to open Windows search.
- Type Visual Studio Code and press Enter to open it.
- Press Ctrl+Shift+X to open Extensions.
- Search for Extension Pack for Java.
- Select the result published by Microsoft.
Finding the version can be a little fiddly because the extension page contains several details. Look for its installed version before choosing a tab.
✔️ I see version 0.31.1 or later
The Java extension is ready. VS Code now has the language support needed for the source files you create next.
ⓧ I see an older version
The installed extension needs an update before you continue.
- Select the available update control on the extension page.
- Wait until the displayed version is 0.31.1 or later.
ⓧ It is not installed
The extension page provides the missing Java language support.
- Click Install on the Microsoft-published extension page.
- Wait until version 0.31.1 or later is shown.
Extension not installing?
- Confirm that the selected extension is published by Microsoft.
- Restart Visual Studio Code before trying the extension action again.
- Ask for help with the extension state you can see.
Create the workspace and confirm the selected JDK
A workspace gives VS Code one clear folder for your tracker. Its Java runtime view shows which JDK the editor uses inside that folder.
- Click File in the top menu.
- Select Open Folder....
- Select Desktop in the folder picker.
- Select New Folder.
- Enter java-work-items as the folder name.
- Select Select Folder.
- Select Yes, I trust the authors if the Workspace Trust dialog appears.
Good progress. You should now see java-work-items at the top of the Explorer sidebar.
- Press Ctrl+Shift+P to open the Command Palette.
- Type Java: Configure Java Runtime.
- Select Java: Configure Java Runtime from the results.
- Read the selected JDK version shown in the runtime view.
Before you run the final check, do you expect the runtime and compiler to report the same major version?
- Switch back to the Windows PowerShell window from earlier.
- Confirm the final runtime and compiler versions by running:
java -version
javac -version
Why check the versions again?
The first check diagnosed the starting state. This final check proves that the command line still reaches the compatible JDK selected for your workspace.
You should see the same major version of 17 or later in both outputs. That major version should match the JDK shown by Java: Configure Java Runtime.
Your Java workspace can now find one compatible runtime plus compiler. Next, you will compile your first source file and see it run in the terminal.
Compile and Run Your First Java Program
Your Java workspace is ready. The tracker still has no code to run.
You will write one class in Visual Studio Code. The javac compiler turns that source into bytecode for the JVM.
Windows PowerShell then launches the compiled class through main. This makes Java's compilation process visible before your tracker grows.
In this step, get ready to:
- Write a Java class with an application entry point.
- Identify the class, method, parameter, statement, and string literal.
- Move the program through Java's compile-then-run workflow.
Create the Java entry point
A Java application starts from an entry point. Your HelloWorldApp class uses the standard public static void main(String[] args) entry point so the JVM knows where execution begins.
- Select the Explorer view in the left sidebar.
- Click the New File... button.
- Enter HelloWorldApp.java as the file name.
- Press Enter to create the file at the top level of java-work-items.
- Add the first runnable class by copying this code into HelloWorldApp.java:
public class HelloWorldApp {
public static void main(String[] args) {
System.out.println("Hello, Java team!");
}
}
What does this code do?
- The HelloWorldApp class provides the program's outer structure.
- The main method gives the JVM its starting point.
- The String[] args parameter can hold command-line arguments passed to the application.
- The System.out.println statement writes one line to standard output.
- The "Hello, Java team!" string literal contains the exact text that the terminal receives.
- Save HelloWorldApp.java by pressing Ctrl+S.
- Confirm HelloWorldApp.java appears directly inside the java-work-items folder in the Explorer sidebar.
You should see HelloWorldApp.java at the top level of the workspace. The editor should show the class with matching opening and closing braces.
File missing from the workspace?
- Check that you created HelloWorldApp.java directly inside java-work-items.
- Check that the file name uses the same capitalization as HelloWorldApp inside the class declaration.
- Help me fix the location or name of my Java source file.
Identify the program's parts
Java code is organized from an outer class into methods. Statements inside those methods perform the work.
- Point to HelloWorldApp as the class name.
- Point to main as the method name.
- Point to String[] args as the method parameter.
- Point to System.out.println as the statement that produces output.
- Point to "Hello, Java team!" as the string literal.
✔️ Awesome, I've got everything!
Your source file now contains one complete Java class with a runnable entry point.
ⓧ I'd like to double check the full code
Here is the complete HelloWorldApp.java file for comparison.
public class HelloWorldApp {
public static void main(String[] args) {
System.out.println("Hello, Java team!");
}
}
Compile the source and run the bytecode
Compilation and execution are separate stages in Java. First, javac creates HelloWorldApp.class from your source file.
The java launcher then asks the JVM to execute that compiled class. Seeing both stages proves which tool performs each job.
- Click Terminal in the Visual Studio Code menu bar.
- Click New Terminal.
- Select PowerShell from the terminal dropdown if another shell name is shown.
The terminal starts in the open java-work-items workspace. Your source file is available from this location.
Before you compile, make a prediction about which new file javac will add.
- Compile HelloWorldApp.java by running this command:
javac HelloWorldApp.java
What does this command do?
The javac compiler reads HelloWorldApp.java. It converts the class into JVM bytecode stored in HelloWorldApp.class.
- Inspect the java-work-items folder in the Explorer sidebar.
You should now see HelloWorldApp.class beside HelloWorldApp.java. That new file is the visible result of compilation.
Class file not appearing?
- Confirm the terminal prompt points to the java-work-items workspace.
- Check every brace and parenthesis in HelloWorldApp.java against the full-code tab.
- Help me diagnose my Java compilation problem.
Before you run the class, predict the exact line the JVM will print.
- Launch the compiled HelloWorldApp class by running this command:
java HelloWorldApp
What does this command do?
The java launcher starts the JVM. The JVM enters the class through main.
The print statement sends its string literal to the terminal. This is the execution stage of the workflow.
You should see Hello, Java team! on its own line in the PowerShell terminal.
You have now proved the entire Java path from source file to terminal output.
Greeting not printing?
- Confirm HelloWorldApp.class still appears beside the source file.
- Check that the class name uses the exact capitalization HelloWorldApp.
- Help me fix my Java launch problem.
Your first Java program now compiles and runs through the JVM. Next, you will turn the greeting into a small tracker and feel the capacity limit of a fixed-size array.
Expose the Fixed Array Limit
Your first Java program compiled into bytecode. The JVM then entered through main() to print your greeting.
A tracker needs storage for several titles. In this step, a fixed-size array starts with room for two items before you test a third.
In this step, get ready to:
- Replace the greeting with a fixed two-item tracker.
- Print each title with a for-each loop.
- Test a third title against the same array and trace the program's result.
Store two work item titles
An array reserves its length when you create it. The new String[2] expression creates two positions for text.
- In the open HelloWorldApp.java tab, select the line that prints Hello, Java team!.
- Replace that line with the following array setup and loop:
String[] workItems = new String[2];
workItems[0] = "Learn Java compilation";
workItems[1] = "Build a work item tracker";
for (String workItem : workItems) {
System.out.println(workItem);
}
What does this code do?
- The workItems variable holds an array with space for two String references.
- Indexes 0 and 1 fill the two available positions with work item titles.
- The loop reads each title as workItem before println writes it to the terminal.
- Save HelloWorldApp.java.
- Compile the updated source in PowerShell by running this command:
javac HelloWorldApp.java
What does this command do?
The javac compiler reads HelloWorldApp.java and updates HelloWorldApp.class. A successful compile returns you to the PowerShell prompt.
Seeing a compiler error?
- Confirm that you saved HelloWorldApp.java before compiling it.
- Check that both array assignments sit inside main().
- Compare each brace in your file with the code block above.
- Help me fix the compiler error in my two-item Java array.
- Run the two-item tracker by running this command:
java HelloWorldApp
What should I see?
You'll see Learn Java compilation on the first line. You'll see Build a work item tracker on the second line.
Good progress. Your array now stores two titles that the loop can read and print.
Still seeing the greeting?
- Confirm that the original greeting line has been removed from HelloWorldApp.java.
- Save the file before compiling it again.
- Run the compile command again before starting the program.
- Help me find why my Java program still prints the old greeting.
Push beyond the original capacity
The tracker currently fills every position in its two-element array. The next assignment asks the same array to hold another title.
- Place your cursor after the loop's closing brace inside main().
- Paste the following two lines at that position:
System.out.println("Adding one more item...");
workItems[2] = "Review the tracker";
What does this code do?
- The first line marks the moment when the tracker tries to grow.
- The workItems[2] assignment targets the third position because array indexes begin at zero.
- Save HelloWorldApp.java.
Run and trace the bounds failure
The new assignment is valid Java syntax. Compilation prepares the updated bytecode before the JVM reaches that assignment.
- Compile the updated tracker by running this command:
javac HelloWorldApp.java
What happens during compilation?
The compiler accepts the assignment because the index is an integer. A successful compile updates HelloWorldApp.class and returns you to the PowerShell prompt.
Does the updated file fail to compile?
- Check that the new lines appear before the closing brace for main().
- Confirm that the array variable is spelled workItems in every location.
- Help me fix the compiler error after adding the third array assignment.
Before you run it, do you think the array can accept the third title without changing its original length?
- Test your prediction by running this command:
java HelloWorldApp
The failure is the evidence
You'll see both work item titles followed by Adding one more item.... The JVM then reports ArrayIndexOutOfBoundsException.
The stack trace names HelloWorldApp.java and reports the failing source line. That intended failure makes the array's fixed capacity visible.
- Read the final line of the stack trace in PowerShell.
- Match its file name to HelloWorldApp.java in the open workspace.
- Use the reported line number to locate workItems[2] in the editor.
- Compare index 2 with the array length 2.
Why does index 2 fail?
Java uses zero-based indexing. A two-element array exposes indexes 0 and 1.
Index 2 requests a third position outside that range. The exception points directly to the assignment that crossed the boundary.
✔️ Awesome, I've got everything!
Your HelloWorldApp.java file now matches the fixed-array experiment. The exception is intentional evidence of the capacity limit.
ⓧ I'd like to double check the full code
Compare your complete HelloWorldApp.java file with this version. Keep every statement in the same order so the stack trace points to the expected assignment.
public class HelloWorldApp {
public static void main(String[] args) {
String[] workItems = new String[2];
workItems[0] = "Learn Java compilation";
workItems[1] = "Build a work item tracker";
for (String workItem : workItems) {
System.out.println(workItem);
}
System.out.println("Adding one more item...");
workItems[2] = "Review the tracker";
}
}
You found the exact line where fixed capacity breaks the tracker. Next, you will replace plain strings with objects and move the tracker to storage that can grow.
Model Work Items with Objects
Your fixed-size array made the tracker fail as soon as a third title reached index 2. It also stored each work item as a plain string.
A Java object keeps each work item's data together. A resizable collection lets the tracker hold a growing set of those objects.
In this step, get ready to:
- Replace the temporary files with a package-based source layout.
- Model each work item with an ID, title, and status.
- Run a tracker that displays three work items without failing.
Restructure the workspace for packages
A named package keeps related source files under a matching directory path. The Visual Studio Code setting tells Java tooling where that source path begins.
Deleting the temporary demo files can trigger a confirmation dialog. Both files are disposable because you have already diagnosed the array failure.
- In the Explorer sidebar, right-click HelloWorldApp.java.
- Select Delete from the menu.
- Confirm the deletion if Visual Studio Code asks.
- In the Explorer sidebar, right-click HelloWorldApp.class.
- Select Delete from the menu.
- Confirm the deletion if Visual Studio Code asks.
You should no longer see HelloWorldApp.java or HelloWorldApp.class in the workspace.
- Right-click the java-work-items folder in the Explorer sidebar.
- Select New Folder.
- Enter .vscode as the folder name.
You should see the new .vscode folder inside java-work-items.
- Right-click the .vscode folder.
- Select New File.
- Enter settings.json as the file name.
- Configure the Java source path by replacing the empty file with this code:
{
"java.project.sourcePaths": ["src/main/java"]
}
What Does This Setting Do?
The java.project.sourcePaths setting identifies the folder where your Java package structure begins.
The value src/main/java keeps source code separate from workspace configuration files.
- Save settings.json.
- Check the Explorer sidebar for .vscode/settings.json.
Source Path Setting Not Recognized?
- Check that settings.json sits inside the .vscode folder.
- Check that the source path uses forward slashes exactly as shown.
- Help me check my Visual Studio Code Java source path configuration.
- Right-click the java-work-items folder in the Explorer sidebar.
- Select New Folder.
- Enter src/main/java/com/nextwork/workitems as the folder path.
- Expand the folders in the Explorer sidebar to confirm the complete package path.
You should see workitems nested under src/main/java/com/nextwork.
Define status and work item objects
An enum defines the allowed status values for every work item. A class provides the blueprint for each work item object.
- Right-click the src/main/java/com/nextwork/workitems folder.
- Select New File.
- Enter WorkItemStatus.java as the file name.
- Define the two allowed status values by replacing the empty file with this code:
package com.nextwork.workitems;
public enum WorkItemStatus {
TODO,
DONE
}
What Does This Enum Do?
- The package declaration connects the file to com.nextwork.workitems.
- The TODO value represents unfinished work.
- The DONE value represents completed work.
- Save WorkItemStatus.java.
- Check the editor for red underlines in the enum declaration.
You should see a clean enum file with no highlighted Java errors.
Seeing an Error in the Enum?
- Check that the package declaration matches the folder path exactly.
- Check that the file name uses the same capitalization as WorkItemStatus.
- Help me fix my WorkItemStatus enum.
The tracker also needs one place to keep each item's ID, title, and current status. A constructor sets that initial state when the application creates an object.
- Right-click the src/main/java/com/nextwork/workitems folder.
- Select New File.
- Enter WorkItem.java as the file name.
- Model the work item by replacing the empty file with this code:
package com.nextwork.workitems;
public final class WorkItem {
private final int id;
private final String title;
private WorkItemStatus status;
public WorkItem(int id, String title) {
this.id = id;
this.title = title;
this.status = WorkItemStatus.TODO;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public WorkItemStatus getStatus() {
return status;
}
public void markDone() {
status = WorkItemStatus.DONE;
}
}
How Does a Work Item Behave?
- The private fields keep each object's ID, title, and status together.
- The WorkItem constructor gives every new object the TODO status.
- The getter methods provide controlled access to each field.
- The markDone() method changes the object's status to DONE.
- Save WorkItem.java.
- Select Problems in the bottom panel.
You should see no Java errors for WorkItem.java or WorkItemStatus.java.
Seeing Errors in WorkItem?
- Confirm that WorkItemStatus.java is saved in the same package folder.
- Compare each opening brace with its closing brace.
- Help me fix my WorkItem class.
Build and run the list-backed tracker
The List type describes an ordered collection. ArrayList provides the resizable storage that the fixed array lacked.
- Right-click the src/main/java/com/nextwork/workitems folder.
- Select New File.
- Enter WorkItemsApp.java as the file name.
- Build the entry point by replacing the empty file with this code:
package com.nextwork.workitems;
import java.util.ArrayList;
import java.util.List;
public final class WorkItemsApp {
public static void main(String[] args) {
List<WorkItem> items = new ArrayList<>();
items.add(new WorkItem(1, "Learn Java compilation"));
items.add(new WorkItem(2, "Build a work item tracker"));
items.add(new WorkItem(3, "Review the tracker"));
items.get(0).markDone();
for (WorkItem item : items) {
System.out.println(
item.getId() + " | " + item.getStatus() + " | " + item.getTitle());
}
}
}
What Does This Application Do?
- The imports make List and ArrayList available to the application.
- The three items.add calls create objects with distinct IDs and titles.
- The items.get(0) call selects the first object for completion.
- The for-each loop prints the stored fields from every object.
- Save WorkItemsApp.java.
Before you run it, consider which work item will display DONE.
- Select Run|Debug above main in WorkItemsApp.java.
What Should You See?
The first line should be 1 | DONE | Learn Java compilation.
The second line should be 2 | TODO | Build a work item tracker.
The third line should be 3 | TODO | Review the tracker.
That is the fixed-capacity failure gone. Your tracker now runs with three stateful work item objects.
Tracker Not Running Cleanly?
- Confirm that all three Java files are inside src/main/java/com/nextwork/workitems.
- Confirm that each Java file starts with the same package declaration.
- Help me troubleshoot my packaged work item tracker.
✔️ Awesome, I've got everything!
Great. Make sure all four project files are saved.
ⓧ I'd like to double check the full code
Compare each file with the complete project state for this step.
{
"java.project.sourcePaths": ["src/main/java"]
}
package com.nextwork.workitems;
public final class WorkItem {
private final int id;
private final String title;
private WorkItemStatus status;
public WorkItem(int id, String title) {
this.id = id;
this.title = title;
this.status = WorkItemStatus.TODO;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public WorkItemStatus getStatus() {
return status;
}
public void markDone() {
status = WorkItemStatus.DONE;
}
}
package com.nextwork.workitems;
public enum WorkItemStatus {
TODO,
DONE
}
package com.nextwork.workitems;
import java.util.ArrayList;
import java.util.List;
public final class WorkItemsApp {
public static void main(String[] args) {
List<WorkItem> items = new ArrayList<>();
items.add(new WorkItem(1, "Learn Java compilation"));
items.add(new WorkItem(2, "Build a work item tracker"));
items.add(new WorkItem(3, "Review the tracker"));
items.get(0).markDone();
for (WorkItem item : items) {
System.out.println(
item.getId() + " | " + item.getStatus() + " | " + item.getTitle());
}
}
}
Your tracker now has a package-based data model with resizable storage. Next, you will move its growing behavior into a dedicated service.
Separate Tracker Behavior into a Service
Your tracker now displays three numbered WorkItem objects. The fixed-size failure is gone.
However, WorkItemsApp currently controls the whole tracker. Adding input rules there would give the entry point another responsibility.
A service layer gives tracker behavior one home. This keeps the entry point focused on running the application.
In this step, get ready to:
- Move work item storage into WorkItemService.
- Add title validation to reject blank work items.
- Refactor WorkItemsApp to use the service.
Move tracker behavior into a service
The service owns a resizable ArrayList of work items. Its nextId field supplies the next available number.
- Use the new-file control in the file sidebar to create WorkItemService.java inside src/main/java/com/nextwork/workitems.
- Build the service storage plus its creation behavior by pasting this code into the new file:
package com.nextwork.workitems;
import java.util.ArrayList;
import java.util.List;
public final class WorkItemService {
private final List<WorkItem> items = new ArrayList<>();
private int nextId = 1;
public WorkItem add(String title) {
WorkItem item = new WorkItem(nextId, title);
items.add(item);
nextId++;
return item;
}
}
What does this code do?
- The private items field stores the work items created through the service.
- The nextId field starts with the first available ID.
- The add(String title) method creates one work item before storing it.
- Incrementing nextId prepares a different ID for the next item.
- Save WorkItemService.java.
- Return to WorkItemsApp.java in the file sidebar.
- Select Run|Debug above main().
You should still see the three numbered work items. This confirms that the new service source compiles without breaking the existing tracker.
Does the new service show errors?
- Check that WorkItemService.java sits beside WorkItem.java.
- Check that the first line uses package com.nextwork.workitems;.
- Confirm that both import lines appear above the class declaration.
- Help me diagnose errors in my new service class.
The service also needs behavior for completing an item. Its read operation returns a protected copy of the stored collection.
- Place your cursor before the final closing brace in WorkItemService.java.
- Add the completion plus read behavior by pasting this code:
public void complete(int id) {
for (WorkItem item : items) {
if (item.getId() == id) {
item.markDone();
return;
}
}
throw new IllegalArgumentException("No work item found for id " + id + ".");
}
public List<WorkItem> findAll() {
return List.copyOf(items);
}
How does the service manage behavior?
- The complete(int id) method searches for the requested work item.
- A matching item receives the status update through markDone().
- A missing ID causes an IllegalArgumentException with a readable message.
- The findAll() method returns an unmodifiable list through List.copyOf(items).
- Save WorkItemService.java.
- Return to WorkItemsApp.java in the file sidebar.
- Select Run|Debug above main().
You should see the same three work items again. The tracker still runs after the service gains its complete set of operations.
Did the tracker stop compiling?
- Check that both methods sit inside the WorkItemService class braces.
- Check that findAll() returns List<WorkItem>.
- Confirm that the service still imports java.util.List.
- Help me fix compilation errors in the service methods.
Protect work items with validation
Every work item needs a meaningful title. Placing the rule in the constructor protects every path that creates a WorkItem.
- Switch back to WorkItem.java in the file sidebar.
- Find the constructor that begins with public WorkItem(int id, String title).
- Compare it with the current constructor below:
public WorkItem(int id, String title) {
this.id = id;
this.title = title;
this.status = WorkItemStatus.TODO;
}
What does the current constructor allow?
The current constructor stores any title it receives. This includes a null value or a string containing only white space.
- Replace the current constructor with this validated version:
public WorkItem(int id, String title) {
if (title == null || title.isBlank()) {
throw new IllegalArgumentException("Title must not be blank.");
}
this.id = id;
this.title = title;
this.status = WorkItemStatus.TODO;
}
How does the validation protect objects?
- The null check protects the program before it calls a method on the title.
- The isBlank() check catches empty titles plus titles containing only white space.
- The IllegalArgumentException stops an invalid work item from being created.
- The exception message explains the rejected input rule.
- Save WorkItem.java.
- Return to WorkItemsApp.java in the file sidebar.
- Select Run|Debug above main().
You should still see all three existing work items. Their valid titles pass the new constructor rule.
Are valid titles being rejected?
- Check that the condition uses || between the two checks.
- Check that title == null appears before title.isBlank().
- Confirm that the validation block appears before the field assignments.
- Help me fix my title validation.
Connect the entry point to the service
The entry point can now delegate tracker operations to WorkItemService. Exception handling converts the rejected blank title into readable terminal output.
- Return to WorkItemsApp.java in the file sidebar.
- Select all code currently in WorkItemsApp.java.
- Replace the selected code with this service-based entry point:
package com.nextwork.workitems;
public final class WorkItemsApp {
public static void main(String[] args) {
WorkItemService service = new WorkItemService();
service.add("Learn Java compilation");
service.add("Build a work item tracker");
service.complete(1);
try {
service.add(" ");
} catch (IllegalArgumentException exception) {
System.out.println("Rejected: " + exception.getMessage());
}
System.out.println("TEAM WORK ITEMS");
for (WorkItem item : service.findAll()) {
System.out.println(
item.getId() + " | " + item.getStatus() + " | " + item.getTitle());
}
}
}
What does the entry point control now?
- The service variable holds the tracker behavior used by the application.
- The valid add calls create two stored work items.
- The try block submits a blank title deliberately.
- The catch block prints the validation message before the final loop reads through findAll().
- Save WorkItemsApp.java.
Before you run the tracker, do you expect the rejected title to prevent the valid items from printing?
- Select Run|Debug above main().
You should see Rejected: Title must not be blank. first. The TEAM WORK ITEMS heading follows.
Item 1 appears as DONE. Item 2 appears as TODO.
That separation is working. Invalid input is rejected while the service keeps valid tracker data available.
Is the final output different?
- Check that the blank title contains only spaces inside service.add(" ").
- Check that service.complete(1) appears before the validation block.
- Save every Java file if the Run panel still shows the earlier three-item output.
- Help me fix the final tracker output.
✔️ Awesome, I've got everything!
Your service now controls creation plus completion. Your constructor rejects blank titles.
- Save every open project file.
ⓧ I'd like to double check the full code
Compare each workspace file with the complete versions below.
{
"java.project.sourcePaths": ["src/main/java"]
}
package com.nextwork.workitems;
public enum WorkItemStatus {
TODO,
DONE
}
package com.nextwork.workitems;
public final class WorkItem {
private final int id;
private final String title;
private WorkItemStatus status;
public WorkItem(int id, String title) {
if (title == null || title.isBlank()) {
throw new IllegalArgumentException("Title must not be blank.");
}
this.id = id;
this.title = title;
this.status = WorkItemStatus.TODO;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public WorkItemStatus getStatus() {
return status;
}
public void markDone() {
status = WorkItemStatus.DONE;
}
}
package com.nextwork.workitems;
import java.util.ArrayList;
import java.util.List;
public final class WorkItemService {
private final List<WorkItem> items = new ArrayList<>();
private int nextId = 1;
public WorkItem add(String title) {
WorkItem item = new WorkItem(nextId, title);
items.add(item);
nextId++;
return item;
}
public void complete(int id) {
for (WorkItem item : items) {
if (item.getId() == id) {
item.markDone();
return;
}
}
throw new IllegalArgumentException("No work item found for id " + id + ".");
}
public List<WorkItem> findAll() {
return List.copyOf(items);
}
}
package com.nextwork.workitems;
public final class WorkItemsApp {
public static void main(String[] args) {
WorkItemService service = new WorkItemService();
service.add("Learn Java compilation");
service.add("Build a work item tracker");
service.complete(1);
try {
service.add(" ");
} catch (IllegalArgumentException exception) {
System.out.println("Rejected: " + exception.getMessage());
}
System.out.println("TEAM WORK ITEMS");
for (WorkItem item : service.findAll()) {
System.out.println(
item.getId() + " | " + item.getStatus() + " | " + item.getTitle());
}
}
}
Secret mission
Filter Work Items by Status
Your tracker shows every work item in one list. Add a reusable status filter that creates a focused view containing only the work items that match a requested status.
Clean Up Your Resources
Clean Up Your Resources
This Java tracker runs entirely on your computer. It has no ongoing costs.
Choose whether to keep the workspace available, pause your work to free up memory, or delete the local files entirely.
Resources you used:
- The java-work-items folder, including .vscode/settings.json and the Java source files under src/main/java/com/nextwork/workitems.
Keep everything running
No action needed. Choose this if you want to keep improving the tracker or reviewing its code.
- Leave the java-work-items folder in its current location.
- Return to the java-work-items folder in Visual Studio Code when you are ready to continue.
Pause - I'll come back to this later
Closing Visual Studio Code frees its memory. Your java-work-items files stay on disk.
- Save any open files in Visual Studio Code.
- Close Visual Studio Code.
- Return to the existing java-work-items folder when you are ready to resume.
Delete - I don't want to use this again
Deleting the folder removes only this local tracker. Your development tools stay available for future projects.
- Copy any code you want to keep outside the java-work-items folder.
- Close Visual Studio Code.
- Locate the java-work-items folder in the Windows folder browser.
- Permanently remove the java-work-items folder using the Windows folder browser.
- Confirm that java-work-items no longer appears in its previous location.
Nice Work!
Nice Work!
Brilliant work! You built a structured command-line work item tracker in Java.
You've learned how to:
- Created a Java entry point with public static void main(String[] args). You used manual compilation to turn HelloWorldApp.java into HelloWorldApp.class bytecode. You then ran that bytecode on the JVM.
- Triggered a predictable ArrayIndexOutOfBoundsException with a fixed-size array. You diagnosed the failing index from the exception details. You replaced the array with a resizable ArrayList. WorkItem objects now keep each title with its WorkItemStatus value.
- Organized the tracker in the com.nextwork.workitems package. You separated tracker behavior into the WorkItemService service layer. You added title validation with IllegalArgumentException. You protected the internal collection with List.copyOf(items).
- Secret Mission: Added a reusable status filter with findByStatus(WorkItemStatus status). The TODO WORK ITEMS view now displays only matching work items.
Ready to quiz yourself?