Here's a step-by-step guide to build each project independently, with a learning-focused approach:
For each project, follow this 4-step process:
1. UNDERSTAND: What am I building? (5 minutes)
2. PLAN: How will I structure it? (15 minutes)
3. BUILD: Implement step by step (60-90 minutes)
4. REFINE: Test, debug, improve (30 minutes)
- Core concept: Two-way communication between server and clients
- Key components:
- Server that accepts connections
- Multiple clients that connect to server
- Messages flow: Client β Server β All other clients
- Technology: Java Sockets (
ServerSocket,Socket)
Sketch on paper:
Server needs to:
1. Listen on a port (e.g., 12345)
2. Accept new connections
3. Read messages from each client
4. Send messages to all clients
Client needs to:
1. Connect to server
2. Send messages
3. Receive messages from others
Draw: [Client1] β [Server] β [Client2]
β
[Client3]
Start with the absolute basics:
// File: SimpleChatServer.java
import java.io.*;
import java.net.*;
public class SimpleChatServer {
public static void main(String[] args) throws IOException {
System.out.println("Starting server...");
ServerSocket serverSocket = new ServerSocket(12345);
// Just accept ONE connection first
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected!");
// Read from client
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
String message = in.readLine();
System.out.println("Client says: " + message);
// Send response
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
out.println("Server received: " + message);
serverSocket.close();
}
}// File: SimpleChatClient.java
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class SimpleChatClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 12345);
// Send message
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Hello Server!");
// Receive response
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String response = in.readLine();
System.out.println("Server says: " + response);
socket.close();
}
}Now make it better:
- Add loops so server/client can send multiple messages
- Add threading to handle multiple clients
- Add broadcast to send to all clients
- Add usernames and exit commands
Test with multiple terminals. Add features:
/listto show online users/whisperfor private messages- Save chat history to file
- Core concept: Manage data with commands, persist to files
- Example: Task manager, contacts, notes, expense tracker
- Key skills: File I/O, parsing, user input, data structures
Choose a domain:
- β Task Manager (what we did)
- β Book Collection Manager
- β Expense Tracker
- β Student Gradebook
- β Recipe Organizer
For Task Manager:
Data to store: task, priority, status, date
Commands: add, list, complete, delete, search
File format: CSV or simple text
// File: MyTaskManager.java
import java.io.*;
import java.util.*;
public class MyTaskManager {
private static final String FILE = "mytasks.txt";
private static List<String> tasks = new ArrayList<>();
public static void main(String[] args) {
loadTasks();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("\nMy Task Manager");
System.out.println("1. Add task");
System.out.println("2. View tasks");
System.out.println("3. Exit");
System.out.print("Choose: ");
String choice = scanner.nextLine();
if (choice.equals("1")) {
System.out.print("Enter task: ");
String task = scanner.nextLine();
tasks.add(task);
System.out.println("Added!");
}
else if (choice.equals("2")) {
if (tasks.isEmpty()) {
System.out.println("No tasks!");
} else {
for (int i = 0; i < tasks.size(); i++) {
System.out.println((i+1) + ". " + tasks.get(i));
}
}
}
else if (choice.equals("3")) {
saveTasks();
System.out.println("Goodbye!");
break;
}
}
scanner.close();
}
static void loadTasks() {
try {
File file = new File(FILE);
if (!file.exists()) return;
Scanner fileScanner = new Scanner(file);
while (fileScanner.hasNextLine()) {
tasks.add(fileScanner.nextLine());
}
fileScanner.close();
} catch (IOException e) {
System.out.println("Error loading tasks");
}
}
static void saveTasks() {
try (PrintWriter writer = new PrintWriter(FILE)) {
for (String task : tasks) {
writer.println(task);
}
} catch (IOException e) {
System.out.println("Error saving tasks");
}
}
}Now enhance it:
- Add due dates:
tasks.add(task + "|" + date) - Add priority levels: Use numbers 1-3
- Add status (todo/done)
- Add search function
- Add categories
- Add input validation
- Make it prettier with formatting
- Add undo functionality
- Export to CSV
- Core concept: Store data in memory with table-like structure
- What it should do: Create tables, insert, query, update, delete
- Key data structures:
Map<String, Map<String, Object>>
Database contains Tables
Each Table contains Rows
Each Row has Columns with Values
Operations:
- CREATE table (name, columns)
- INSERT row (values)
- SELECT rows (with optional WHERE)
- UPDATE rows
- DELETE rows
// File: MyMiniDB.java
import java.util.*;
public class MyMiniDB {
// Map: tableName -> rows
// Each row: Map of column->value
private static Map<String, List<Map<String, String>>> db = new HashMap<>();
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("mydb> ");
String input = scanner.nextLine().trim();
if (input.equalsIgnoreCase("exit")) break;
String[] parts = input.split(" ", 2);
String command = parts[0].toUpperCase();
try {
switch (command) {
case "CREATE" -> createTable(parts[1]);
case "INSERT" -> insert(parts[1]);
case "SELECT" -> select(parts[1]);
case "HELP" -> help();
default -> System.out.println("Unknown command");
}
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
scanner.close();
}
static void createTable(String input) {
// Format: CREATE users (id,name,email)
String[] parts = input.split("\\(");
String tableName = parts[0].trim();
String columnsStr = parts[1].replace(")", "");
String[] columns = columnsStr.split(",");
db.put(tableName, new ArrayList<>());
System.out.println("Table '" + tableName + "' created");
}
static void insert(String input) {
// Format: INSERT INTO users VALUES (1,John,john@email.com)
String[] parts = input.split("VALUES");
String tablePart = parts[0].replace("INTO", "").trim();
String tableName = tablePart.trim();
String valuesStr = parts[1].trim().replace("(", "").replace(")", "");
String[] values = valuesStr.split(",");
List<Map<String, String>> table = db.get(tableName);
if (table == null) {
System.out.println("Table not found!");
return;
}
// Create row (for simplicity, assume 3 columns)
Map<String, String> row = new HashMap<>();
row.put("id", values[0].trim());
row.put("name", values[1].trim());
row.put("email", values[2].trim());
table.add(row);
System.out.println("Inserted 1 row");
}
static void select(String input) {
// Format: SELECT * FROM users
String tableName = input.replace("SELECT", "")
.replace("*", "")
.replace("FROM", "")
.trim();
List<Map<String, String>> table = db.get(tableName);
if (table == null) {
System.out.println("Table not found!");
return;
}
System.out.println("\nResults:");
for (Map<String, String> row : table) {
System.out.println("ID: " + row.get("id") +
", Name: " + row.get("name") +
", Email: " + row.get("email"));
}
System.out.println("Total: " + table.size() + " rows");
}
static void help() {
System.out.println("Commands:");
System.out.println(" CREATE table (col1,col2,col3)");
System.out.println(" INSERT INTO table VALUES (val1,val2,val3)");
System.out.println(" SELECT * FROM table");
System.out.println(" EXIT");
}
}Now enhance:
- Add WHERE clause for filtering
- Add UPDATE and DELETE
- Add primary keys
- Add indexes for faster search
- Add persistence (save to file)
- Add transaction support
- Add JOIN operations
- Add data type validation
- Add SQL-like parser
- Core concept: Download files using multiple threads
- Key components: URL connection, file writing, progress tracking
- Threading: Each download in separate thread, manage concurrency
Main Components:
1. Download Manager - controls all downloads
2. Download Task - one file download
3. Thread Pool - manages concurrent downloads
4. Progress Tracking - bytes downloaded/total
Features:
- Add URL to download
- Pause/resume downloads
- Track progress
- Cancel downloads
// File: SimpleDownloader.java
import java.io.*;
import java.net.*;
import java.util.*;
public class SimpleDownloader {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Simple Downloader");
System.out.println("Enter URL to download (or 'quit'):");
while (true) {
System.out.print("URL: ");
String url = scanner.nextLine();
if (url.equalsIgnoreCase("quit")) break;
System.out.print("Save as: ");
String filename = scanner.nextLine();
// Start download in new thread
new Thread(() -> {
downloadFile(url, filename);
}).start();
System.out.println("Download started in background...");
}
scanner.close();
}
static void downloadFile(String urlStr, String filename) {
try {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
try (
InputStream in = conn.getInputStream();
FileOutputStream out = new FileOutputStream(filename)
) {
byte[] buffer = new byte[4096];
int bytesRead;
long totalBytes = 0;
System.out.println("Downloading " + filename + "...");
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
// Show progress every 1MB
if (totalBytes % (1024 * 1024) < 4096) {
System.out.println(filename + ": " +
(totalBytes / (1024 * 1024)) + " MB downloaded");
}
}
System.out.println("Download complete: " + filename);
} catch (IOException e) {
System.out.println("Download failed: " + e.getMessage());
}
} catch (Exception e) {
System.out.println("Invalid URL: " + e.getMessage());
}
}
}Now enhance:
- Add pause/resume functionality
- Add download queue
- Add speed calculation
- Add multiple connection support (chunk downloading)
- Add GUI progress bars
- Add retry on failure
- Add download scheduling
- Add bandwidth limiting
- Add checksum verification
- Core concept: HTTP server that responds to API requests
- HTTP basics: Methods (GET, POST, PUT, DELETE), status codes, headers
- Key components: Socket server, request parsing, routing, response building
Server should:
1. Listen on port (8080)
2. Parse HTTP requests
3. Route to appropriate handler
4. Return JSON responses
5. Handle different HTTP methods
Example endpoints:
GET /api/users - list all users
GET /api/users/1 - get user 1
POST /api/users - create user
PUT /api/users/1 - update user 1
DELETE /api/users/1 - delete user 1
// File: MyAPIServer.java
import java.io.*;
import java.net.*;
import java.util.*;
public class MyAPIServer {
private static Map<Integer, String> users = new HashMap<>();
public static void main(String[] args) throws IOException {
// Initialize some data
users.put(1, "Alice");
users.put(2, "Bob");
System.out.println("Starting server on port 8080...");
ServerSocket server = new ServerSocket(8080);
while (true) {
Socket client = server.accept();
handleRequest(client);
}
}
static void handleRequest(Socket client) throws IOException {
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true);
// Read first line (request line)
String requestLine = in.readLine();
if (requestLine == null) return;
System.out.println("Request: " + requestLine);
String[] parts = requestLine.split(" ");
String method = parts[0];
String path = parts[1];
// Skip headers (read until empty line)
while (!in.readLine().isEmpty()) {}
// Route the request
String response;
if (path.equals("/api/users") && method.equals("GET")) {
response = getAllUsers();
}
else if (path.matches("/api/users/\\d+") && method.equals("GET")) {
int id = Integer.parseInt(path.substring("/api/users/".length()));
response = getUser(id);
}
else {
response = "{\"error\": \"Not found\"}";
}
// Send HTTP response
out.println("HTTP/1.1 200 OK");
out.println("Content-Type: application/json");
out.println("Connection: close");
out.println();
out.println(response);
client.close();
}
static String getAllUsers() {
StringBuilder json = new StringBuilder("{\"users\": [");
boolean first = true;
for (Map.Entry<Integer, String> entry : users.entrySet()) {
if (!first) json.append(",");
json.append("{\"id\": ").append(entry.getKey())
.append(", \"name\": \"").append(entry.getValue()).append("\"}");
first = false;
}
json.append("]}");
return json.toString();
}
static String getUser(int id) {
if (users.containsKey(id)) {
return "{\"id\": " + id + ", \"name\": \"" + users.get(id) + "\"}";
} else {
return "{\"error\": \"User not found\"}";
}
}
}Test it:
# Terminal 1
javac MyAPIServer.java && java MyAPIServer
# Terminal 2
curl http://localhost:8080/api/users
curl http://localhost:8080/api/users/1Now enhance:
- Add POST method to create users
- Add PUT/DELETE methods
- Add request body parsing
- Add authentication
- Add database persistence
- Add thread pooling for multiple requests
- Add proper error handling (404, 500)
- Add request logging
- Add CORS headers
- Add rate limiting
- Add request validation
- Monday: Simple Chat (just 1 client β server)
- Tuesday: Task Manager (add/view tasks only)
- Wednesday: Mini DB (CREATE, INSERT, SELECT only)
- Thursday: Downloader (single file download)
- Friday: API Server (GET endpoints only)
Add 1-2 features to each project:
- Chat: Multiple clients, usernames
- Task Manager: File saving, priorities
- DB: WHERE clause, UPDATE
- Downloader: Progress display
- API Server: POST method
- Add error handling everywhere
- Write documentation for each
- Create GitHub repository
- Add README with instructions
v0.1: Make it WORK (even if ugly)
v0.2: Make it BETTER (add features)
v0.3: Make it RIGHT (clean code)
v0.4: Make it FAST (optimize)
When stuck:
// 1. Add print statements
System.out.println("DEBUG: Got here with value = " + value);
// 2. Comment out code until it works
// 3. Google specific error messages
// 4. Take a break, then re-read codeTake my simple examples and:
- Change variable names
- Add a new feature
- Break it intentionally, then fix
- Rewrite in your own style
// TODO: Add input validation here
// FIXME: This breaks with large files
// IDEA: Could cache results for speed- Start with what you KNOW - If you understand files better than sockets, start with Project 2
- One change at a time - Test after EACH small change
- Use version control even for small projects:
git init git add . git commit -m "Basic working version"
- Ask specific questions when stuck:
- Bad: "My code doesn't work"
- Good: "When I run X, I get error Y. I tried Z but still fails"
- Java Docs: Always check official documentation
- Stack Overflow: For specific error messages
- GitHub: See how others structure similar projects
- YouTube: Watch building-from-scratch tutorials
Remember: Every expert was once a beginner who kept building. Your first versions will be messy - that's normal! The important thing is to start, finish, and learn from each build.
Which project do you want to start with first? I can give you more specific step-by-step guidance for that one!