JavaNote: Your Quick Reference for Core Java ConceptsJava remains one of the most widely used programming languages in industry and education. JavaNote is designed as a compact, well-organized quick reference to the core concepts every Java developer — from beginner to experienced — needs to recall quickly. This article organizes essential Java topics, provides concise examples, highlights common pitfalls, and offers idiomatic advice to help you write correct, maintainable Java code.
Table of contents
- Java language essentials
- Primitive types & boxing/unboxing
- Object-oriented fundamentals
- Classes, constructors, and initialization
- Inheritance, polymorphism, and method overriding
- Interfaces, default methods, and functional interfaces
- Exception handling
- Collections framework essentials
- Generics and type safety
- Concurrency basics and java.util.concurrent
- I/O and NIO overview
- Streams and lambda expressions
- Memory model, garbage collection, and performance tips
- Best practices and common pitfalls
- Quick reference snippets
1. Java language essentials
- Java source files are compiled to bytecode (.class) and run on the Java Virtual Machine (JVM).
- Java is strongly typed and statically typed: variable types are checked at compile time.
- The entry point of a Java application is a static method with signature: public static void main(String[] args).
- Package organization: use packages to avoid name collisions and to structure code.
Example main:
public class Main { public static void main(String[] args) { System.out.println("Hello, JavaNote!"); } }
2. Primitive types & boxing/unboxing
Java has eight primitive types: byte, short, int, long, float, double, char, boolean. Their wrapper classes are: Byte, Short, Integer, Long, Float, Double, Character, Boolean.
- Use primitives for performance and memory efficiency when nullability is not required.
- Autoboxing/unboxing converts between primitives and wrappers automatically but can introduce unexpected NullPointerException or performance overhead.
Example:
Integer a = 10; // autoboxing int b = a + 5; // unboxing
Pitfall: Integer caching and == comparison — use .equals for wrapper equality.
3. Object-oriented fundamentals
Key concepts:
- Encapsulation: keep fields private, expose behavior via methods.
- Abstraction: hide complexity behind simple interfaces.
- Inheritance: reuse behavior between classes (single inheritance for classes).
- Polymorphism: treat subclass instances as instances of their superclass or interface type.
Example encapsulation:
public class Person { private String name; public Person(String name) { this.name = name; } public String getName() { return name; } }
4. Classes, constructors, and initialization
- Constructors initialize new objects. Overload constructors for different initializations.
- Use static initializers for class-level setup and instance initializers for shared initialization across constructors sparingly.
- Prefer immutability: make fields final when possible, initialize them in constructors.
Example immutable class:
public final class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } }
5. Inheritance, polymorphism, and method overriding
- Use extends to inherit from a class. Use super(…) to call parent constructors.
- Overriding rules: method signatures must match; return types can be covariant. Use @Override to catch mistakes.
- Mark methods final to prevent overriding, classes final to prevent subclassing.
Example:
class Animal { void speak() { System.out.println("..."); } } class Dog extends Animal { @Override void speak() { System.out.println("Bark"); } }
6. Interfaces, default methods, and functional interfaces
- Interfaces define contracts; classes implement them. Since Java 8, interfaces can have default and static methods.
- Functional interfaces (single abstract method) can be used with lambda expressions. Common examples: Runnable, Callable, Comparator, Function, Predicate.
Example functional interface usage:
List<String> names = List.of("Ann","Bob"); names.sort((a,b) -> a.compareToIgnoreCase(b));
7. Exception handling
- Checked exceptions must be declared or handled (throws / try-catch). RuntimeExceptions are unchecked.
- Prefer specific exceptions; don’t swallow exceptions silently. Use try-with-resources for AutoCloseable resources.
Example:
try (BufferedReader r = new BufferedReader(new FileReader("file.txt"))) { String line = r.readLine(); } catch (IOException e) { e.printStackTrace(); }
8. Collections framework essentials
- Core interfaces: Collection, List, Set, Map, Queue, Deque. Common implementations: ArrayList, LinkedList, HashSet, TreeSet, HashMap, LinkedHashMap, ArrayDeque.
- Choose collections by required properties: ordering, duplicates, random access, concurrency.
Quick tips:
- Use ArrayList for random-access lists, LinkedList sparingly.
- Use HashMap for typical key-value lookup; TreeMap if sorted order needed.
- Use ConcurrentHashMap for concurrent maps.
9. Generics and type safety
- Generics provide compile-time type safety and eliminate casts. Use bounded type parameters for constraints.
- Wildcards: ? extends T for covariance (read-only), ? super T for contravariance (write).
- Type erasure: generic type information is not available at runtime; you cannot create new T[] directly.
Example:
public static <T> List<T> singletonList(T value) { return Collections.singletonList(value); }
10. Concurrency basics and java.util.concurrent
- Threads are created via Thread or Executor frameworks. Prefer ExecutorService for managing thread pools.
- Synchronization: use synchronized blocks or java.util.concurrent locks. Avoid holding locks while performing I/O.
- High-level concurrency utilities: CountDownLatch, Semaphore, CyclicBarrier, Concurrent collections, CompletableFuture.
Executor example:
ExecutorService ex = Executors.newFixedThreadPool(4); Future<Integer> f = ex.submit(() -> compute()); ex.shutdown();
Pitfall: avoid thread leaks — always shutdown executors.
11. I/O and NIO overview
- java.io provides stream-based I/O (InputStream/OutputStream, Reader/Writer).
- java.nio and NIO.2 (java.nio.file) offer non-blocking I/O, channels, buffers, and a more flexible file API.
Files example:
Path p = Paths.get("data.txt"); List<String> lines = Files.readAllLines(p, StandardCharsets.UTF_8);
12. Streams and lambda expressions
- Streams (java.util.stream) provide a fluent API for processing sequences: map, filter, reduce, collect. Streams can be sequential or parallel.
- Prefer streams for expressive data transformations; avoid overusing streams for simple loops where readability suffers.
Example:
List<Integer> squares = IntStream.range(1, 6) .map(i -> i * i) .boxed() .collect(Collectors.toList());
13. Memory model, garbage collection, and performance tips
- JVM memory areas: heap (objects), stack (method frames), metaspace (class metadata).
- Garbage collectors: G1, Shenandoah, ZGC (availability depends on JVM). Choose collector based on latency vs throughput needs.
- Reduce allocations for performance; reuse buffers, use primitive collections if needed (or Trove/fastutil). Measure with a profiler before optimizing.
Simple GC tip: prefer immutable small objects but avoid creating many short-lived temporary objects in hot loops.
14. Best practices and common pitfalls
- Follow naming conventions: packages lowercase, class names PascalCase, methods camelCase.
- Keep methods short and focused (single responsibility).
- Prefer composition over inheritance.
- Validate method arguments; use Objects.requireNonNull when nulls are not allowed.
- Avoid String concatenation in loops (use StringBuilder).
- Beware of mutable static state — causes concurrency and testing issues.
Common pitfalls:
- Using == to compare strings or wrapper objects. Use .equals.
- Relying on iteration order of HashMap. Use LinkedHashMap if insertion-order matters.
- Forgetting to close resources — use try-with-resources.
15. Quick reference snippets
-
Synchronized block:
synchronized (lock) { // critical section }
-
Try-with-resources:
try (InputStream in = new FileInputStream("data.bin")) { // use in }
-
Reading file lines:
Files.lines(Paths.get("file.txt")) .forEach(System.out::println);
-
Simple CompletableFuture:
CompletableFuture.supplyAsync(() -> fetch()) .thenApply(data -> process(data)) .exceptionally(ex -> handle(ex));
JavaNote is meant to be a living quick-reference: keep it near your editor, update snippets with modern idioms (records, text blocks, pattern matching, new API additions) as Java evolves, and rely on it for quick reminders of concepts, not exhaustive tutorials.