Java Basics to Advanced: A Practical Guide for Students
Who this is for: students (or anyone) who want a practical path from zero to productive Java developer.
What you will get: short explanations, runnable examples, exercises, and a small CLI mini‑project. Examples target modern Java (17+), but most code works on 11+.
0) Setup and Running Code
- Install JDK 17 or later (Temurin or Oracle JDK).
- Verify:
java -versionandjavac -version.
Compile and run a single file:
javac Main.java
java Main
Minimal Gradle build (optional):
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
test {
useJUnitPlatform()
}
Project structure (Gradle/Maven convention):
src/
main/java/... your .java files
test/java/... your test files
1) First Java Program
public class Main {
public static void main(String[] args) {
System.out.println("Hello, Java!");
}
}
public static void main(String[] args)is the entry point.System.out.println(...)prints a line.
Try: change the message, recompile, rerun.
2) Variables, Types, and Operators
Primitives: int, long, double, boolean, char. Reference types: String, arrays, classes you define.
int age = 20;
double gpa = 3.7;
boolean enrolled = true;
char grade = 'A';
String name = "Ada";
final double PI = 3.14159; // constant
var count = 5; // local type inference (Java 10+)
Operators: + - * / %, comparisons == != < <= > >=, logical && || !.
Watch out: integer division truncates: 5 / 2 == 2.
3) Control Flow
// if / else
int n = 7;
if (n % 2 == 0) {
System.out.println("even");
} else {
System.out.println("odd");
}
// switch (enhanced)
import java.time.DayOfWeek;
DayOfWeek day = DayOfWeek.MONDAY;
String type = switch (day) {
case SATURDAY, SUNDAY -> "WEEKEND";
default -> "WEEKDAY";
};
System.out.println(type);
// loops
for (int i = 0; i < 3; i++) System.out.println(i);
int k = 3;
while (k > 0) { k--; }
for (int x : new int[]{1,2,3}) System.out.println(x);
4) Methods (Functions)
static int add(int a, int b) { return a + b; }
static double mean(int a, int b, int c) { return (a + b + c) / 3.0; }
// Overloading
static int max(int a, int b) { return a > b ? a : b; }
static double max(double a, double b) { return a > b ? a : b; }
Tip: keep methods short and single‑purpose.
5) Classes and Objects
Define a simple Student type with identity, behavior, and invariants.
import java.util.*;
public class Student {
private final String id; // identity, immutable
private String name; // mutable
private final List<Integer> scores = new ArrayList<>();
public Student(String id, String name) {
if (id == null || id.isBlank()) throw new IllegalArgumentException("id required");
this.id = id;
this.name = name;
}
public String getId() { return id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public void addScore(int s) {
if (s < 0 || s > 100) throw new IllegalArgumentException("0..100 only");
scores.add(s);
}
public double average() {
return scores.stream().mapToInt(Integer::intValue).average().orElse(0.0);
}
@Override public String toString() {
return "Student{" + id + ", name=" + name + ", avg=" + average() + "}";
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student other)) return false;
return id.equals(other.id);
}
@Override public int hashCode() { return id.hashCode(); }
}
Notes:
- Use
equals/hashCodefor logical equality (here: sameid). - Use
finalfor fields that should not change.
6) Collections and Generics
import java.util.*;
List<Student> students = new ArrayList<>();
students.add(new Student("s1", "Ada"));
students.add(new Student("s2", "Grace"));
Set<String> courses = new HashSet<>();
courses.add("CS101"); courses.add("CS101"); // still size 1 because a Set is unique
Map<String, Integer> credits = new HashMap<>();
credits.put("CS101", 4);
credits.put("MATH", 3);
A tiny generic type:
class Box<T> {
private T value;
public void set(T v) { value = v; }
public T get() { return value; }
}
Box<Integer> b = new Box<>();
b.set(42);
int x = b.get();
7) Inheritance, Interfaces, and Polymorphism
Prefer composition. Use inheritance for true “is‑a” relationships.
interface Gradable { double score(); }
abstract class Person {
private final String id;
protected Person(String id) { this.id = id; }
public String id() { return id; }
}
class Undergraduate extends Person implements Gradable {
private final int midterm, fin;
public Undergraduate(String id, int midterm, int fin) {
super(id);
this.midterm = midterm; this.fin = fin;
}
@Override public double score() { return midterm * 0.4 + fin * 0.6; }
}
class Graduate extends Person implements Gradable {
private final int research, defense;
public Graduate(String id, int research, int defense) {
super(id);
this.research = research; this.defense = defense;
}
@Override public double score() { return research * 0.7 + defense * 0.3; }
}
List<Gradable> list = List.of(
new Undergraduate("u1", 80, 90),
new Graduate("g1", 95, 85)
);
for (Gradable g : list) System.out.println(g.score());
8) Exceptions and Resource Safety
try {
Student s = new Student("s3", "Lin");
s.addScore(120); // will throw
} catch (IllegalArgumentException e) {
System.out.println("Bad input: " + e.getMessage());
}
Try‑with‑resources auto‑closes files/streams:
import java.nio.file.*;
import java.io.*;
Path p = Path.of("notes.txt");
try (BufferedWriter w = Files.newBufferedWriter(p)) {
w.write("study hard");
}
Custom exception:
class InvalidScoreException extends RuntimeException {
public InvalidScoreException(String msg) { super(msg); }
}
9) I/O in One File (NIO)
import java.nio.file.*;
import java.util.*;
Path p = Path.of("students.txt");
Files.writeString(p, "s1,Ada\ns2,Grace\n");
List<String> lines = Files.readAllLines(p);
for (String line : lines) System.out.println(line);
10) Lambdas, Streams, and Optional
import java.util.*;
import java.util.stream.*;
List<Student> students = List.of(
new Student("s1", "Ada"),
new Student("s2", "Grace"),
new Student("s3", "Lin")
);
// Suppose we add some scores
students.get(0).addScore(100); students.get(0).addScore(90);
students.get(1).addScore(70); students.get(1).addScore(85);
students.get(2).addScore(88);
// Top performers (avg >= 90), sorted by name
List<String> top = students.stream()
.filter(s -> s.average() >= 90)
.sorted(Comparator.comparing(Student::getName))
.map(Student::getName)
.toList();
System.out.println(top);
// Optional
students.stream()
.max(Comparator.comparingDouble(Student::average))
.ifPresent(s -> System.out.println("Top: " + s.getName()));
11) Concurrency Basics
Use executors instead of raw threads.
import java.util.concurrent.*;
import java.util.*;
ExecutorService pool = Executors.newFixedThreadPool(4);
try {
List<Callable<Integer>> tasks = List.of(
() -> { Thread.sleep(200); return 1; },
() -> { Thread.sleep(150); return 2; },
() -> { Thread.sleep(100); return 3; }
);
List<Future<Integer>> futures = pool.invokeAll(tasks);
int sum = 0;
for (Future<Integer> f : futures) sum += f.get();
System.out.println(sum);
} finally {
pool.shutdown();
}
CompletableFuture for composition:
import java.util.concurrent.*;
CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> 40);
CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> 2);
int result = a.thenCombine(b, Integer::sum).join();
System.out.println(result);
12) Packages and Organization
Create packages to avoid name clashes:
package com.example.school;
public class App { /* ... */ }
Folder must match: src/main/java/com/example/school/App.java.
13) Testing with JUnit 5
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
public class StudentTest {
@Test void averageWorks() {
Student s = new Student("s1", "Ada");
s.addScore(100); s.addScore(80);
assertEquals(90.0, s.average(), 0.0001);
}
}
Run: ./gradlew test or your IDE’s test runner.
14) Mini Project: Gradebook CLI
A tiny loop that accepts commands: add, score, avg, top, quit.
import java.util.*;
public class Gradebook {
private final Map<String, Student> byId = new HashMap<>();
public void add(String id, String name) {
byId.putIfAbsent(id, new Student(id, name));
}
public void score(String id, int s) {
Student st = byId.get(id);
if (st == null) { System.out.println("no such id"); return; }
st.addScore(s);
}
public void avg(String id) {
Student st = byId.get(id);
System.out.println(st == null ? "no such id" : st.average());
}
public void top() {
byId.values().stream()
.max(Comparator.comparingDouble(Student::average))
.ifPresent(st -> System.out.println(st.getId() + ": " + st.average()));
}
public static void main(String[] args) {
Gradebook g = new Gradebook();
Scanner sc = new Scanner(System.in);
System.out.println("commands: add id name | score id n | avg id | top | quit");
while (true) {
System.out.print("> ");
String cmd = sc.next();
switch (cmd) {
case "add" -> g.add(sc.next(), sc.next());
case "score" -> g.score(sc.next(), sc.nextInt());
case "avg" -> g.avg(sc.next());
case "top" -> g.top();
case "quit" -> { return; }
default -> System.out.println("?");
}
}
}
}
Try it: add students and scores, then check top.
15) Common Pitfalls
==vsequals: useequalsfor object content comparison (e.g.,String),==for primitives and reference identity.- Integer division: cast to double if needed, e.g.,
(a + b) / 2.0. - Nulls: avoid returning null; prefer
Optionalor empty collections. - Mutability leaks: return unmodifiable views or copies, keep fields
finalwhen possible. - Concurrency: never share mutable state without synchronization; prefer immutable data and high‑level APIs.
16) Exercises
- Basics: write a method that returns the factorial of
n. Handlen < 0with an exception. - Classes: extend
Studentto includeMap<String,Integer>per‑course scores and compute per‑course averages. - Collections: read
students.csvand buildMap<String, List<Integer>>of scores by id. - Streams: given a list of students, produce the top 3 by average and print names.
- Exceptions: create
InvalidGradeExceptionand use it when adding out‑of‑range grades. - Concurrency: run 4 tasks that each sleep a random time and return a number; sum results with
CompletableFuture.allOf. - Testing: add tests for edge cases (empty scores, one score, very large inputs).
17) Next Steps
- Learn about records (concise immutable data carriers), enums, and sealed classes.
- Explore the Java Module System for larger apps.
- Practice with a web stack: Spring Boot or a lightweight HTTP server.
- Read Effective Java (Joshua Bloch) and Java Language Specification sections as you advance.
Keep building small programs, write tests, and refactor often. Happy coding!